| /* | 
 | * 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. | 
 | */ | 
 |  | 
 |  | 
 | import { each, indexOf, curry, assert, map, createHashMap } from 'zrender/src/core/util'; | 
 | import * as graphic from '../../util/graphic'; | 
 | import * as brushHelper from './brushHelper'; | 
 | import { | 
 |     BrushPanelConfig, BrushControllerEvents, BrushType, | 
 |     BrushAreaRange, BrushDimensionMinMax | 
 | } from './BrushController'; | 
 | import ExtensionAPI from '../../core/ExtensionAPI'; | 
 | import GridModel from '../../coord/cartesian/GridModel'; | 
 | import GeoModel from '../../coord/geo/GeoModel'; | 
 | import { CoordinateSystemMaster } from '../../coord/CoordinateSystem'; | 
 | import Cartesian2D from '../../coord/cartesian/Cartesian2D'; | 
 | import Geo from '../../coord/geo/Geo'; | 
 | import GlobalModel from '../../model/Global'; | 
 | import { BrushAreaParam, BrushAreaParamInternal } from '../brush/BrushModel'; | 
 | import SeriesModel from '../../model/Series'; | 
 | import { Dictionary } from '../../util/types'; | 
 | import { | 
 |     ModelFinderObject, ModelFinder, | 
 |     parseFinder as modelUtilParseFinder, | 
 |     ParsedModelFinderKnown | 
 | } from '../../util/model'; | 
 |  | 
 | type COORD_CONVERTS_INDEX = 0 | 1; | 
 |  | 
 | // FIXME | 
 | // how to genarialize to more coordinate systems. | 
 | const INCLUDE_FINDER_MAIN_TYPES = [ | 
 |     'grid', 'xAxis', 'yAxis', 'geo', 'graph', | 
 |     'polar', 'radiusAxis', 'angleAxis', 'bmap' | 
 | ]; | 
 |  | 
 | type BrushableCoordinateSystem = Cartesian2D | Geo; | 
 | type BrushTargetBuilderKey = 'grid' | 'geo'; | 
 |  | 
 | /** | 
 |  * 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. | 
 |  */ | 
 | interface BrushTargetInfo { | 
 |     panelId: string; | 
 |     coordSysModel: CoordinateSystemMaster['model']; | 
 |     // Use the first one as the representitive coordSys. | 
 |     // A representitive cartesian in grid (first cartesian by default). | 
 |     coordSys: BrushableCoordinateSystem; | 
 |     // All cartesians. | 
 |     coordSyses: BrushableCoordinateSystem[]; | 
 |     getPanelRect: GetPanelRect, | 
 | } | 
 | export interface BrushTargetInfoCartesian2D extends BrushTargetInfo { | 
 |     gridModel: GridModel; | 
 |     coordSys: Cartesian2D; | 
 |     coordSyses: Cartesian2D[]; | 
 |     xAxisDeclared: boolean; | 
 |     yAxisDeclared: boolean; | 
 | } | 
 | export interface BrushTargetInfoGeo extends BrushTargetInfo { | 
 |     geoModel: GeoModel, | 
 |     coordSysModel: GeoModel, | 
 |     coordSys: Geo, | 
 |     coordSyses: Geo[], | 
 | } | 
 | type GetPanelRect = () => graphic.BoundingRect; | 
 |  | 
 |  | 
 | class BrushTargetManager { | 
 |  | 
 |     private _targetInfoList: BrushTargetInfo[] = []; | 
 |  | 
 |     /** | 
 |      * @param finder contains Index/Id/Name of xAxis/yAxis/geo/grid | 
 |      *        Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]} | 
 |      * @param opt.include include coordinate system types. | 
 |      */ | 
 |     constructor( | 
 |         finder: ModelFinderObject, | 
 |         ecModel: GlobalModel, | 
 |         opt?: {include?: BrushTargetBuilderKey[]} | 
 |     ) { | 
 |         const foundCpts = parseFinder(ecModel, finder); | 
 |  | 
 |         each(targetInfoBuilders, (builder, type) => { | 
 |             if (!opt || !opt.include || indexOf(opt.include, type) >= 0) { | 
 |                 builder(foundCpts, this._targetInfoList); | 
 |             } | 
 |         }); | 
 |     } | 
 |  | 
 |     setOutputRanges( | 
 |         areas: BrushControllerEvents['brush']['areas'], | 
 |         ecModel: GlobalModel | 
 |     ): BrushAreaParam[] { | 
 |         this.matchOutputRanges(areas, ecModel, function ( | 
 |             area: BrushAreaParam, | 
 |             coordRange: ReturnType<ConvertCoord>['values'], | 
 |             coordSys: BrushableCoordinateSystem | 
 |         ) { | 
 |             (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. | 
 |                 const result = coordConvert[area.brushType](0, coordSys, coordRange); | 
 |                 area.__rangeOffset = { | 
 |                     offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]), | 
 |                     xyMinMax: result.xyMinMax | 
 |                 }; | 
 |             } | 
 |         }); | 
 |         return areas; | 
 |     } | 
 |  | 
 |     matchOutputRanges<T extends ( | 
 |         Parameters<BrushTargetManager['findTargetInfo']>[0] & { | 
 |             brushType: BrushType; | 
 |             range: BrushAreaRange; | 
 |         } | 
 |     )>( | 
 |         areas: T[], | 
 |         ecModel: GlobalModel, | 
 |         cb: ( | 
 |             area: T, | 
 |             coordRange: ReturnType<ConvertCoord>['values'], | 
 |             coordSys: BrushableCoordinateSystem, | 
 |             ecModel: GlobalModel | 
 |         ) => void | 
 |     ) { | 
 |         each(areas, function (area) { | 
 |             const targetInfo = this.findTargetInfo(area, ecModel); | 
 |  | 
 |             if (targetInfo && targetInfo !== true) { | 
 |                 each( | 
 |                     targetInfo.coordSyses, | 
 |                     function (coordSys) { | 
 |                         const result = coordConvert[area.brushType](1, coordSys, area.range, true); | 
 |                         cb(area, result.values, coordSys, ecModel); | 
 |                     } | 
 |                 ); | 
 |             } | 
 |         }, this); | 
 |     } | 
 |  | 
 |     /** | 
 |      * the `areas` is `BrushModel.areas`. | 
 |      * Called in layout stage. | 
 |      * convert `area.coordRange` to global range and set panelId to `area.range`. | 
 |      */ | 
 |     setInputRanges( | 
 |         areas: BrushAreaParamInternal[], | 
 |         ecModel: GlobalModel | 
 |     ): void { | 
 |         each(areas, function (area) { | 
 |             const targetInfo = this.findTargetInfo(area, ecModel); | 
 |  | 
 |             if (__DEV__) { | 
 |                 assert( | 
 |                     !targetInfo || targetInfo === true || area.coordRange, | 
 |                     'coordRange must be specified when coord index specified.' | 
 |                 ); | 
 |                 assert( | 
 |                     !targetInfo || targetInfo !== true || area.range, | 
 |                     'range must be specified in global brush.' | 
 |                 ); | 
 |             } | 
 |  | 
 |             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. | 
 |                 const result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange); | 
 |                 const rangeOffset = area.__rangeOffset; | 
 |                 area.range = rangeOffset | 
 |                     ? diffProcessor[area.brushType]( | 
 |                         result.values, | 
 |                         rangeOffset.offset, | 
 |                         getScales(result.xyMinMax, rangeOffset.xyMinMax) | 
 |                     ) | 
 |                     : result.values; | 
 |             } | 
 |         }, this); | 
 |     } | 
 |  | 
 |     makePanelOpts( | 
 |         api: ExtensionAPI, | 
 |         getDefaultBrushType?: (targetInfo: BrushTargetInfo) => BrushType | 
 |     ): BrushPanelConfig[] { | 
 |         return map(this._targetInfoList, function (targetInfo) { | 
 |             const rect = targetInfo.getPanelRect(); | 
 |             return { | 
 |                 panelId: targetInfo.panelId, | 
 |                 defaultBrushType: getDefaultBrushType ? getDefaultBrushType(targetInfo) : null, | 
 |                 clipPath: brushHelper.makeRectPanelClipPath(rect), | 
 |                 isTargetByCursor: brushHelper.makeRectIsTargetByCursor( | 
 |                     rect, api, targetInfo.coordSysModel | 
 |                 ), | 
 |                 getLinearBrushOtherExtent: brushHelper.makeLinearBrushOtherExtent(rect) | 
 |             }; | 
 |         }); | 
 |     } | 
 |  | 
 |     controlSeries(area: BrushAreaParamInternal, seriesModel: SeriesModel, ecModel: GlobalModel): boolean { | 
 |         // 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. | 
 |         const targetInfo = this.findTargetInfo(area, ecModel); | 
 |         return targetInfo === true || ( | 
 |             targetInfo && indexOf( | 
 |                 targetInfo.coordSyses, seriesModel.coordinateSystem as BrushableCoordinateSystem | 
 |             ) >= 0 | 
 |         ); | 
 |     } | 
 |  | 
 |     /** | 
 |      * If return Object, a coord found. | 
 |      * If reutrn true, global found. | 
 |      * Otherwise nothing found. | 
 |      */ | 
 |     findTargetInfo( | 
 |         area: ModelFinderObject & { | 
 |             panelId?: string | 
 |         }, | 
 |         ecModel: GlobalModel | 
 |     ): BrushTargetInfo | true { | 
 |         const targetInfoList = this._targetInfoList; | 
 |         const foundCpts = parseFinder(ecModel, area); | 
 |  | 
 |         for (let i = 0; i < targetInfoList.length; i++) { | 
 |             const targetInfo = targetInfoList[i]; | 
 |             const areaPanelId = area.panelId; | 
 |             if (areaPanelId) { | 
 |                 if (targetInfo.panelId === areaPanelId) { | 
 |                     return targetInfo; | 
 |                 } | 
 |             } | 
 |             else { | 
 |                 for (let j = 0; j < targetInfoMatchers.length; j++) { | 
 |                     if (targetInfoMatchers[j](foundCpts, targetInfo)) { | 
 |                         return targetInfo; | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         return true; | 
 |     } | 
 |  | 
 | } | 
 |  | 
 | function formatMinMax(minMax: BrushDimensionMinMax): BrushDimensionMinMax { | 
 |     minMax[0] > minMax[1] && minMax.reverse(); | 
 |     return minMax; | 
 | } | 
 |  | 
 | function parseFinder( | 
 |     ecModel: GlobalModel, finder: ModelFinder | 
 | ): ParsedModelFinderKnown { | 
 |     return modelUtilParseFinder( | 
 |         ecModel, finder, {includeMainTypes: INCLUDE_FINDER_MAIN_TYPES} | 
 |     ); | 
 | } | 
 |  | 
 | type TargetInfoBuilder = ( | 
 |     foundCpts: ParsedModelFinderKnown, targetInfoList: BrushTargetInfo[] | 
 | ) => void; | 
 | const targetInfoBuilders: Record<BrushTargetBuilderKey, TargetInfoBuilder> = { | 
 |  | 
 |     grid: function (foundCpts, targetInfoList) { | 
 |         const xAxisModels = foundCpts.xAxisModels; | 
 |         const yAxisModels = foundCpts.yAxisModels; | 
 |         const gridModels = foundCpts.gridModels; | 
 |         // Remove duplicated. | 
 |         const gridModelMap = createHashMap<GridModel>(); | 
 |         const xAxesHas = {} as Dictionary<boolean>; | 
 |         const yAxesHas = {} as Dictionary<boolean>; | 
 |  | 
 |         if (!xAxisModels && !yAxisModels && !gridModels) { | 
 |             return; | 
 |         } | 
 |  | 
 |         each(xAxisModels, function (axisModel) { | 
 |             const gridModel = axisModel.axis.grid.model; | 
 |             gridModelMap.set(gridModel.id, gridModel); | 
 |             xAxesHas[gridModel.id] = true; | 
 |         }); | 
 |         each(yAxisModels, function (axisModel) { | 
 |             const 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) { | 
 |             const grid = gridModel.coordinateSystem; | 
 |             const cartesians = [] as Cartesian2D[]; | 
 |  | 
 |             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: panelRectBuilders.grid, | 
 |                 xAxisDeclared: xAxesHas[gridModel.id], | 
 |                 yAxisDeclared: yAxesHas[gridModel.id] | 
 |             } as BrushTargetInfoCartesian2D); | 
 |         }); | 
 |     }, | 
 |  | 
 |     geo: function (foundCpts, targetInfoList) { | 
 |         each(foundCpts.geoModels, function (geoModel: GeoModel) { | 
 |             const coordSys = geoModel.coordinateSystem; | 
 |             targetInfoList.push({ | 
 |                 panelId: 'geo--' + geoModel.id, | 
 |                 geoModel: geoModel, | 
 |                 coordSysModel: geoModel, | 
 |                 coordSys: coordSys, | 
 |                 coordSyses: [coordSys], | 
 |                 getPanelRect: panelRectBuilders.geo | 
 |             } as BrushTargetInfoGeo); | 
 |         }); | 
 |     } | 
 | }; | 
 |  | 
 | type TargetInfoMatcher = ( | 
 |     foundCpts: ParsedModelFinderKnown, targetInfo: BrushTargetInfo | 
 | ) => boolean; | 
 | const targetInfoMatchers: TargetInfoMatcher[] = [ | 
 |  | 
 |     // grid | 
 |     function (foundCpts, targetInfo) { | 
 |         const xAxisModel = foundCpts.xAxisModel; | 
 |         const yAxisModel = foundCpts.yAxisModel; | 
 |         let gridModel = foundCpts.gridModel; | 
 |  | 
 |         !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model); | 
 |         !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model); | 
 |  | 
 |         return gridModel && gridModel === (targetInfo as BrushTargetInfoCartesian2D).gridModel; | 
 |     }, | 
 |  | 
 |     // geo | 
 |     function (foundCpts, targetInfo) { | 
 |         const geoModel = foundCpts.geoModel; | 
 |         return geoModel && geoModel === (targetInfo as BrushTargetInfoGeo).geoModel; | 
 |     } | 
 | ]; | 
 |  | 
 | type PanelRectBuilder = (this: BrushTargetInfo) => graphic.BoundingRect; | 
 | const panelRectBuilders: Record<BrushTargetBuilderKey, PanelRectBuilder> = { | 
 |  | 
 |     grid: function (this: BrushTargetInfoCartesian2D) { | 
 |         // grid is not Transformable. | 
 |         return this.coordSys.master.getRect().clone(); | 
 |     }, | 
 |  | 
 |     geo: function (this: BrushTargetInfoGeo) { | 
 |         const coordSys = this.coordSys; | 
 |         const rect = coordSys.getBoundingRect().clone(); | 
 |         // geo roam and zoom transform | 
 |         rect.applyTransform(graphic.getTransform(coordSys)); | 
 |         return rect; | 
 |     } | 
 | }; | 
 |  | 
 | type ConvertCoord = ( | 
 |     to: COORD_CONVERTS_INDEX, | 
 |     coordSys: BrushableCoordinateSystem, | 
 |     rangeOrCoordRange: BrushAreaRange, | 
 |     clamp?: boolean | 
 | ) => { | 
 |     values: BrushAreaRange, | 
 |     xyMinMax: BrushDimensionMinMax[] | 
 | }; | 
 | const coordConvert: Record<BrushType, ConvertCoord> = { | 
 |  | 
 |     lineX: curry(axisConvert, 0), | 
 |  | 
 |     lineY: curry(axisConvert, 1), | 
 |  | 
 |     rect: function (to, coordSys, rangeOrCoordRange: BrushDimensionMinMax[], clamp): { | 
 |         values: BrushDimensionMinMax[], | 
 |         xyMinMax: BrushDimensionMinMax[] | 
 |     } { | 
 |         const xminymin = to | 
 |             ? coordSys.pointToData([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp) | 
 |             : coordSys.dataToPoint([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp); | 
 |         const xmaxymax = to | 
 |             ? coordSys.pointToData([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp) | 
 |             : coordSys.dataToPoint([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp); | 
 |         const values = [ | 
 |             formatMinMax([xminymin[0], xmaxymax[0]]), | 
 |             formatMinMax([xminymin[1], xmaxymax[1]]) | 
 |         ]; | 
 |         return {values: values, xyMinMax: values}; | 
 |     }, | 
 |  | 
 |     polygon: function (to, coordSys, rangeOrCoordRange: BrushDimensionMinMax[], clamp): { | 
 |         values: BrushDimensionMinMax[], | 
 |         xyMinMax: BrushDimensionMinMax[] | 
 |     } { | 
 |         const xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]]; | 
 |         const values = map(rangeOrCoordRange, function (item) { | 
 |             const p = to ? coordSys.pointToData(item, clamp) : coordSys.dataToPoint(item, clamp); | 
 |             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: 0 | 1, | 
 |     to: COORD_CONVERTS_INDEX, | 
 |     coordSys: Cartesian2D, | 
 |     rangeOrCoordRange: BrushDimensionMinMax | 
 | ): { | 
 |     values: BrushDimensionMinMax, | 
 |     xyMinMax: BrushDimensionMinMax[] | 
 | } { | 
 |     if (__DEV__) { | 
 |         assert( | 
 |             coordSys.type === 'cartesian2d', | 
 |             'lineX/lineY brush is available only in cartesian2d.' | 
 |         ); | 
 |     } | 
 |  | 
 |     const axis = coordSys.getAxis(['x', 'y'][axisNameIndex]); | 
 |     const values = formatMinMax(map([0, 1], function (i) { | 
 |         return to | 
 |             ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i]), true) | 
 |             : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i])); | 
 |     })); | 
 |     const xyMinMax = []; | 
 |     xyMinMax[axisNameIndex] = values; | 
 |     xyMinMax[1 - axisNameIndex] = [NaN, NaN]; | 
 |  | 
 |     return {values: values, xyMinMax: xyMinMax}; | 
 | } | 
 |  | 
 |  | 
 | type DiffProcess = ( | 
 |     values: BrushDimensionMinMax | BrushDimensionMinMax[], | 
 |     refer: BrushDimensionMinMax | BrushDimensionMinMax[], | 
 |     scales: ReturnType<typeof getScales> | 
 | ) => BrushDimensionMinMax | BrushDimensionMinMax[]; | 
 |  | 
 | const diffProcessor: Record<BrushType, DiffProcess> = { | 
 |  | 
 |     lineX: curry(axisDiffProcessor, 0), | 
 |  | 
 |     lineY: curry(axisDiffProcessor, 1), | 
 |  | 
 |     rect: function ( | 
 |         values: BrushDimensionMinMax[], refer: BrushDimensionMinMax[], scales: ReturnType<typeof getScales> | 
 |     ): BrushDimensionMinMax[] { | 
 |         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: BrushDimensionMinMax[], refer: BrushDimensionMinMax[], scales: ReturnType<typeof getScales> | 
 |     ): BrushDimensionMinMax[] { | 
 |         return map(values, function (item, idx) { | 
 |             return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]]; | 
 |         }); | 
 |     } | 
 | }; | 
 |  | 
 | function axisDiffProcessor( | 
 |     axisNameIndex: 0 | 1, | 
 |     values: BrushDimensionMinMax, | 
 |     refer: BrushDimensionMinMax, | 
 |     scales: ReturnType<typeof getScales> | 
 | ): BrushDimensionMinMax { | 
 |     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. | 
 | // Return [0~1, 0~1] | 
 | function getScales(xyMinMaxCurr: BrushDimensionMinMax[], xyMinMaxOrigin: BrushDimensionMinMax[]): number[] { | 
 |     const sizeCurr = getSize(xyMinMaxCurr); | 
 |     const sizeOrigin = getSize(xyMinMaxOrigin); | 
 |     const 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: BrushDimensionMinMax[]): number[] { | 
 |     return xyMinMax | 
 |         ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]] | 
 |         : [NaN, NaN]; | 
 | } | 
 |  | 
 | export default BrushTargetManager; |