Spaces:
Running
Running
/* | |
* 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; | |