/*
|
* 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.
|
*/
|
|
/**
|
* Simple view coordinate system
|
* Mapping given x, y to transformd view x, y
|
*/
|
|
import * as vector from 'zrender/src/core/vector';
|
import * as matrix from 'zrender/src/core/matrix';
|
import BoundingRect from 'zrender/src/core/BoundingRect';
|
import Transformable from 'zrender/src/core/Transformable';
|
import { CoordinateSystemMaster, CoordinateSystem } from './CoordinateSystem';
|
import GlobalModel from '../model/Global';
|
import { ParsedModelFinder, ParsedModelFinderKnown } from '../util/model';
|
|
const v2ApplyTransform = vector.applyTransform;
|
|
export type ViewCoordSysTransformInfoPart = Pick<Transformable, 'x' | 'y' | 'scaleX' | 'scaleY'>;
|
|
class View extends Transformable implements CoordinateSystemMaster, CoordinateSystem {
|
|
readonly type: string = 'view';
|
|
static dimensions = ['x', 'y'];
|
readonly dimensions = ['x', 'y'];
|
|
readonly name: string;
|
|
zoomLimit: {
|
max?: number;
|
min?: number;
|
};
|
|
/**
|
* Represents the transform brought by roam/zoom.
|
* If `View['_viewRect']` applies roam transform,
|
* we can get the final displayed rect.
|
*/
|
private _roamTransformable = new Transformable();
|
/**
|
* Represents the transform from `View['_rect']` to `View['_viewRect']`.
|
*/
|
protected _rawTransformable = new Transformable();
|
private _rawTransform: matrix.MatrixArray;
|
|
/**
|
* This is a user specified point on the source, which will be
|
* located to the center of the `View['_viewRect']`.
|
* The unit this the same as `View['_rect']`.
|
*/
|
private _center: number[];
|
private _zoom: number;
|
|
/**
|
* The rect of the source, where the measure is used by "data" and "center".
|
* Has nothing to do with roam/zoom.
|
* The unit is defined by the source. For example,
|
* for geo source the unit is lat/lng,
|
* for SVG source the unit is the same as the width/height defined in SVG.
|
*/
|
private _rect: BoundingRect;
|
/**
|
* The visible rect on the canvas. Has nothing to do with roam/zoom.
|
* The unit of `View['_viewRect']` is pixel of the canvas.
|
*/
|
private _viewRect: BoundingRect;
|
|
|
constructor(name?: string) {
|
super();
|
this.name = name;
|
}
|
|
setBoundingRect(x: number, y: number, width: number, height: number): BoundingRect {
|
this._rect = new BoundingRect(x, y, width, height);
|
return this._rect;
|
}
|
|
/**
|
* @return {module:zrender/core/BoundingRect}
|
*/
|
getBoundingRect(): BoundingRect {
|
return this._rect;
|
}
|
|
setViewRect(x: number, y: number, width: number, height: number): void {
|
this._transformTo(x, y, width, height);
|
this._viewRect = new BoundingRect(x, y, width, height);
|
}
|
|
/**
|
* Transformed to particular position and size
|
*/
|
protected _transformTo(x: number, y: number, width: number, height: number): void {
|
const rect = this.getBoundingRect();
|
const rawTransform = this._rawTransformable;
|
|
rawTransform.transform = rect.calculateTransform(
|
new BoundingRect(x, y, width, height)
|
);
|
|
const rawParent = rawTransform.parent;
|
rawTransform.parent = null;
|
rawTransform.decomposeTransform();
|
rawTransform.parent = rawParent;
|
|
this._updateTransform();
|
}
|
|
/**
|
* Set center of view
|
*/
|
setCenter(centerCoord?: number[]): void {
|
if (!centerCoord) {
|
return;
|
}
|
this._center = centerCoord;
|
|
this._updateCenterAndZoom();
|
}
|
|
setZoom(zoom: number): void {
|
zoom = zoom || 1;
|
|
const zoomLimit = this.zoomLimit;
|
if (zoomLimit) {
|
if (zoomLimit.max != null) {
|
zoom = Math.min(zoomLimit.max, zoom);
|
}
|
if (zoomLimit.min != null) {
|
zoom = Math.max(zoomLimit.min, zoom);
|
}
|
}
|
this._zoom = zoom;
|
|
this._updateCenterAndZoom();
|
}
|
|
/**
|
* Get default center without roam
|
*/
|
getDefaultCenter(): number[] {
|
// Rect before any transform
|
const rawRect = this.getBoundingRect();
|
const cx = rawRect.x + rawRect.width / 2;
|
const cy = rawRect.y + rawRect.height / 2;
|
|
return [cx, cy];
|
}
|
|
getCenter(): number[] {
|
return this._center || this.getDefaultCenter();
|
}
|
|
getZoom(): number {
|
return this._zoom || 1;
|
}
|
|
getRoamTransform(): matrix.MatrixArray {
|
return this._roamTransformable.getLocalTransform();
|
}
|
|
/**
|
* Remove roam
|
*/
|
private _updateCenterAndZoom(): void {
|
// Must update after view transform updated
|
const rawTransformMatrix = this._rawTransformable.getLocalTransform();
|
const roamTransform = this._roamTransformable;
|
let defaultCenter = this.getDefaultCenter();
|
let center = this.getCenter();
|
const zoom = this.getZoom();
|
|
center = vector.applyTransform([], center, rawTransformMatrix);
|
defaultCenter = vector.applyTransform([], defaultCenter, rawTransformMatrix);
|
|
roamTransform.originX = center[0];
|
roamTransform.originY = center[1];
|
roamTransform.x = defaultCenter[0] - center[0];
|
roamTransform.y = defaultCenter[1] - center[1];
|
roamTransform.scaleX = roamTransform.scaleY = zoom;
|
|
this._updateTransform();
|
}
|
|
/**
|
* Update transform props on `this` based on the current
|
* `this._roamTransformable` and `this._rawTransformable`.
|
*/
|
protected _updateTransform(): void {
|
const roamTransformable = this._roamTransformable;
|
const rawTransformable = this._rawTransformable;
|
|
rawTransformable.parent = roamTransformable;
|
roamTransformable.updateTransform();
|
rawTransformable.updateTransform();
|
|
matrix.copy(this.transform || (this.transform = []), rawTransformable.transform || matrix.create());
|
|
this._rawTransform = rawTransformable.getLocalTransform();
|
|
this.invTransform = this.invTransform || [];
|
matrix.invert(this.invTransform, this.transform);
|
|
this.decomposeTransform();
|
}
|
|
getTransformInfo(): {
|
roam: ViewCoordSysTransformInfoPart
|
raw: ViewCoordSysTransformInfoPart
|
} {
|
const roamTransformable = this._roamTransformable;
|
const rawTransformable = this._rawTransformable;
|
return {
|
roam: {
|
x: roamTransformable.x,
|
y: roamTransformable.y,
|
scaleX: roamTransformable.scaleX,
|
scaleY: roamTransformable.scaleY
|
},
|
raw: {
|
x: rawTransformable.x,
|
y: rawTransformable.y,
|
scaleX: rawTransformable.scaleX,
|
scaleY: rawTransformable.scaleY
|
}
|
};
|
}
|
|
getViewRect(): BoundingRect {
|
return this._viewRect;
|
}
|
|
/**
|
* Get view rect after roam transform
|
*/
|
getViewRectAfterRoam(): BoundingRect {
|
const rect = this.getBoundingRect().clone();
|
rect.applyTransform(this.transform);
|
return rect;
|
}
|
|
/**
|
* Convert a single (lon, lat) data item to (x, y) point.
|
*/
|
dataToPoint(data: number[], noRoam?: boolean, out?: number[]): number[] {
|
const transform = noRoam ? this._rawTransform : this.transform;
|
out = out || [];
|
return transform
|
? v2ApplyTransform(out, data, transform)
|
: vector.copy(out, data);
|
}
|
|
/**
|
* Convert a (x, y) point to (lon, lat) data
|
*/
|
pointToData(point: number[]): number[] {
|
const invTransform = this.invTransform;
|
return invTransform
|
? v2ApplyTransform([], point, invTransform)
|
: [point[0], point[1]];
|
}
|
|
convertToPixel(ecModel: GlobalModel, finder: ParsedModelFinder, value: number[]): number[] {
|
const coordSys = getCoordSys(finder);
|
return coordSys === this ? coordSys.dataToPoint(value) : null;
|
}
|
|
convertFromPixel(ecModel: GlobalModel, finder: ParsedModelFinder, pixel: number[]): number[] {
|
const coordSys = getCoordSys(finder);
|
return coordSys === this ? coordSys.pointToData(pixel) : null;
|
}
|
|
/**
|
* @implements
|
*/
|
containPoint(point: number[]): boolean {
|
return this.getViewRectAfterRoam().contain(point[0], point[1]);
|
}
|
|
/**
|
* @return {number}
|
*/
|
// getScalarScale() {
|
// // Use determinant square root of transform to mutiply scalar
|
// let m = this.transform;
|
// let det = Math.sqrt(Math.abs(m[0] * m[3] - m[2] * m[1]));
|
// return det;
|
// }
|
}
|
|
function getCoordSys(finder: ParsedModelFinderKnown): View {
|
const seriesModel = finder.seriesModel;
|
return seriesModel ? seriesModel.coordinateSystem as View : null; // e.g., graph.
|
}
|
|
export default View;
|