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