| /* |
| * 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 BoundingRect from 'zrender/src/core/BoundingRect'; |
| import Cartesian from './Cartesian'; |
| import { ScaleDataValue } from '../../util/types'; |
| import Axis2D from './Axis2D'; |
| import { CoordinateSystem } from '../CoordinateSystem'; |
| import GridModel from './GridModel'; |
| import Grid from './Grid'; |
| import Scale from '../../scale/Scale'; |
| import { invert } from 'zrender/src/core/matrix'; |
| import { applyTransform } from 'zrender/src/core/vector'; |
| |
| export const cartesian2DDimensions = ['x', 'y']; |
| |
| function canCalculateAffineTransform(scale: Scale) { |
| return scale.type === 'interval' || scale.type === 'time'; |
| } |
| |
| class Cartesian2D extends Cartesian<Axis2D> implements CoordinateSystem { |
| |
| readonly type = 'cartesian2d'; |
| |
| readonly dimensions = cartesian2DDimensions; |
| |
| model: GridModel; |
| |
| master: Grid; |
| |
| private _transform: number[]; |
| private _invTransform: number[]; |
| |
| /** |
| * Calculate an affine transform matrix if two axes are time or value. |
| * It's mainly for accelartion on the large time series data. |
| */ |
| calcAffineTransform() { |
| this._transform = this._invTransform = null; |
| |
| const xAxisScale = this.getAxis('x').scale; |
| const yAxisScale = this.getAxis('y').scale; |
| |
| if (!canCalculateAffineTransform(xAxisScale) || !canCalculateAffineTransform(yAxisScale)) { |
| return; |
| } |
| |
| const xScaleExtent = xAxisScale.getExtent(); |
| const yScaleExtent = yAxisScale.getExtent(); |
| |
| const start = this.dataToPoint([xScaleExtent[0], yScaleExtent[0]]); |
| const end = this.dataToPoint([xScaleExtent[1], yScaleExtent[1]]); |
| |
| const xScaleSpan = xScaleExtent[1] - xScaleExtent[0]; |
| const yScaleSpan = yScaleExtent[1] - yScaleExtent[0]; |
| |
| if (!xScaleSpan || !yScaleSpan) { |
| return; |
| } |
| // Accelerate data to point calculation on the special large time series data. |
| const scaleX = (end[0] - start[0]) / xScaleSpan; |
| const scaleY = (end[1] - start[1]) / yScaleSpan; |
| const translateX = start[0] - xScaleExtent[0] * scaleX; |
| const translateY = start[1] - yScaleExtent[0] * scaleY; |
| |
| const m = this._transform = [scaleX, 0, 0, scaleY, translateX, translateY]; |
| this._invTransform = invert([], m); |
| } |
| |
| /** |
| * Base axis will be used on stacking. |
| */ |
| getBaseAxis(): Axis2D { |
| return this.getAxesByScale('ordinal')[0] |
| || this.getAxesByScale('time')[0] |
| || this.getAxis('x'); |
| } |
| |
| containPoint(point: number[]): boolean { |
| const axisX = this.getAxis('x'); |
| const axisY = this.getAxis('y'); |
| return axisX.contain(axisX.toLocalCoord(point[0])) |
| && axisY.contain(axisY.toLocalCoord(point[1])); |
| } |
| |
| containData(data: ScaleDataValue[]): boolean { |
| return this.getAxis('x').containData(data[0]) |
| && this.getAxis('y').containData(data[1]); |
| } |
| |
| dataToPoint(data: ScaleDataValue[], reserved?: unknown, out?: number[]): number[] { |
| out = out || []; |
| const xVal = data[0]; |
| const yVal = data[1]; |
| // Fast path |
| if (this._transform |
| // It's supported that if data is like `[Inifity, 123]`, where only Y pixel calculated. |
| && xVal != null |
| && isFinite(xVal as number) |
| && yVal != null |
| && isFinite(yVal as number) |
| ) { |
| return applyTransform(out, data as number[], this._transform); |
| } |
| const xAxis = this.getAxis('x'); |
| const yAxis = this.getAxis('y'); |
| out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(xVal)); |
| out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(yVal)); |
| return out; |
| } |
| |
| clampData(data: ScaleDataValue[], out?: number[]): number[] { |
| const xScale = this.getAxis('x').scale; |
| const yScale = this.getAxis('y').scale; |
| const xAxisExtent = xScale.getExtent(); |
| const yAxisExtent = yScale.getExtent(); |
| const x = xScale.parse(data[0]); |
| const y = yScale.parse(data[1]); |
| out = out || []; |
| out[0] = Math.min( |
| Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x), |
| Math.max(xAxisExtent[0], xAxisExtent[1]) |
| ); |
| out[1] = Math.min( |
| Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y), |
| Math.max(yAxisExtent[0], yAxisExtent[1]) |
| ); |
| |
| return out; |
| } |
| |
| pointToData(point: number[], out?: number[]): number[] { |
| out = out || []; |
| if (this._invTransform) { |
| return applyTransform(out, point, this._invTransform); |
| } |
| const xAxis = this.getAxis('x'); |
| const yAxis = this.getAxis('y'); |
| out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0])); |
| out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1])); |
| return out; |
| } |
| |
| getOtherAxis(axis: Axis2D): Axis2D { |
| return this.getAxis(axis.dim === 'x' ? 'y' : 'x'); |
| } |
| |
| /** |
| * Get rect area of cartesian. |
| * Area will have a contain function to determine if a point is in the coordinate system. |
| */ |
| getArea(): Cartesian2DArea { |
| const xExtent = this.getAxis('x').getGlobalExtent(); |
| const yExtent = this.getAxis('y').getGlobalExtent(); |
| const x = Math.min(xExtent[0], xExtent[1]); |
| const y = Math.min(yExtent[0], yExtent[1]); |
| const width = Math.max(xExtent[0], xExtent[1]) - x; |
| const height = Math.max(yExtent[0], yExtent[1]) - y; |
| |
| return new BoundingRect(x, y, width, height); |
| } |
| |
| }; |
| |
| interface Cartesian2DArea extends BoundingRect {} |
| |
| export default Cartesian2D; |