123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var _config = require("../../config");
- var __DEV__ = _config.__DEV__;
- var zrUtil = require("zrender/lib/core/util");
- var graphic = require("../../util/graphic");
- var modelUtil = require("../../util/model");
- var brushHelper = require("./brushHelper");
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var each = zrUtil.each;
- var indexOf = zrUtil.indexOf;
- var curry = zrUtil.curry;
- var COORD_CONVERTS = ['dataToPoint', 'pointToData']; // FIXME
- // how to genarialize to more coordinate systems.
- var INCLUDE_FINDER_MAIN_TYPES = ['grid', 'xAxis', 'yAxis', 'geo', 'graph', 'polar', 'radiusAxis', 'angleAxis', 'bmap'];
- /**
- * [option in constructor]:
- * {
- * Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
- * }
- *
- *
- * [targetInfo]:
- *
- * There can be multiple axes in a single targetInfo. Consider the case
- * of `grid` component, a targetInfo represents a grid which contains one or more
- * cartesian and one or more axes. And consider the case of parallel system,
- * which has multiple axes in a coordinate system.
- * Can be {
- * panelId: ...,
- * coordSys: <a representitive cartesian in grid (first cartesian by default)>,
- * coordSyses: all cartesians.
- * gridModel: <grid component>
- * xAxes: correspond to coordSyses on index
- * yAxes: correspond to coordSyses on index
- * }
- * or {
- * panelId: ...,
- * coordSys: <geo coord sys>
- * coordSyses: [<geo coord sys>]
- * geoModel: <geo component>
- * }
- *
- *
- * [panelOpt]:
- *
- * Make from targetInfo. Input to BrushController.
- * {
- * panelId: ...,
- * rect: ...
- * }
- *
- *
- * [area]:
- *
- * Generated by BrushController or user input.
- * {
- * panelId: Used to locate coordInfo directly. If user inpput, no panelId.
- * brushType: determine how to convert to/from coord('rect' or 'polygon' or 'lineX/Y').
- * Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
- * range: pixel range.
- * coordRange: representitive coord range (the first one of coordRanges).
- * coordRanges: <Array> coord ranges, used in multiple cartesian in one grid.
- * }
- */
- /**
- * @param {Object} option contains Index/Id/Name of xAxis/yAxis/geo/grid
- * Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]}
- * @param {module:echarts/model/Global} ecModel
- * @param {Object} [opt]
- * @param {Array.<string>} [opt.include] include coordinate system types.
- */
- function BrushTargetManager(option, ecModel, opt) {
- /**
- * @private
- * @type {Array.<Object>}
- */
- var targetInfoList = this._targetInfoList = [];
- var info = {};
- var foundCpts = parseFinder(ecModel, option);
- each(targetInfoBuilders, function (builder, type) {
- if (!opt || !opt.include || indexOf(opt.include, type) >= 0) {
- builder(foundCpts, targetInfoList, info);
- }
- });
- }
- var proto = BrushTargetManager.prototype;
- proto.setOutputRanges = function (areas, ecModel) {
- this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {
- (area.coordRanges || (area.coordRanges = [])).push(coordRange); // area.coordRange is the first of area.coordRanges
- if (!area.coordRange) {
- area.coordRange = coordRange; // In 'category' axis, coord to pixel is not reversible, so we can not
- // rebuild range by coordRange accrately, which may bring trouble when
- // brushing only one item. So we use __rangeOffset to rebuilding range
- // by coordRange. And this it only used in brush component so it is no
- // need to be adapted to coordRanges.
- var result = coordConvert[area.brushType](0, coordSys, coordRange);
- area.__rangeOffset = {
- offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]),
- xyMinMax: result.xyMinMax
- };
- }
- });
- };
- proto.matchOutputRanges = function (areas, ecModel, cb) {
- each(areas, function (area) {
- var targetInfo = this.findTargetInfo(area, ecModel);
- if (targetInfo && targetInfo !== true) {
- zrUtil.each(targetInfo.coordSyses, function (coordSys) {
- var result = coordConvert[area.brushType](1, coordSys, area.range);
- cb(area, result.values, coordSys, ecModel);
- });
- }
- }, this);
- };
- proto.setInputRanges = function (areas, ecModel) {
- each(areas, function (area) {
- var targetInfo = this.findTargetInfo(area, ecModel);
- area.range = area.range || []; // convert coordRange to global range and set panelId.
- if (targetInfo && targetInfo !== true) {
- area.panelId = targetInfo.panelId; // (1) area.range shoule always be calculate from coordRange but does
- // not keep its original value, for the sake of the dataZoom scenario,
- // where area.coordRange remains unchanged but area.range may be changed.
- // (2) Only support converting one coordRange to pixel range in brush
- // component. So do not consider `coordRanges`.
- // (3) About __rangeOffset, see comment above.
- var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange);
- var rangeOffset = area.__rangeOffset;
- area.range = rangeOffset ? diffProcessor[area.brushType](result.values, rangeOffset.offset, getScales(result.xyMinMax, rangeOffset.xyMinMax)) : result.values;
- }
- }, this);
- };
- proto.makePanelOpts = function (api, getDefaultBrushType) {
- return zrUtil.map(this._targetInfoList, function (targetInfo) {
- var rect = targetInfo.getPanelRect();
- return {
- panelId: targetInfo.panelId,
- defaultBrushType: getDefaultBrushType && getDefaultBrushType(targetInfo),
- clipPath: brushHelper.makeRectPanelClipPath(rect),
- isTargetByCursor: brushHelper.makeRectIsTargetByCursor(rect, api, targetInfo.coordSysModel),
- getLinearBrushOtherExtent: brushHelper.makeLinearBrushOtherExtent(rect)
- };
- });
- };
- proto.controlSeries = function (area, seriesModel, ecModel) {
- // Check whether area is bound in coord, and series do not belong to that coord.
- // If do not do this check, some brush (like lineX) will controll all axes.
- var targetInfo = this.findTargetInfo(area, ecModel);
- return targetInfo === true || targetInfo && indexOf(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0;
- };
- /**
- * If return Object, a coord found.
- * If reutrn true, global found.
- * Otherwise nothing found.
- *
- * @param {Object} area
- * @param {Array} targetInfoList
- * @return {Object|boolean}
- */
- proto.findTargetInfo = function (area, ecModel) {
- var targetInfoList = this._targetInfoList;
- var foundCpts = parseFinder(ecModel, area);
- for (var i = 0; i < targetInfoList.length; i++) {
- var targetInfo = targetInfoList[i];
- var areaPanelId = area.panelId;
- if (areaPanelId) {
- if (targetInfo.panelId === areaPanelId) {
- return targetInfo;
- }
- } else {
- for (var i = 0; i < targetInfoMatchers.length; i++) {
- if (targetInfoMatchers[i](foundCpts, targetInfo)) {
- return targetInfo;
- }
- }
- }
- }
- return true;
- };
- function formatMinMax(minMax) {
- minMax[0] > minMax[1] && minMax.reverse();
- return minMax;
- }
- function parseFinder(ecModel, option) {
- return modelUtil.parseFinder(ecModel, option, {
- includeMainTypes: INCLUDE_FINDER_MAIN_TYPES
- });
- }
- var targetInfoBuilders = {
- grid: function (foundCpts, targetInfoList) {
- var xAxisModels = foundCpts.xAxisModels;
- var yAxisModels = foundCpts.yAxisModels;
- var gridModels = foundCpts.gridModels; // Remove duplicated.
- var gridModelMap = zrUtil.createHashMap();
- var xAxesHas = {};
- var yAxesHas = {};
- if (!xAxisModels && !yAxisModels && !gridModels) {
- return;
- }
- each(xAxisModels, function (axisModel) {
- var gridModel = axisModel.axis.grid.model;
- gridModelMap.set(gridModel.id, gridModel);
- xAxesHas[gridModel.id] = true;
- });
- each(yAxisModels, function (axisModel) {
- var gridModel = axisModel.axis.grid.model;
- gridModelMap.set(gridModel.id, gridModel);
- yAxesHas[gridModel.id] = true;
- });
- each(gridModels, function (gridModel) {
- gridModelMap.set(gridModel.id, gridModel);
- xAxesHas[gridModel.id] = true;
- yAxesHas[gridModel.id] = true;
- });
- gridModelMap.each(function (gridModel) {
- var grid = gridModel.coordinateSystem;
- var cartesians = [];
- each(grid.getCartesians(), function (cartesian, index) {
- if (indexOf(xAxisModels, cartesian.getAxis('x').model) >= 0 || indexOf(yAxisModels, cartesian.getAxis('y').model) >= 0) {
- cartesians.push(cartesian);
- }
- });
- targetInfoList.push({
- panelId: 'grid--' + gridModel.id,
- gridModel: gridModel,
- coordSysModel: gridModel,
- // Use the first one as the representitive coordSys.
- coordSys: cartesians[0],
- coordSyses: cartesians,
- getPanelRect: panelRectBuilder.grid,
- xAxisDeclared: xAxesHas[gridModel.id],
- yAxisDeclared: yAxesHas[gridModel.id]
- });
- });
- },
- geo: function (foundCpts, targetInfoList) {
- each(foundCpts.geoModels, function (geoModel) {
- var coordSys = geoModel.coordinateSystem;
- targetInfoList.push({
- panelId: 'geo--' + geoModel.id,
- geoModel: geoModel,
- coordSysModel: geoModel,
- coordSys: coordSys,
- coordSyses: [coordSys],
- getPanelRect: panelRectBuilder.geo
- });
- });
- }
- };
- var targetInfoMatchers = [// grid
- function (foundCpts, targetInfo) {
- var xAxisModel = foundCpts.xAxisModel;
- var yAxisModel = foundCpts.yAxisModel;
- var gridModel = foundCpts.gridModel;
- !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model);
- !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model);
- return gridModel && gridModel === targetInfo.gridModel;
- }, // geo
- function (foundCpts, targetInfo) {
- var geoModel = foundCpts.geoModel;
- return geoModel && geoModel === targetInfo.geoModel;
- }];
- var panelRectBuilder = {
- grid: function () {
- // grid is not Transformable.
- return this.coordSys.grid.getRect().clone();
- },
- geo: function () {
- var coordSys = this.coordSys;
- var rect = coordSys.getBoundingRect().clone(); // geo roam and zoom transform
- rect.applyTransform(graphic.getTransform(coordSys));
- return rect;
- }
- };
- var coordConvert = {
- lineX: curry(axisConvert, 0),
- lineY: curry(axisConvert, 1),
- rect: function (to, coordSys, rangeOrCoordRange) {
- var xminymin = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]]);
- var xmaxymax = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]]);
- var values = [formatMinMax([xminymin[0], xmaxymax[0]]), formatMinMax([xminymin[1], xmaxymax[1]])];
- return {
- values: values,
- xyMinMax: values
- };
- },
- polygon: function (to, coordSys, rangeOrCoordRange) {
- var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]];
- var values = zrUtil.map(rangeOrCoordRange, function (item) {
- var p = coordSys[COORD_CONVERTS[to]](item);
- xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]);
- xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]);
- xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]);
- xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]);
- return p;
- });
- return {
- values: values,
- xyMinMax: xyMinMax
- };
- }
- };
- function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {
- var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]);
- var values = formatMinMax(zrUtil.map([0, 1], function (i) {
- return to ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i])) : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i]));
- }));
- var xyMinMax = [];
- xyMinMax[axisNameIndex] = values;
- xyMinMax[1 - axisNameIndex] = [NaN, NaN];
- return {
- values: values,
- xyMinMax: xyMinMax
- };
- }
- var diffProcessor = {
- lineX: curry(axisDiffProcessor, 0),
- lineY: curry(axisDiffProcessor, 1),
- rect: function (values, refer, scales) {
- return [[values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]], [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]]];
- },
- polygon: function (values, refer, scales) {
- return zrUtil.map(values, function (item, idx) {
- return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]];
- });
- }
- };
- function axisDiffProcessor(axisNameIndex, values, refer, scales) {
- return [values[0] - scales[axisNameIndex] * refer[0], values[1] - scales[axisNameIndex] * refer[1]];
- } // We have to process scale caused by dataZoom manually,
- // although it might be not accurate.
- function getScales(xyMinMaxCurr, xyMinMaxOrigin) {
- var sizeCurr = getSize(xyMinMaxCurr);
- var sizeOrigin = getSize(xyMinMaxOrigin);
- var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]];
- isNaN(scales[0]) && (scales[0] = 1);
- isNaN(scales[1]) && (scales[1] = 1);
- return scales;
- }
- function getSize(xyMinMax) {
- return xyMinMax ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]] : [NaN, NaN];
- }
- var _default = BrushTargetManager;
- module.exports = _default;
|