View.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { __extends } from "tslib";
  41. import BoundingRect, { boundingRectApplyTransform, boundingRectCalculateTransform, boundingRectContain, boundingRectCopy, boundingRectCreate } from 'zrender/lib/core/BoundingRect.js';
  42. import Transformable, { copyTransform, transformableCreate, transformableGetLocalTransform } from 'zrender/lib/core/Transformable.js';
  43. import { isPositionSizeOptionPercent, mathAbs, parsePercent } from '../util/number.js';
  44. import { assert, each } from 'zrender/lib/core/util.js';
  45. import { getViewOfComponentOrSeries } from '../core/ExtensionAPI.js';
  46. import { decomposeTransform, payloadDisableAnimation, updateProps, WH, XY } from '../util/graphic.js';
  47. import { invert as matrixInvert, mul as matrixMul, create as matrixCreate, copy as matrixCopy } from 'zrender/lib/core/matrix.js';
  48. import { applyTransform as vectorApplyTransform, copy as vectorCopy, set as vectorSet } from 'zrender/lib/core/vector.js';
  49. /**
  50. * @tutorial [VIEW_COORD_SYS_ANIMATION]
  51. *
  52. * Some VIEW_COORD_SYS may need to support roaming animation, which may be required when the original
  53. * inputs (such as `center/zoom/left/top/right/bottomwidth/height/etc.`) are changed via `setOption`.
  54. * Roaming animation requires strict visual alignment between VIEW_COORD_SYS itself (consider "geo map")
  55. * and its content (e.g., scatter series).
  56. * A logically correct implementation is probably applying animation to VIEW_COORD_SYS's inputs and
  57. * calculate VIEW_COORD_SYS's transformation and lay out its dependent series per frame via
  58. * `dispatchAction`-ish pattern. But this approach require more mechanism to be introduced and careful
  59. * refactoring.
  60. * Instead, we currently still follow the conventional implementation - create animation separately on
  61. * each element (typically via `graphic.updateProps`) . See VIEW_COORD_SYS_ANIMATION_PROPS_CONSTRAINT.
  62. *
  63. * [VIEW_COORD_SYS_ANIMATION_PROPS_CONSTRAINT]:
  64. * The animation should only interpolate `x/y/scaleX/scaleY`, thereby ensuring visual alignment, even
  65. * though `MapDraw` applies animations only on a "VIEW_COORD_SYS_TRANS_ROAM" but scatter series applies
  66. * animation on a "VIEW_COORD_SYS_TRANS_OVERALL".
  67. * But this constraint must always be respected by all relevant components and series.
  68. *
  69. * [VIEW_COORD_SYS_ANIMATION_INTERRUPTION]:
  70. * The animation should be able to be interrupted, such as when users roam it by mouse or touch.
  71. * When interrupted, `center` and `zoom` must be synchronized back to the VIEW_COORD_SYS and host models
  72. * (e.g., geo components or map series). Otherwise, a next `setOption` can cause unexpected result due to
  73. * the outdated `center` and `zoom`.
  74. * But see FIXME_VIEW_COORD_SYS_SYNC_BACK for some existing bugs.
  75. *
  76. * [VIEW_COORD_SYS_UPDATE_FLOW]:
  77. * To avoid bidirectional flow, we consider the original inputs (center/zoom/dataRect/viewRect/etc.) as
  78. * the single source of truth, and always calculate transformations based on that.
  79. * - Roaming interactions ('pan'/'zoom') can trigger an update to the original inputs via
  80. * `dispatchAction({type: 'xxxRoam', ...})`.
  81. * - Roaming animation can synchronize back to the original inputs when interrupted.
  82. * See `syncBackEl`: It is zrender elements that are assigned with VIEW_COORD_SYS_TRANSFORMATION,
  83. * and may be modified outside, typically by animation.
  84. */
  85. /**
  86. * @tutorial [VIEW_COORD_SYS_TRANSFORMATION]
  87. *
  88. * @tutorial [VIEW_COORD_SYS_TRANS_RAW]
  89. * It is the transformation from `ViewInner['dataRect']` to `ViewInner['viewRect']`.
  90. * - ViewInner['dataRect']:
  91. * A rect in a source space (or say, data space).
  92. * The unit is defined by the source. For example,
  93. * - for geo source, the unit defined as lat/lng,
  94. * - for SVG source, the unit is the same as the width/height defined in SVG.
  95. * - for series.graph/series.tree/series.sankey, the unit defined as px directly.
  96. * - ViewInner['viewRect']:
  97. * A rect in the canvas pixel space.
  98. * For example, it can be ec option left/right/top/bottom/width/height of a component or series.
  99. * NOTICE:
  100. * - ViewInner['dataRect'] and ViewInner['viewRect'] affect the behavior when centerOption contains
  101. * absolute values. See VIEW_COORD_SYS_CENTER_ZOOM_DEFINITION .
  102. *
  103. * @tutorial [VIEW_COORD_SYS_TRANS_ROAM]
  104. * It is built with respect to `RoamOptionMixin['center']` and `RoamOptionMixin['zoom']`.
  105. * [VIEW_COORD_SYS_CENTER_ZOOM_DEFINITION]:
  106. * {pxSpace} <-roamTrans- {viewRectSpace} <-rawTrans- {dataRectSpace} <-- ...
  107. * | |
  108. * zoomOption centerOption
  109. * `RoamOptionMixin['center']` is a user specified point on the space of `ViewInner['dataRect']`,
  110. * which will be located to the center of `ViewInner['viewRect']`.
  111. * It can also be a percent value (e.g. '50%'), based on ViewInner['dataRect'].width/height` (since v6).
  112. * Under this definition, users can use '0%' to map the top-left of `ViewInner['dataRect']`
  113. * to the center of `ViewInner['viewRect']`.
  114. * NOTICE:
  115. * - `RoamOptionMixin['center']` is in a linear space regardless of `GeoCommonOptionMixin['projection']`
  116. * - Absolute values in centerOption may hardly have a rigorous meaningful definition if the layout
  117. * is auto-calculated, since users are unlikely to known the exact value of the bounding rect.
  118. * And it is affected by the settings of ViewInner['dataRect'] and ViewInner['viewRect'].
  119. * Therefore, percent centerOption is preferred.
  120. * Use cases:
  121. * geo and map series (`MapDraw`) use this transformation for roamming, since the source
  122. * has been converted with respect to VIEW_COORD_SYS_TRANS_RAW.
  123. *
  124. * @tutorial [VIEW_COORD_SYS_TRANS_OVERALL]
  125. * This is the result of
  126. * `matrix_left_multiply(VIEW_COORD_SYS_TRANS_ROAM, VIEW_COORD_SYS_TRANS_RAW)`.
  127. * Use case:
  128. * sankey/tree/graph series use this transformation for roamming.
  129. *
  130. * @tutorial [VIEW_COORD_SYS_TRANSFORMATION_FORMULA]
  131. * [Basic]:
  132. * {pxSpace} <-roamTrans- {viewRectSpace} <-rawTrans- {dataRectSpace} <-- ...
  133. * pxPoint = roamTrans * rawTrans * dataPoint
  134. * overallTrans = roamTrans * rawTrans
  135. * [e.g., geo case]:
  136. * pxPoint = roamTrans * (rawTrans * (projection * dataPoint))
  137. * Hence: pxPoint = group_syncBackType_ROAM * (rawTrans * (projection * dataPoint))
  138. * Hence: pxPoint = group_syncBackType_ROAM * rawTrans_projection_precalculated_point
  139. * [e.g., graph series case]:
  140. * pxPoint = (roamTrans * rawTrans) * upper_els
  141. * Hence: pxPoint = group_syncBackType_OVERALL * upper_els
  142. * (NOTE: upper_els can have their own transformations)
  143. *
  144. * @see viewCoordSysCopyTrans for fetching transformations.
  145. */
  146. export var VIEW_COORD_SYS_TRANS_RAW = 0;
  147. export var VIEW_COORD_SYS_TRANS_ROAM = 1;
  148. export var VIEW_COORD_SYS_TRANS_OVERALL = 2;
  149. function inner(viewCoordSys) {
  150. return viewCoordSys;
  151. }
  152. export var VIEW_COORD_SYS_TYPE = 'view';
  153. /**
  154. * [VIEW_COORD_SYS]
  155. *
  156. * @final [NOTICE] Inheritance of this class is not recommended. Use composition instead.
  157. */
  158. var View = /** @class */function (_super) {
  159. __extends(View, _super);
  160. function View(invertY, legacyCenterBase, legacyGeo) {
  161. var _this = _super.call(this) || this;
  162. _this.type = VIEW_COORD_SYS_TYPE;
  163. _this.dimensions = ['x', 'y'];
  164. var viewInner = inner(_this);
  165. viewInner.invertY = invertY;
  166. viewInner.lgCt = legacyCenterBase;
  167. viewInner.lgGeo = legacyGeo;
  168. var trans = viewInner.trans = [];
  169. trans[VIEW_COORD_SYS_TRANS_RAW] = transformableCreate();
  170. trans[VIEW_COORD_SYS_TRANS_ROAM] = transformableCreate();
  171. trans[VIEW_COORD_SYS_TRANS_OVERALL] = transformableCreate();
  172. viewInner.mtRaw = matrixCreate();
  173. viewInner.mtRawInv = matrixCreate();
  174. viewInner.mtOverall = matrixCreate();
  175. viewInner.mtOverallInv = matrixCreate();
  176. viewInner.zoom = 1;
  177. return _this;
  178. }
  179. /**
  180. * @implements CoordinateSystem['getBoundingRect']
  181. * @see VIEW_COORD_SYS_TRANS_RAW
  182. *
  183. * This is a rect in data space.
  184. * For historicall reason, the name is `getBoundingRect` - preserve it for backward compatibility.
  185. */
  186. View.prototype.getBoundingRect = function () {
  187. return viewCoordSysCopyBoundingRect(null, this);
  188. };
  189. /**
  190. * @implements CoordinateSystem['getViewRect']
  191. * @see VIEW_COORD_SYS_TRANS_RAW
  192. */
  193. View.prototype.getViewRect = function () {
  194. return viewCoordSysCopyViewRect(null, this);
  195. };
  196. /**
  197. * @implements CoordinateSystem['getRoamTransform']
  198. */
  199. View.prototype.getRoamTransform = function () {
  200. return transformableGetLocalTransform(inner(this).trans[VIEW_COORD_SYS_TRANS_ROAM]);
  201. };
  202. View.prototype.dataToPoint = function (data, noRoam, out) {
  203. var transform = noRoam ? inner(this).mtRaw : inner(this).mtOverall;
  204. out = out || [];
  205. return transform ? vectorApplyTransform(out, data, transform) : vectorCopy(out, data);
  206. };
  207. View.prototype.pointToData = function (point, reserved, out) {
  208. out = out || [];
  209. var invTransform = inner(this).mtOverallInv;
  210. return invTransform ? vectorApplyTransform(out, point, invTransform) : vectorCopy(out, point);
  211. };
  212. View.prototype.convertToPixel = function (ecModel, finder, value) {
  213. var coordSys = getCoordSys(finder);
  214. return coordSys === this ? coordSys.dataToPoint(value) : null;
  215. };
  216. View.prototype.convertFromPixel = function (ecModel, finder, pixel) {
  217. var coordSys = getCoordSys(finder);
  218. return coordSys === this ? coordSys.pointToData(pixel) : null;
  219. };
  220. View.prototype.containPoint = function (point) {
  221. var viewInner = inner(this);
  222. boundingRectCopy(tmpPixelRectForContain, viewInner.dataRect);
  223. boundingRectApplyTransform(tmpPixelRectForContain, tmpPixelRectForContain, viewInner.mtOverall);
  224. return boundingRectContain(tmpPixelRectForContain, point[0], point[1]);
  225. };
  226. View.dimensions = ['x', 'y'];
  227. return View;
  228. }(Transformable);
  229. var tmpPixelRectForContain = boundingRectCreate();
  230. export function viewCoordSysCopyOverallMatrix(out, viewCoordSys) {
  231. return matrixCopy(out || [], inner(viewCoordSys).mtOverall);
  232. }
  233. export function viewCoordSysGetZoomOption(viewCoordSys) {
  234. return inner(viewCoordSys).zoom;
  235. }
  236. export function viewCoordSysCopyBoundingRect(out, viewCoordSys) {
  237. return boundingRectCopy(out || boundingRectCreate(), inner(viewCoordSys).dataRect);
  238. }
  239. export function viewCoordSysCopyViewRect(out, viewCoordSys) {
  240. return boundingRectCopy(out || boundingRectCreate(), inner(viewCoordSys).viewRect);
  241. }
  242. export function viewCoordSysCopyTrans(out, viewCoordSys, transKind
  243. // @return The input `out` or create an object.
  244. ) {
  245. return copyTransform(out || transformableCreate(), inner(viewCoordSys).trans[transKind]);
  246. }
  247. function viewCoordSysIsInputReady(viewInner) {
  248. return !!(viewInner.dataRect && viewInner.viewRect);
  249. // NOTE: Other parameters has default values. Only parameters
  250. // above are required to be input.
  251. }
  252. function calcOverallTransFromSyncBackEl(out, viewInner, syncBackEl, syncBackType) {
  253. if (syncBackType === VIEW_COORD_SYS_TRANS_ROAM) {
  254. calcOverallTrans(out, viewInner.trans[VIEW_COORD_SYS_TRANS_RAW], syncBackEl);
  255. } else {
  256. copyTransform(out, syncBackEl);
  257. }
  258. }
  259. function calcRoamTransFromOverallTrans(out, viewInner, overallTrans) {
  260. // Convert overallTrans to roamTrans. If update animation is applied to overallTrans,
  261. // we assume that rawTrans is changed instantly, and roamTrans takes that animation.
  262. // Formula:
  263. // overallTrans = roamTrans * rawTrans
  264. // overallTrans * invert(rawTrans) = roamTrans
  265. transformableGetLocalTransform(overallTrans, tmpMtRTO);
  266. matrixMul(tmpMtRTO, tmpMtRTO, viewInner.mtRawInv);
  267. decomposeTransform(out, tmpMtRTO);
  268. }
  269. var tmpMtRTO = matrixCreate();
  270. /**
  271. * [NOTICE]
  272. * The definition of this center has always been irrelevant to some other series center like
  273. * 'series-pie.center' - this center is a point in the space of `ViewInner['dataRect'].x/y`,
  274. * rather than canvas viewport, and the unit is not necessarily pixel (e.g., in geo case).
  275. */
  276. export function viewCoordSysSetRoamOptionFromModel(viewCoordSys, hostModel) {
  277. var viewInner = inner(viewCoordSys);
  278. viewInner.centerOption = hostModel.getShallow('center');
  279. var zoomLimit = viewInner.zoomLimit = hostModel.getShallow('scaleLimit');
  280. var zoomOption = hostModel.getShallow('zoom');
  281. viewInner.zoom = clampByZoomLimit(zoomOption || 1, zoomLimit) || 1;
  282. if (viewCoordSysIsInputReady(viewInner)) {
  283. viewCoordSysUpdateTransform(viewInner);
  284. }
  285. }
  286. /**
  287. * @see ViewInner['dataRect']
  288. */
  289. export function viewCoordSysSetBoundingRect(viewCoordSys, x, y, width, height) {
  290. var viewInner = inner(viewCoordSys);
  291. viewInner.dataRect = new BoundingRect(x, y, width, height);
  292. if (viewCoordSysIsInputReady(viewInner)) {
  293. viewCoordSysUpdateTransform(viewInner);
  294. }
  295. }
  296. /**
  297. * @see ViewInner['viewRect']
  298. */
  299. export function viewCoordSysSetViewRect(viewCoordSys, x, y, width, height) {
  300. var viewInner = inner(viewCoordSys);
  301. viewInner.viewRect = new BoundingRect(x, y, width, height);
  302. if (viewCoordSysIsInputReady(viewInner)) {
  303. viewCoordSysUpdateTransform(viewInner);
  304. }
  305. }
  306. function viewCoordSysUpdateTransform(viewInner) {
  307. // The order matters.
  308. viewCoordSysUpdateRawTrans(viewInner);
  309. viewCoordSysUpdateRoamTrans(viewInner);
  310. viewCoordSysUpdateOverallTrans(viewInner);
  311. }
  312. function viewCoordSysUpdateRawTrans(viewInner) {
  313. var dataRect = viewInner.dataRect;
  314. var viewRect = viewInner.viewRect;
  315. var rawTrans = viewInner.trans[VIEW_COORD_SYS_TRANS_RAW];
  316. var invertY = viewInner.invertY;
  317. if (invertY) {
  318. dataRect = boundingRectCopy(tmpRectURT, dataRect);
  319. dataRect.y = -dataRect.y - dataRect.height;
  320. }
  321. boundingRectCalculateTransform(tmpMtURT, dataRect, viewRect);
  322. decomposeTransform(rawTrans, tmpMtURT);
  323. if (invertY) {
  324. rawTrans.scaleY = -rawTrans.scaleY;
  325. }
  326. var mtRaw = transformableGetLocalTransform(rawTrans, viewInner.mtRaw);
  327. matrixInvert(viewInner.mtRawInv, mtRaw);
  328. }
  329. var tmpMtURT = matrixCreate();
  330. var tmpRectURT = boundingRectCreate();
  331. /**
  332. * NOTICE: It depends on `viewCoordSysUpdateRawTrans`.
  333. */
  334. function viewCoordSysUpdateRoamTrans(viewInner) {
  335. var viewRectCenter = viewCoordSysGetViewRectCenter(viewInner);
  336. var roamViewCenter = parseCenterOption(tmpCenterURT, viewInner, viewInner.centerOption) ? vectorApplyTransform(tmpCenterURT, tmpCenterURT, viewInner.mtRaw) : viewRectCenter;
  337. var zoom = viewInner.zoom;
  338. var roamTrans = viewInner.trans[VIEW_COORD_SYS_TRANS_ROAM];
  339. roamTrans.x = viewRectCenter[0] - zoom * roamViewCenter[0];
  340. roamTrans.y = viewRectCenter[1] - zoom * roamViewCenter[1];
  341. roamTrans.scaleX = roamTrans.scaleY = zoom;
  342. // [VIEW_COORD_SYS_APPLY_ROAM_CENTER_AND_ZOOM]
  343. // Ordinarily, the definition is:
  344. // roamTrans.originX = roamViewCenter[0];
  345. // roamTrans.originY = roamViewCenter[1];
  346. // roamTrans.x = viewRectCenter[0] - roamViewCenter[0];
  347. // roamTrans.y = viewRectCenter[1] - roamViewCenter[1];
  348. // roamTrans.scaleX = roamTrans.scaleY = zoom;
  349. // But `el.originX`/`originY` should not be set.
  350. // (see VIEW_COORD_SYS_ANIMATION_PROPS_CONSTRAINT for the reason),
  351. // so we use the above formula instead.
  352. }
  353. var tmpCenterURT = [];
  354. /**
  355. * NOTICE: It depends on `viewCoordSysUpdateRoamTrans` and `viewCoordSysUpdateRawTrans`
  356. */
  357. function viewCoordSysUpdateOverallTrans(viewInner) {
  358. var trans = viewInner.trans;
  359. var roamTrans = trans[VIEW_COORD_SYS_TRANS_ROAM];
  360. var rawTrans = trans[VIEW_COORD_SYS_TRANS_RAW];
  361. var overallTrans = trans[VIEW_COORD_SYS_TRANS_OVERALL];
  362. calcOverallTrans(overallTrans, rawTrans, roamTrans);
  363. var mtOverall = transformableGetLocalTransform(overallTrans, viewInner.mtOverall);
  364. var mtOverallInv = matrixInvert(viewInner.mtOverallInv, mtOverall);
  365. legacyCopyOverallTrans(viewInner, overallTrans, mtOverall, mtOverallInv);
  366. legacyCopyOverallTrans(viewInner.lgGeo, overallTrans, mtOverall, mtOverallInv);
  367. }
  368. /**
  369. * [VIEW_COORD_SYS_TRANS_OVERALL_BACKWARD_COMPATIBILITY]
  370. * VIEW_COORD_SYS_TRANS_OVERALL transformable has long been View or Geo instance itself.
  371. * We keep backward compatibility, since some users may have visited it directly (e.g.
  372. * for drawing a computed bounding rect).
  373. */
  374. function legacyCopyOverallTrans(target, overallTrans, mtOverall, mtOverallInv) {
  375. if (target) {
  376. copyTransform(target, overallTrans);
  377. matrixCopy(target.transform || (target.transform = []), mtOverall);
  378. matrixCopy(target.invTransform || (target.invTransform = []), mtOverallInv);
  379. }
  380. }
  381. function calcOverallTrans(out, rawTrans, roamTrans) {
  382. transformableGetLocalTransform(rawTrans, tmpMtCOT1);
  383. transformableGetLocalTransform(roamTrans, tmpMtCOT2);
  384. matrixMul(tmpMtCOT2, tmpMtCOT2, tmpMtCOT1);
  385. decomposeTransform(out, tmpMtCOT2);
  386. }
  387. var tmpMtCOT1 = matrixCreate();
  388. var tmpMtCOT2 = matrixCreate();
  389. function getCoordSys(finder) {
  390. var seriesModel = finder.seriesModel;
  391. return seriesModel ? seriesModel.coordinateSystem : null; // e.g., graph.
  392. }
  393. function viewCoordSysGetViewRectCenter(viewInner) {
  394. var viewRect = viewInner.viewRect;
  395. tmpViewRectCenter[0] = viewRect.x + viewRect.width / 2;
  396. tmpViewRectCenter[1] = viewRect.y + viewRect.height / 2;
  397. return tmpViewRectCenter;
  398. }
  399. var tmpViewRectCenter = [];
  400. export function isViewCoordSys(coordSys) {
  401. return coordSys && coordSys.type === 'view';
  402. }
  403. /**
  404. * NOTICE:
  405. * - `syncBackEl` should be in the pixel space without any other transformation
  406. * in its accesters, otherwise the roaming may incorrect.
  407. * - `syncBackEl` can be a `Group`, having its own descendants and transformation.
  408. * But in this case, `dataToPoint` can only reach the space of `syncBackEl` itself.
  409. */
  410. export function applyViewCoordSysTransToElement(syncBackEl, syncBackType, viewCoordSys,
  411. // If NullUndefined, no animation.
  412. // Typically, there should be no animation in the first render and in `__updateOnOwnRoam`.
  413. animatableModel) {
  414. var viewInner = inner(viewCoordSys);
  415. viewInner.syncBackEl = syncBackEl;
  416. viewInner.syncBackType = syncBackType;
  417. if (!animatableModel) {
  418. viewCoordSysCopyTrans(syncBackEl, viewCoordSys, syncBackType);
  419. syncBackEl.dirty();
  420. } else {
  421. updateProps(syncBackEl, viewCoordSysCopyTrans(null, viewCoordSys, syncBackType), animatableModel);
  422. }
  423. }
  424. /**
  425. * FIXME: [FIXME_VIEW_COORD_SYS_SYNC_BACK]
  426. * Currently the "sync back" is only performed on roam action. But these cases are not
  427. * covered:
  428. * 1. Consider both center change and series data adding is requested via `setOption`
  429. * and animation is not finished, the new added series symbols should lay out based
  430. * on the "intermediate state" rather than the "final state" of the VIEW_COORD_SYS,
  431. * otherwise, visual artifacts can arise.
  432. * 2. See the case of FIXME_SYMBOL_CLIP_CONSIDERING_COORD_SYS_ALIGNMENT_DURING_ANIMATION .
  433. */
  434. function viewCoordSysSyncBack(viewCoordSys, hostModel, otherModelsToSync, payload) {
  435. var viewInner = inner(viewCoordSys);
  436. var syncBackEl = viewInner.syncBackEl;
  437. if (syncBackEl) {
  438. if (process.env.NODE_ENV !== 'production') {
  439. assert(viewInner.syncBackType != null);
  440. }
  441. // @see VIEW_COORD_SYS_ANIMATION_INTERRUPTION
  442. syncBackEl.stopAnimation();
  443. // syncBackEl may have been changed regardless of animation,
  444. // therefore, need to sync back.
  445. calcOverallTransFromSyncBackEl(tmpTransSB1, viewInner, syncBackEl, viewInner.syncBackType);
  446. } else {
  447. copyTransform(tmpTransSB1, viewInner.trans[VIEW_COORD_SYS_TRANS_OVERALL]);
  448. }
  449. // Sync back to model.
  450. calcRoamTransFromOverallTrans(tmpTransSB2, viewInner, tmpTransSB1);
  451. payload ? applyRoamPayloadToOverallTrans(tmpTransSB1, tmpTransSB2, viewInner, payload) : copyTransform(tmpTransSB1, tmpTransSB2);
  452. calcRoamTransFromOverallTrans(tmpTransSB1, viewInner, tmpTransSB1);
  453. syncBackToRoamOptionFromRoamTrans(viewInner, hostModel, otherModelsToSync, tmpTransSB1);
  454. }
  455. var tmpTransSB1 = transformableCreate();
  456. var tmpTransSB2 = transformableCreate();
  457. /**
  458. * Invert to `center` and `zoom` of VIEW_COORD_SYS and host models based on
  459. * - `syncBackEl` or VIEW_COORD_SYS_TRANS_OVERALL
  460. * - Delta in `payload`.
  461. *
  462. * Should be only called in action handlers.
  463. *
  464. * @see RoamHostView['__updateOnOwnRoam']
  465. */
  466. export function ownRoamModelCoordSysUpdateInAction(payload, hostModel,
  467. // This is only used for MAP_SERIES_GROUP, where the change need to sync to all series,
  468. // otherwise when legend filter some series and the other series take the responsibility
  469. // to draw map, or roam then legend restored, the result will be incorrect.
  470. otherModelsToSync) {
  471. // NOTE: VIEW_COORD_SYS instances are depended on by this action handler.
  472. // Under the current design, actions should not be triggered before `setOption`
  473. // being called, therefore coord sys instances always exist.
  474. var viewCoordSys = getOwnRoamViewCoordSys(hostModel);
  475. if (viewCoordSys) {
  476. // Sync back to model.
  477. viewCoordSysSyncBack(viewCoordSys, hostModel, otherModelsToSync, payload);
  478. // Recalculate from model.
  479. viewCoordSysSetRoamOptionFromModel(viewCoordSys, hostModel);
  480. }
  481. }
  482. export function getOwnRoamViewCoordSys(hostModel) {
  483. return hostModel.__ownRoamView ? hostModel.__ownRoamView() : null;
  484. }
  485. export function ownRoamViewUpdateDirectlyInAction(payload, componentOrSeries, ecModel, api) {
  486. // Tricky: disable animation in `updateProps` of `graphic.ts`.
  487. ecModel.setUpdatePayload(payloadDisableAnimation(payload));
  488. var componentOrSeriesView = getViewOfComponentOrSeries(api, componentOrSeries);
  489. if (componentOrSeriesView && componentOrSeriesView.__updateOnOwnRoam) {
  490. componentOrSeriesView.__updateOnOwnRoam(payload, componentOrSeries, api);
  491. }
  492. }
  493. function applyRoamPayloadToOverallTrans(targetOverallTrans, roamTrans, viewInner, payload) {
  494. // NOTE: payload.dx/dy should always applied to pixel space, i.e., "overallTrans".
  495. if (payload.dx != null && payload.dy != null) {
  496. targetOverallTrans.x += payload.dx;
  497. targetOverallTrans.y += payload.dy;
  498. }
  499. var deltaZoom = payload.zoom;
  500. if (deltaZoom != null) {
  501. // Although `zoomOption` is defined on roamTrans
  502. var oldZoom = getZoomFromRoamTrans(roamTrans);
  503. var newZoom = clampByZoomLimit(oldZoom * deltaZoom, viewInner.zoomLimit);
  504. var deltaZoom2 = newZoom / oldZoom;
  505. // Keep the mouse center when scaling.
  506. targetOverallTrans.x -= (payload.originX - targetOverallTrans.x) * (deltaZoom2 - 1);
  507. targetOverallTrans.y -= (payload.originY - targetOverallTrans.y) * (deltaZoom2 - 1);
  508. // Although `zoomOption` is defined on roamTrans,
  509. // deltaZoom can be applied to overallTrans directly.
  510. targetOverallTrans.scaleX *= deltaZoom2;
  511. targetOverallTrans.scaleY *= deltaZoom2;
  512. }
  513. }
  514. function getZoomFromRoamTrans(trans) {
  515. if (process.env.NODE_ENV !== 'production') {
  516. // scaleX and scaleY should be the same even during animation.
  517. // See VIEW_COORD_SYS_APPLY_ROAM_CENTER_AND_ZOOM .
  518. assert(mathAbs(trans.scaleX - trans.scaleY) < 1e-5);
  519. }
  520. return trans.scaleX;
  521. }
  522. function parseCenterOption(
  523. // `out` is a center in `dataRect` space.
  524. out, viewInner, centerOption
  525. // @return Whether a valid center is obtained.
  526. ) {
  527. if (process.env.NODE_ENV !== 'production') {
  528. assert(viewCoordSysIsInputReady(viewInner));
  529. }
  530. var dataRect = viewInner.dataRect;
  531. if (!centerOption) {
  532. return false;
  533. }
  534. // #16904 introduced percentage string here, such as '33%'. But it was based on canvas
  535. // width/height, which is not reasonable - the unit may incorrect, and it is unpredictable if
  536. // the `ViewInner['dataRect']` is not calculated based on the current canvas rect. Therefore the percentage
  537. // value is changed to based on `ViewInner['dataRect'].width/height` since v6. Under this definition, users
  538. // can use '0%' to map the top-left of `ViewInner['dataRect']` to the center of `ViewInner['viewRect']`.
  539. var lgCt = viewInner.lgCt;
  540. if (lgCt) {
  541. vectorSet(out, parsePercent(centerOption[0], lgCt.w), parsePercent(centerOption[1], lgCt.h));
  542. } else if (dataRect) {
  543. vectorSet(out, parsePercent(centerOption[0], dataRect.width, dataRect.x), parsePercent(centerOption[1], dataRect.height, dataRect.y));
  544. }
  545. return true;
  546. }
  547. /**
  548. * An inverse operation to `parseCenterOption`.
  549. * Mainly for percentage center option.
  550. */
  551. function invertBackToCenterOption(viewInner, center) {
  552. var lastCenterOption = viewInner.centerOption;
  553. var dataRect = viewInner.dataRect;
  554. if (process.env.NODE_ENV !== 'production') {
  555. assert(center && dataRect);
  556. }
  557. return !lastCenterOption || viewInner.lgCt ? center.slice() : [invertToPercentPerCenterDim(0, center, lastCenterOption, dataRect), invertToPercentPerCenterDim(1, center, lastCenterOption, dataRect)];
  558. }
  559. function invertToPercentPerCenterDim(dimIdx, center, lastCenterOption, dataRect) {
  560. return lastCenterOption && dataRect && dataRect[WH[dimIdx]] && isPositionSizeOptionPercent(lastCenterOption[dimIdx]) ? (center[dimIdx] - dataRect[XY[dimIdx]]) / dataRect[WH[dimIdx]] * 100 + '%' : center[dimIdx];
  561. }
  562. export function useLegacyViewCoordSysCenterBase(ecModel, api) {
  563. return api && ecModel && ecModel.getShallow('legacyViewCoordSysCenterBase') ? {
  564. w: api.getWidth(),
  565. h: api.getHeight()
  566. } : null;
  567. }
  568. /**
  569. * @see VIEW_COORD_SYS_ANIMATION_INTERRUPTION
  570. */
  571. function syncBackToRoamOptionFromRoamTrans(viewInner, hostModel, otherModelsToSync, roamTrans) {
  572. // This is the inverse operation of `viewCoordSysSetRoamOptionFromModel`.
  573. var viewRectCenter = viewCoordSysGetViewRectCenter(viewInner);
  574. var zoom = getZoomFromRoamTrans(roamTrans);
  575. var notZoomNearZero = mathAbs(zoom) > 1e-6;
  576. tmpCenterITR[0] = notZoomNearZero ? (viewRectCenter[0] - roamTrans.x) / zoom : viewRectCenter[0]; // Unlikely to occur.
  577. tmpCenterITR[1] = notZoomNearZero ? (viewRectCenter[1] - roamTrans.y) / zoom : viewRectCenter[1]; // Unlikely to occur.
  578. vectorApplyTransform(tmpCenterITR, tmpCenterITR, viewInner.mtRawInv);
  579. var centerOption = invertBackToCenterOption(viewInner, tmpCenterITR);
  580. syncBackRoamOptionToRoamHostModel(hostModel, centerOption, zoom);
  581. each(otherModelsToSync, function (otherModel) {
  582. if (otherModel !== hostModel) {
  583. syncBackRoamOptionToRoamHostModel(otherModel, centerOption.slice(), zoom);
  584. }
  585. });
  586. }
  587. var tmpCenterITR = [];
  588. /**
  589. * Models should be updated, otherwise consequent `setOption()` can cause outdated `center`
  590. * and `zoom` to be used.
  591. */
  592. function syncBackRoamOptionToRoamHostModel(hostModel, center, zoom) {
  593. var option = hostModel.option;
  594. option.center = center;
  595. option.zoom = zoom;
  596. }
  597. export function clampByZoomLimit(zoom, zoomLimit) {
  598. if (zoomLimit) {
  599. var zoomMin = zoomLimit.min || 0;
  600. var zoomMax = zoomLimit.max || Infinity;
  601. zoom = Math.max(Math.min(zoomMax, zoom), zoomMin);
  602. }
  603. return zoom;
  604. }
  605. export function calcCompensationScaleToPreserveNodeSize(viewCoordSys, model) {
  606. var nodeScaleRatio = model.getShallow('nodeScaleRatio', true) || 1;
  607. var viewInner = inner(viewCoordSys);
  608. // Scale node when zoom changes
  609. return ((viewInner.zoom - 1) * nodeScaleRatio + 1) / (viewInner.trans[VIEW_COORD_SYS_TRANS_OVERALL].scaleX || 1);
  610. }
  611. export default View;