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