| /* | |
| * 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 * as polygonContain from 'zrender/src/contain/polygon'; | |
| import BoundingRect, { RectLike } from 'zrender/src/core/BoundingRect'; | |
| import {linePolygonIntersect} from '../../util/graphic'; | |
| import { BrushType, BrushDimensionMinMax } from '../helper/BrushController'; | |
| import { BrushAreaParamInternal } from './BrushModel'; | |
| export interface BrushSelectableArea extends BrushAreaParamInternal { | |
| boundingRect: BoundingRect; | |
| selectors: BrushCommonSelectorsForSeries | |
| } | |
| /** | |
| * Key of the first level is brushType: `line`, `rect`, `polygon`. | |
| * See moudule:echarts/component/helper/BrushController | |
| * function param: | |
| * {Object} itemLayout fetch from data.getItemLayout(dataIndex) | |
| * {Object} selectors {point: selector, rect: selector, ...} | |
| * {Object} area {range: [[], [], ..], boudingRect} | |
| * function return: | |
| * {boolean} Whether in the given brush. | |
| */ | |
| interface BrushSelectorOnBrushType { | |
| // For chart element type "point" | |
| point( | |
| // fetch from data.getItemLayout(dataIndex) | |
| itemLayout: number[], | |
| selectors: BrushCommonSelectorsForSeries, | |
| area: BrushSelectableArea | |
| ): boolean; | |
| // For chart element type "rect" | |
| rect( | |
| // fetch from data.getItemLayout(dataIndex) | |
| itemLayout: RectLike, | |
| selectors: BrushCommonSelectorsForSeries, | |
| area: BrushSelectableArea | |
| ): boolean; | |
| } | |
| /** | |
| * This methods are corresponding to `BrushSelectorOnBrushType`, | |
| * but `area: BrushSelectableArea` is binded to each method. | |
| */ | |
| export interface BrushCommonSelectorsForSeries { | |
| // For chart element type "point" | |
| point(itemLayout: number[]): boolean; | |
| // For chart element type "rect" | |
| rect(itemLayout: RectLike): boolean; | |
| } | |
| export function makeBrushCommonSelectorForSeries( | |
| area: BrushSelectableArea | |
| ): BrushCommonSelectorsForSeries { | |
| const brushType = area.brushType; | |
| // Do not use function binding or curry for performance. | |
| const selectors: BrushCommonSelectorsForSeries = { | |
| point(itemLayout: number[]) { | |
| return selector[brushType].point(itemLayout, selectors, area); | |
| }, | |
| rect(itemLayout: RectLike) { | |
| return selector[brushType].rect(itemLayout, selectors, area); | |
| } | |
| }; | |
| return selectors; | |
| } | |
| const selector: Record<BrushType, BrushSelectorOnBrushType> = { | |
| lineX: getLineSelectors(0), | |
| lineY: getLineSelectors(1), | |
| rect: { | |
| point: function (itemLayout, selectors, area) { | |
| return itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]); | |
| }, | |
| rect: function (itemLayout, selectors, area) { | |
| return itemLayout && area.boundingRect.intersect(itemLayout); | |
| } | |
| }, | |
| polygon: { | |
| point: function (itemLayout, selectors, area) { | |
| return itemLayout | |
| && area.boundingRect.contain( | |
| itemLayout[0], itemLayout[1] | |
| ) | |
| && polygonContain.contain( | |
| area.range as BrushDimensionMinMax[], itemLayout[0], itemLayout[1] | |
| ); | |
| }, | |
| rect: function (itemLayout, selectors, area) { | |
| const points = area.range as BrushDimensionMinMax[]; | |
| if (!itemLayout || points.length <= 1) { | |
| return false; | |
| } | |
| const x = itemLayout.x; | |
| const y = itemLayout.y; | |
| const width = itemLayout.width; | |
| const height = itemLayout.height; | |
| const p = points[0]; | |
| if (polygonContain.contain(points, x, y) | |
| || polygonContain.contain(points, x + width, y) | |
| || polygonContain.contain(points, x, y + height) | |
| || polygonContain.contain(points, x + width, y + height) | |
| || BoundingRect.create(itemLayout).contain(p[0], p[1]) | |
| || linePolygonIntersect(x, y, x + width, y, points) | |
| || linePolygonIntersect(x, y, x, y + height, points) | |
| || linePolygonIntersect(x + width, y, x + width, y + height, points) | |
| || linePolygonIntersect(x, y + height, x + width, y + height, points) | |
| ) { | |
| return true; | |
| } | |
| } | |
| } | |
| }; | |
| function getLineSelectors(xyIndex: 0 | 1): BrushSelectorOnBrushType { | |
| const xy = ['x', 'y'] as const; | |
| const wh = ['width', 'height'] as const; | |
| return { | |
| point: function (itemLayout, selectors, area) { | |
| if (itemLayout) { | |
| const range = area.range as BrushDimensionMinMax; | |
| const p = itemLayout[xyIndex]; | |
| return inLineRange(p, range); | |
| } | |
| }, | |
| rect: function (itemLayout, selectors, area) { | |
| if (itemLayout) { | |
| const range = area.range as BrushDimensionMinMax; | |
| const layoutRange = [ | |
| itemLayout[xy[xyIndex]], | |
| itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]] | |
| ]; | |
| layoutRange[1] < layoutRange[0] && layoutRange.reverse(); | |
| return inLineRange(layoutRange[0], range) | |
| || inLineRange(layoutRange[1], range) | |
| || inLineRange(range[0], layoutRange) | |
| || inLineRange(range[1], layoutRange); | |
| } | |
| } | |
| }; | |
| } | |
| function inLineRange(p: number, range: BrushDimensionMinMax): boolean { | |
| return range[0] <= p && p <= range[1]; | |
| } | |
| export default selector; |