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. | |
| */ | |
| /** | |
| * 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'; | |
| import { parsePercent } from '../util/number'; | |
| import type ExtensionAPI from '../core/ExtensionAPI'; | |
| 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 | string)[], api: ExtensionAPI): void { | |
| if (!centerCoord) { | |
| return; | |
| } | |
| this._center = [ | |
| parsePercent(centerCoord[0], api.getWidth()), | |
| parsePercent(centerCoord[1], api.getHeight()) | |
| ]; | |
| 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 rawTransformable = this._rawTransformable; | |
| const roamTransformable = this._roamTransformable; | |
| // Because roamTransformabel has `originX/originY` modified, | |
| // but the caller of `getTransformInfo` can not handle `originX/originY`, | |
| // so need to recalculate them. | |
| const dummyTransformable = new Transformable(); | |
| dummyTransformable.transform = roamTransformable.transform; | |
| dummyTransformable.decomposeTransform(); | |
| return { | |
| roam: { | |
| x: dummyTransformable.x, | |
| y: dummyTransformable.y, | |
| scaleX: dummyTransformable.scaleX, | |
| scaleY: dummyTransformable.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 multiply 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; | |