| 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;
 |