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