123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- /*
- * 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 echarts = require("../../echarts");
- var zrUtil = require("zrender/lib/core/util");
- var BoundingRect = require("zrender/lib/core/BoundingRect");
- var visualSolution = require("../../visual/visualSolution");
- var selector = require("./selector");
- var throttleUtil = require("../../util/throttle");
- var BrushTargetManager = require("../helper/BrushTargetManager");
- /*
- * 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 STATE_LIST = ['inBrush', 'outOfBrush'];
- var DISPATCH_METHOD = '__ecBrushSelect';
- var DISPATCH_FLAG = '__ecInBrushSelectEvent';
- var PRIORITY_BRUSH = echarts.PRIORITY.VISUAL.BRUSH;
- /**
- * Layout for visual, the priority higher than other layout, and before brush visual.
- */
- echarts.registerLayout(PRIORITY_BRUSH, function (ecModel, api, payload) {
- ecModel.eachComponent({
- mainType: 'brush'
- }, function (brushModel) {
- payload && payload.type === 'takeGlobalCursor' && brushModel.setBrushOption(payload.key === 'brush' ? payload.brushOption : {
- brushType: false
- });
- });
- layoutCovers(ecModel);
- });
- function layoutCovers(ecModel) {
- ecModel.eachComponent({
- mainType: 'brush'
- }, function (brushModel) {
- var brushTargetManager = brushModel.brushTargetManager = new BrushTargetManager(brushModel.option, ecModel);
- brushTargetManager.setInputRanges(brushModel.areas, ecModel);
- });
- }
- /**
- * Register the visual encoding if this modules required.
- */
- echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
- var brushSelected = [];
- var throttleType;
- var throttleDelay;
- ecModel.eachComponent({
- mainType: 'brush'
- }, function (brushModel, brushIndex) {
- var thisBrushSelected = {
- brushId: brushModel.id,
- brushIndex: brushIndex,
- brushName: brushModel.name,
- areas: zrUtil.clone(brushModel.areas),
- selected: []
- }; // Every brush component exists in event params, convenient
- // for user to find by index.
- brushSelected.push(thisBrushSelected);
- var brushOption = brushModel.option;
- var brushLink = brushOption.brushLink;
- var linkedSeriesMap = [];
- var selectedDataIndexForLink = [];
- var rangeInfoBySeries = [];
- var hasBrushExists = 0;
- if (!brushIndex) {
- // Only the first throttle setting works.
- throttleType = brushOption.throttleType;
- throttleDelay = brushOption.throttleDelay;
- } // Add boundingRect and selectors to range.
- var areas = zrUtil.map(brushModel.areas, function (area) {
- return bindSelector(zrUtil.defaults({
- boundingRect: boundingRectBuilders[area.brushType](area)
- }, area));
- });
- var visualMappings = visualSolution.createVisualMappings(brushModel.option, STATE_LIST, function (mappingOption) {
- mappingOption.mappingMethod = 'fixed';
- });
- zrUtil.isArray(brushLink) && zrUtil.each(brushLink, function (seriesIndex) {
- linkedSeriesMap[seriesIndex] = 1;
- });
- function linkOthers(seriesIndex) {
- return brushLink === 'all' || linkedSeriesMap[seriesIndex];
- } // If no supported brush or no brush on the series,
- // all visuals should be in original state.
- function brushed(rangeInfoList) {
- return !!rangeInfoList.length;
- }
- /**
- * Logic for each series: (If the logic has to be modified one day, do it carefully!)
- *
- * ( brushed ┬ && ┬hasBrushExist ┬ && linkOthers ) => StepA: ┬record, ┬ StepB: ┬visualByRecord.
- * !brushed┘ ├hasBrushExist ┤ └nothing,┘ ├visualByRecord.
- * └!hasBrushExist┘ └nothing.
- * ( !brushed && ┬hasBrushExist ┬ && linkOthers ) => StepA: nothing, StepB: ┬visualByRecord.
- * └!hasBrushExist┘ └nothing.
- * ( brushed ┬ && !linkOthers ) => StepA: nothing, StepB: ┬visualByCheck.
- * !brushed┘ └nothing.
- * ( !brushed && !linkOthers ) => StepA: nothing, StepB: nothing.
- */
- // Step A
- ecModel.eachSeries(function (seriesModel, seriesIndex) {
- var rangeInfoList = rangeInfoBySeries[seriesIndex] = [];
- seriesModel.subType === 'parallel' ? stepAParallel(seriesModel, seriesIndex, rangeInfoList) : stepAOthers(seriesModel, seriesIndex, rangeInfoList);
- });
- function stepAParallel(seriesModel, seriesIndex) {
- var coordSys = seriesModel.coordinateSystem;
- hasBrushExists |= coordSys.hasAxisBrushed();
- linkOthers(seriesIndex) && coordSys.eachActiveState(seriesModel.getData(), function (activeState, dataIndex) {
- activeState === 'active' && (selectedDataIndexForLink[dataIndex] = 1);
- });
- }
- function stepAOthers(seriesModel, seriesIndex, rangeInfoList) {
- var selectorsByBrushType = getSelectorsByBrushType(seriesModel);
- if (!selectorsByBrushType || brushModelNotControll(brushModel, seriesIndex)) {
- return;
- }
- zrUtil.each(areas, function (area) {
- selectorsByBrushType[area.brushType] && brushModel.brushTargetManager.controlSeries(area, seriesModel, ecModel) && rangeInfoList.push(area);
- hasBrushExists |= brushed(rangeInfoList);
- });
- if (linkOthers(seriesIndex) && brushed(rangeInfoList)) {
- var data = seriesModel.getData();
- data.each(function (dataIndex) {
- if (checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex)) {
- selectedDataIndexForLink[dataIndex] = 1;
- }
- });
- }
- } // Step B
- ecModel.eachSeries(function (seriesModel, seriesIndex) {
- var seriesBrushSelected = {
- seriesId: seriesModel.id,
- seriesIndex: seriesIndex,
- seriesName: seriesModel.name,
- dataIndex: []
- }; // Every series exists in event params, convenient
- // for user to find series by seriesIndex.
- thisBrushSelected.selected.push(seriesBrushSelected);
- var selectorsByBrushType = getSelectorsByBrushType(seriesModel);
- var rangeInfoList = rangeInfoBySeries[seriesIndex];
- var data = seriesModel.getData();
- var getValueState = linkOthers(seriesIndex) ? function (dataIndex) {
- return selectedDataIndexForLink[dataIndex] ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') : 'outOfBrush';
- } : function (dataIndex) {
- return checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') : 'outOfBrush';
- }; // If no supported brush or no brush, all visuals are in original state.
- (linkOthers(seriesIndex) ? hasBrushExists : brushed(rangeInfoList)) && visualSolution.applyVisual(STATE_LIST, visualMappings, data, getValueState);
- });
- });
- dispatchAction(api, throttleType, throttleDelay, brushSelected, payload);
- });
- function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload) {
- // This event will not be triggered when `setOpion`, otherwise dead lock may
- // triggered when do `setOption` in event listener, which we do not find
- // satisfactory way to solve yet. Some considered resolutions:
- // (a) Diff with prevoius selected data ant only trigger event when changed.
- // But store previous data and diff precisely (i.e., not only by dataIndex, but
- // also detect value changes in selected data) might bring complexity or fragility.
- // (b) Use spectial param like `silent` to suppress event triggering.
- // But such kind of volatile param may be weird in `setOption`.
- if (!payload) {
- return;
- }
- var zr = api.getZr();
- if (zr[DISPATCH_FLAG]) {
- return;
- }
- if (!zr[DISPATCH_METHOD]) {
- zr[DISPATCH_METHOD] = doDispatch;
- }
- var fn = throttleUtil.createOrUpdate(zr, DISPATCH_METHOD, throttleDelay, throttleType);
- fn(api, brushSelected);
- }
- function doDispatch(api, brushSelected) {
- if (!api.isDisposed()) {
- var zr = api.getZr();
- zr[DISPATCH_FLAG] = true;
- api.dispatchAction({
- type: 'brushSelect',
- batch: brushSelected
- });
- zr[DISPATCH_FLAG] = false;
- }
- }
- function checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) {
- for (var i = 0, len = rangeInfoList.length; i < len; i++) {
- var area = rangeInfoList[i];
- if (selectorsByBrushType[area.brushType](dataIndex, data, area.selectors, area)) {
- return true;
- }
- }
- }
- function getSelectorsByBrushType(seriesModel) {
- var brushSelector = seriesModel.brushSelector;
- if (zrUtil.isString(brushSelector)) {
- var sels = [];
- zrUtil.each(selector, function (selectorsByElementType, brushType) {
- sels[brushType] = function (dataIndex, data, selectors, area) {
- var itemLayout = data.getItemLayout(dataIndex);
- return selectorsByElementType[brushSelector](itemLayout, selectors, area);
- };
- });
- return sels;
- } else if (zrUtil.isFunction(brushSelector)) {
- var bSelector = {};
- zrUtil.each(selector, function (sel, brushType) {
- bSelector[brushType] = brushSelector;
- });
- return bSelector;
- }
- return brushSelector;
- }
- function brushModelNotControll(brushModel, seriesIndex) {
- var seriesIndices = brushModel.option.seriesIndex;
- return seriesIndices != null && seriesIndices !== 'all' && (zrUtil.isArray(seriesIndices) ? zrUtil.indexOf(seriesIndices, seriesIndex) < 0 : seriesIndex !== seriesIndices);
- }
- function bindSelector(area) {
- var selectors = area.selectors = {};
- zrUtil.each(selector[area.brushType], function (selFn, elType) {
- // Do not use function binding or curry for performance.
- selectors[elType] = function (itemLayout) {
- return selFn(itemLayout, selectors, area);
- };
- });
- return area;
- }
- var boundingRectBuilders = {
- lineX: zrUtil.noop,
- lineY: zrUtil.noop,
- rect: function (area) {
- return getBoundingRectFromMinMax(area.range);
- },
- polygon: function (area) {
- var minMax;
- var range = area.range;
- for (var i = 0, len = range.length; i < len; i++) {
- minMax = minMax || [[Infinity, -Infinity], [Infinity, -Infinity]];
- var rg = range[i];
- rg[0] < minMax[0][0] && (minMax[0][0] = rg[0]);
- rg[0] > minMax[0][1] && (minMax[0][1] = rg[0]);
- rg[1] < minMax[1][0] && (minMax[1][0] = rg[1]);
- rg[1] > minMax[1][1] && (minMax[1][1] = rg[1]);
- }
- return minMax && getBoundingRectFromMinMax(minMax);
- }
- };
- function getBoundingRectFromMinMax(minMax) {
- return new BoundingRect(minMax[0][0], minMax[1][0], minMax[0][1] - minMax[0][0], minMax[1][1] - minMax[1][0]);
- }
- exports.layoutCovers = layoutCovers;
|