Grid.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  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. /**
  41. * Grid is a region which contains at most 4 cartesian systems
  42. *
  43. * TODO Default cartesian
  44. */
  45. import { isObject, each, indexOf, retrieve3, keys, assert, eqNaN, find, retrieve2 } from 'zrender/lib/core/util.js';
  46. import { createBoxLayoutReference, getLayoutRect } from '../../util/layout.js';
  47. import { createScaleByModel, ifAxisCrossZero, niceScaleExtent, getDataDimensionsOnAxis, isNameLocationCenter, shouldAxisShow } from '../../coord/axisHelper.js';
  48. import Cartesian2D, { cartesian2DDimensions } from './Cartesian2D.js';
  49. import Axis2D from './Axis2D.js';
  50. import { SINGLE_REFERRING } from '../../util/model.js';
  51. // Depends on GridModel, AxisModel, which performs preprocess.
  52. import { OUTER_BOUNDS_CLAMP_DEFAULT, OUTER_BOUNDS_DEFAULT } from './GridModel.js';
  53. import { findAxisModels, createCartesianAxisViewCommonPartBuilder, updateCartesianAxisViewCommonPartBuilder, isCartesian2DInjectedAsDataCoordSys } from './cartesianAxisHelper.js';
  54. import { isIntervalOrLogScale } from '../../scale/helper.js';
  55. import { alignScaleTicks } from '../axisAlignTicks.js';
  56. import { expandOrShrinkRect, WH, XY } from '../../util/graphic.js';
  57. import { AxisBuilderSharedContext, resolveAxisNameOverlapDefault, moveIfOverlapByLinearLabels, getLabelInner } from '../../component/axis/AxisBuilder.js';
  58. import { error, log } from '../../util/log.js';
  59. import { AxisTickLabelComputingKind } from '../axisTickLabelBuilder.js';
  60. import { injectCoordSysByOption } from '../../core/CoordinateSystem.js';
  61. import { mathMax, parsePositionSizeOption } from '../../util/number.js';
  62. // margin is [top, right, bottom, left]
  63. var XY_TO_MARGIN_IDX = [[3, 1], [0, 2] // xyIdx 1 => 'y'
  64. ];
  65. var Grid = /** @class */function () {
  66. function Grid(gridModel, ecModel, api) {
  67. // FIXME:TS where used (different from registered type 'cartesian2d')?
  68. this.type = 'grid';
  69. this._coordsMap = {};
  70. this._coordsList = [];
  71. this._axesMap = {};
  72. this._axesList = [];
  73. this.axisPointerEnabled = true;
  74. this.dimensions = cartesian2DDimensions;
  75. this._initCartesian(gridModel, ecModel, api);
  76. this.model = gridModel;
  77. }
  78. Grid.prototype.getRect = function () {
  79. return this._rect;
  80. };
  81. Grid.prototype.update = function (ecModel, api) {
  82. var axesMap = this._axesMap;
  83. this._updateScale(ecModel, this.model);
  84. function updateAxisTicks(axes) {
  85. var alignTo;
  86. // Axis is added in order of axisIndex.
  87. var axesIndices = keys(axes);
  88. var len = axesIndices.length;
  89. if (!len) {
  90. return;
  91. }
  92. var axisNeedsAlign = [];
  93. // Process once and calculate the ticks for those don't use alignTicks.
  94. for (var i = len - 1; i >= 0; i--) {
  95. var idx = +axesIndices[i]; // Convert to number.
  96. var axis = axes[idx];
  97. var model = axis.model;
  98. var scale = axis.scale;
  99. if (
  100. // Only value and log axis without interval support alignTicks.
  101. isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) {
  102. axisNeedsAlign.push(axis);
  103. } else {
  104. niceScaleExtent(scale, model);
  105. if (isIntervalOrLogScale(scale)) {
  106. // Can only align to interval or log axis.
  107. alignTo = axis;
  108. }
  109. }
  110. }
  111. ;
  112. // All axes has set alignTicks. Pick the first one.
  113. // PENDING. Should we find the axis that both set interval, min, max and align to this one?
  114. if (axisNeedsAlign.length) {
  115. if (!alignTo) {
  116. alignTo = axisNeedsAlign.pop();
  117. niceScaleExtent(alignTo.scale, alignTo.model);
  118. }
  119. each(axisNeedsAlign, function (axis) {
  120. alignScaleTicks(axis.scale, axis.model, alignTo.scale);
  121. });
  122. }
  123. }
  124. updateAxisTicks(axesMap.x);
  125. updateAxisTicks(axesMap.y);
  126. // Key: axisDim_axisIndex, value: boolean, whether onZero target.
  127. var onZeroRecords = {};
  128. each(axesMap.x, function (xAxis) {
  129. fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords);
  130. });
  131. each(axesMap.y, function (yAxis) {
  132. fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords);
  133. });
  134. // Resize again if containLabel is enabled
  135. // FIXME It may cause getting wrong grid size in data processing stage
  136. this.resize(this.model, api);
  137. };
  138. /**
  139. * Resize the grid.
  140. *
  141. * [NOTE]
  142. * If both "grid.containLabel/grid.contain" and pixel-required-data-processing (such as, "dataSampling")
  143. * exist, circular dependency occurs in logic.
  144. * The final compromised sequence is:
  145. * 1. Calculate "axis.extent" (pixel extent) and AffineTransform based on only "grid layout options".
  146. * Not accurate if "grid.containLabel/grid.contain" is required, but it is a compromise to avoid
  147. * circular dependency.
  148. * 2. Perform "series data processing" (where "dataSampling" requires "axis.extent").
  149. * 3. Calculate "scale.extent" (data extent) based on "processed series data".
  150. * 4. Modify "axis.extent" for "grid.containLabel/grid.contain":
  151. * 4.1. Calculate "axis labels" based on "scale.extent".
  152. * 4.2. Modify "axis.extent" by the bounding rects of "axis labels and names".
  153. */
  154. Grid.prototype.resize = function (gridModel, api, beforeDataProcessing) {
  155. var layoutRef = createBoxLayoutReference(gridModel, api);
  156. var gridRect = this._rect = getLayoutRect(gridModel.getBoxLayoutParams(), layoutRef.refContainer);
  157. // PENDING: whether to support that if the input `coord` is out of the base coord sys,
  158. // do not render anything. At present, the behavior is undefined.
  159. var axesMap = this._axesMap;
  160. var coordsList = this._coordsList;
  161. var optionContainLabel = gridModel.get('containLabel'); // No `.get(, true)` for backward compat.
  162. updateAllAxisExtentTransByGridRect(axesMap, gridRect);
  163. if (!beforeDataProcessing) {
  164. var axisBuilderSharedCtx = createAxisBiulders(gridRect, coordsList, axesMap, optionContainLabel, api);
  165. var noPxChange = void 0;
  166. if (optionContainLabel) {
  167. if (legacyLayOutGridByContainLabel) {
  168. // console.time('legacyLayOutGridByContainLabel');
  169. legacyLayOutGridByContainLabel(this._axesList, gridRect);
  170. updateAllAxisExtentTransByGridRect(axesMap, gridRect);
  171. // console.timeEnd('legacyLayOutGridByContainLabel');
  172. } else {
  173. if (process.env.NODE_ENV !== 'production') {
  174. log('Specified `grid.containLabel` but no `use(LegacyGridContainLabel)`;' + 'use `grid.outerBounds` instead.', true);
  175. }
  176. noPxChange = layOutGridByOuterBounds(gridRect.clone(), 'axisLabel', null, gridRect, axesMap, axisBuilderSharedCtx, layoutRef);
  177. }
  178. } else {
  179. var _a = prepareOuterBounds(gridModel, gridRect, layoutRef),
  180. outerBoundsRect = _a.outerBoundsRect,
  181. parsedOuterBoundsContain = _a.parsedOuterBoundsContain,
  182. outerBoundsClamp = _a.outerBoundsClamp;
  183. if (outerBoundsRect) {
  184. // console.time('layOutGridByOuterBounds');
  185. noPxChange = layOutGridByOuterBounds(outerBoundsRect, parsedOuterBoundsContain, outerBoundsClamp, gridRect, axesMap, axisBuilderSharedCtx, layoutRef);
  186. // console.timeEnd('layOutGridByOuterBounds');
  187. }
  188. }
  189. // console.time('buildAxesView_determine');
  190. createOrUpdateAxesView(gridRect, axesMap, AxisTickLabelComputingKind.determine, null, noPxChange, layoutRef);
  191. // console.timeEnd('buildAxesView_determine');
  192. } // End of beforeDataProcessing
  193. each(this._coordsList, function (coord) {
  194. // Calculate affine matrix to accelerate the data to point transform.
  195. // If all the axes scales are time or value.
  196. coord.calcAffineTransform();
  197. });
  198. };
  199. Grid.prototype.getAxis = function (dim, axisIndex) {
  200. var axesMapOnDim = this._axesMap[dim];
  201. if (axesMapOnDim != null) {
  202. return axesMapOnDim[axisIndex || 0];
  203. }
  204. };
  205. Grid.prototype.getAxes = function () {
  206. return this._axesList.slice();
  207. };
  208. Grid.prototype.getCartesian = function (xAxisIndex, yAxisIndex) {
  209. if (xAxisIndex != null && yAxisIndex != null) {
  210. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  211. return this._coordsMap[key];
  212. }
  213. if (isObject(xAxisIndex)) {
  214. yAxisIndex = xAxisIndex.yAxisIndex;
  215. xAxisIndex = xAxisIndex.xAxisIndex;
  216. }
  217. for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {
  218. if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) {
  219. return coordList[i];
  220. }
  221. }
  222. };
  223. Grid.prototype.getCartesians = function () {
  224. return this._coordsList.slice();
  225. };
  226. /**
  227. * @implements
  228. */
  229. Grid.prototype.convertToPixel = function (ecModel, finder, value) {
  230. var target = this._findConvertTarget(finder);
  231. return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null;
  232. };
  233. /**
  234. * @implements
  235. */
  236. Grid.prototype.convertFromPixel = function (ecModel, finder, value) {
  237. var target = this._findConvertTarget(finder);
  238. return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null;
  239. };
  240. Grid.prototype._findConvertTarget = function (finder) {
  241. var seriesModel = finder.seriesModel;
  242. var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];
  243. var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];
  244. var gridModel = finder.gridModel;
  245. var coordsList = this._coordsList;
  246. var cartesian;
  247. var axis;
  248. if (seriesModel) {
  249. cartesian = seriesModel.coordinateSystem;
  250. indexOf(coordsList, cartesian) < 0 && (cartesian = null);
  251. } else if (xAxisModel && yAxisModel) {
  252. cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  253. } else if (xAxisModel) {
  254. axis = this.getAxis('x', xAxisModel.componentIndex);
  255. } else if (yAxisModel) {
  256. axis = this.getAxis('y', yAxisModel.componentIndex);
  257. }
  258. // Lowest priority.
  259. else if (gridModel) {
  260. var grid = gridModel.coordinateSystem;
  261. if (grid === this) {
  262. cartesian = this._coordsList[0];
  263. }
  264. }
  265. return {
  266. cartesian: cartesian,
  267. axis: axis
  268. };
  269. };
  270. /**
  271. * @implements
  272. */
  273. Grid.prototype.containPoint = function (point) {
  274. var coord = this._coordsList[0];
  275. if (coord) {
  276. return coord.containPoint(point);
  277. }
  278. };
  279. /**
  280. * Initialize cartesian coordinate systems
  281. */
  282. Grid.prototype._initCartesian = function (gridModel, ecModel, api) {
  283. var _this = this;
  284. var grid = this;
  285. var axisPositionUsed = {
  286. left: false,
  287. right: false,
  288. top: false,
  289. bottom: false
  290. };
  291. var axesMap = {
  292. x: {},
  293. y: {}
  294. };
  295. var axesCount = {
  296. x: 0,
  297. y: 0
  298. };
  299. // Create axis
  300. ecModel.eachComponent('xAxis', createAxisCreator('x'), this);
  301. ecModel.eachComponent('yAxis', createAxisCreator('y'), this);
  302. if (!axesCount.x || !axesCount.y) {
  303. // Roll back when there no either x or y axis
  304. this._axesMap = {};
  305. this._axesList = [];
  306. return;
  307. }
  308. this._axesMap = axesMap;
  309. // Create cartesian2d
  310. each(axesMap.x, function (xAxis, xAxisIndex) {
  311. each(axesMap.y, function (yAxis, yAxisIndex) {
  312. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  313. var cartesian = new Cartesian2D(key);
  314. cartesian.master = _this;
  315. cartesian.model = gridModel;
  316. _this._coordsMap[key] = cartesian;
  317. _this._coordsList.push(cartesian);
  318. cartesian.addAxis(xAxis);
  319. cartesian.addAxis(yAxis);
  320. });
  321. });
  322. function createAxisCreator(dimName) {
  323. return function (axisModel, idx) {
  324. if (!isAxisUsedInTheGrid(axisModel, gridModel)) {
  325. return;
  326. }
  327. var axisPosition = axisModel.get('position');
  328. if (dimName === 'x') {
  329. // Fix position
  330. if (axisPosition !== 'top' && axisPosition !== 'bottom') {
  331. // Default bottom of X
  332. axisPosition = axisPositionUsed.bottom ? 'top' : 'bottom';
  333. }
  334. } else {
  335. // Fix position
  336. if (axisPosition !== 'left' && axisPosition !== 'right') {
  337. // Default left of Y
  338. axisPosition = axisPositionUsed.left ? 'right' : 'left';
  339. }
  340. }
  341. axisPositionUsed[axisPosition] = true;
  342. var axis = new Axis2D(dimName, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition);
  343. var isCategory = axis.type === 'category';
  344. axis.onBand = isCategory && axisModel.get('boundaryGap');
  345. axis.inverse = axisModel.get('inverse');
  346. // Inject axis into axisModel
  347. axisModel.axis = axis;
  348. // Inject axisModel into axis
  349. axis.model = axisModel;
  350. // Inject grid info axis
  351. axis.grid = grid;
  352. // Index of axis, can be used as key
  353. axis.index = idx;
  354. grid._axesList.push(axis);
  355. axesMap[dimName][idx] = axis;
  356. axesCount[dimName]++;
  357. };
  358. }
  359. };
  360. /**
  361. * Update cartesian properties from series.
  362. */
  363. Grid.prototype._updateScale = function (ecModel, gridModel) {
  364. // Reset scale
  365. each(this._axesList, function (axis) {
  366. axis.scale.setExtent(Infinity, -Infinity);
  367. if (axis.type === 'category') {
  368. var categorySortInfo = axis.model.get('categorySortInfo');
  369. axis.scale.setSortInfo(categorySortInfo);
  370. }
  371. });
  372. ecModel.eachSeries(function (seriesModel) {
  373. // If pie (or other similar series) use cartesian2d, the unionExtent logic below is
  374. // wrong, therefore skip it temporarily. See also in `defaultAxisExtentFromData.ts`.
  375. // TODO: support union extent in this case.
  376. if (isCartesian2DInjectedAsDataCoordSys(seriesModel)) {
  377. var axesModelMap = findAxisModels(seriesModel);
  378. var xAxisModel = axesModelMap.xAxisModel;
  379. var yAxisModel = axesModelMap.yAxisModel;
  380. if (!isAxisUsedInTheGrid(xAxisModel, gridModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel)) {
  381. return;
  382. }
  383. var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  384. var data = seriesModel.getData();
  385. var xAxis = cartesian.getAxis('x');
  386. var yAxis = cartesian.getAxis('y');
  387. unionExtent(data, xAxis);
  388. unionExtent(data, yAxis);
  389. }
  390. }, this);
  391. function unionExtent(data, axis) {
  392. each(getDataDimensionsOnAxis(data, axis.dim), function (dim) {
  393. axis.scale.unionExtentFromData(data, dim);
  394. });
  395. }
  396. };
  397. /**
  398. * @param dim 'x' or 'y' or 'auto' or null/undefined
  399. */
  400. Grid.prototype.getTooltipAxes = function (dim) {
  401. var baseAxes = [];
  402. var otherAxes = [];
  403. each(this.getCartesians(), function (cartesian) {
  404. var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis();
  405. var otherAxis = cartesian.getOtherAxis(baseAxis);
  406. indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);
  407. indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);
  408. });
  409. return {
  410. baseAxes: baseAxes,
  411. otherAxes: otherAxes
  412. };
  413. };
  414. Grid.create = function (ecModel, api) {
  415. var grids = [];
  416. ecModel.eachComponent('grid', function (gridModel, idx) {
  417. var grid = new Grid(gridModel, ecModel, api);
  418. grid.name = 'grid_' + idx;
  419. // dataSampling requires axis extent, so resize
  420. // should be performed in create stage.
  421. grid.resize(gridModel, api, true);
  422. gridModel.coordinateSystem = grid;
  423. grids.push(grid);
  424. });
  425. // Inject the coordinateSystems into seriesModel
  426. ecModel.eachSeries(function (seriesModel) {
  427. injectCoordSysByOption({
  428. targetModel: seriesModel,
  429. coordSysType: 'cartesian2d',
  430. coordSysProvider: coordSysProvider
  431. });
  432. function coordSysProvider() {
  433. var axesModelMap = findAxisModels(seriesModel);
  434. var xAxisModel = axesModelMap.xAxisModel;
  435. var yAxisModel = axesModelMap.yAxisModel;
  436. var gridModel = xAxisModel.getCoordSysModel();
  437. if (process.env.NODE_ENV !== 'production') {
  438. if (!gridModel) {
  439. throw new Error('Grid "' + retrieve3(xAxisModel.get('gridIndex'), xAxisModel.get('gridId'), 0) + '" not found');
  440. }
  441. if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {
  442. throw new Error('xAxis and yAxis must use the same grid');
  443. }
  444. }
  445. var grid = gridModel.coordinateSystem;
  446. return grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  447. }
  448. });
  449. return grids;
  450. };
  451. // For deciding which dimensions to use when creating list data
  452. Grid.dimensions = cartesian2DDimensions;
  453. return Grid;
  454. }();
  455. /**
  456. * Check if the axis is used in the specified grid.
  457. */
  458. function isAxisUsedInTheGrid(axisModel, gridModel) {
  459. return axisModel.getCoordSysModel() === gridModel;
  460. }
  461. function fixAxisOnZero(axesMap, otherAxisDim, axis,
  462. // Key: see `getOnZeroRecordKey`
  463. onZeroRecords) {
  464. axis.getAxesOnZeroOf = function () {
  465. // TODO: onZero of multiple axes.
  466. return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : [];
  467. };
  468. // onZero can not be enabled in these two situations:
  469. // 1. When any other axis is a category axis.
  470. // 2. When no axis is cross 0 point.
  471. var otherAxes = axesMap[otherAxisDim];
  472. var otherAxisOnZeroOf;
  473. var axisModel = axis.model;
  474. var onZero = axisModel.get(['axisLine', 'onZero']);
  475. var onZeroAxisIndex = axisModel.get(['axisLine', 'onZeroAxisIndex']);
  476. if (!onZero) {
  477. return;
  478. }
  479. // If target axis is specified.
  480. if (onZeroAxisIndex != null) {
  481. if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) {
  482. otherAxisOnZeroOf = otherAxes[onZeroAxisIndex];
  483. }
  484. } else {
  485. // Find the first available other axis.
  486. for (var idx in otherAxes) {
  487. if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx])
  488. // Consider that two Y axes on one value axis,
  489. // if both onZero, the two Y axes overlap.
  490. && !onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]) {
  491. otherAxisOnZeroOf = otherAxes[idx];
  492. break;
  493. }
  494. }
  495. }
  496. if (otherAxisOnZeroOf) {
  497. onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true;
  498. }
  499. function getOnZeroRecordKey(axis) {
  500. return axis.dim + '_' + axis.index;
  501. }
  502. }
  503. function canOnZeroToAxis(axis) {
  504. return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis);
  505. }
  506. function updateAxisTransform(axis, coordBase) {
  507. var axisExtent = axis.getExtent();
  508. var axisExtentSum = axisExtent[0] + axisExtent[1];
  509. // Fast transform
  510. axis.toGlobalCoord = axis.dim === 'x' ? function (coord) {
  511. return coord + coordBase;
  512. } : function (coord) {
  513. return axisExtentSum - coord + coordBase;
  514. };
  515. axis.toLocalCoord = axis.dim === 'x' ? function (coord) {
  516. return coord - coordBase;
  517. } : function (coord) {
  518. return axisExtentSum - coord + coordBase;
  519. };
  520. }
  521. function updateAllAxisExtentTransByGridRect(axesMap, gridRect) {
  522. each(axesMap.x, function (axis) {
  523. return updateAxisExtentTransByGridRect(axis, gridRect.x, gridRect.width);
  524. });
  525. each(axesMap.y, function (axis) {
  526. return updateAxisExtentTransByGridRect(axis, gridRect.y, gridRect.height);
  527. });
  528. }
  529. function updateAxisExtentTransByGridRect(axis, gridXY, gridWH) {
  530. var extent = [0, gridWH];
  531. var idx = axis.inverse ? 1 : 0;
  532. axis.setExtent(extent[idx], extent[1 - idx]);
  533. updateAxisTransform(axis, gridXY);
  534. }
  535. var legacyLayOutGridByContainLabel;
  536. export function registerLegacyGridContainLabelImpl(impl) {
  537. legacyLayOutGridByContainLabel = impl;
  538. }
  539. // Return noPxChange.
  540. function layOutGridByOuterBounds(outerBoundsRect, outerBoundsContain, outerBoundsClamp, gridRect, axesMap, axisBuilderSharedCtx, layoutRef) {
  541. if (process.env.NODE_ENV !== 'production') {
  542. assert(outerBoundsContain === 'all' || outerBoundsContain === 'axisLabel');
  543. }
  544. // Assume `updateAllAxisExtentTransByGridRect` has been performed once before this call.
  545. // [NOTE]:
  546. // - The bounding rect of the axis elements might be sensitve to variations in `axis.extent` due to strategies
  547. // like hideOverlap/moveOverlap. @see the comment in `LabelLayoutBase['suggestIgnore']`.
  548. // - The final `gridRect` might be slightly smaller than the ideally expected result if labels are giant and
  549. // get hidden due to overlapping. More iterations could improve precision, but not performant. We consider
  550. // the current result acceptable, since no alignment among charts can be guaranteed when using this feature.
  551. createOrUpdateAxesView(gridRect, axesMap, AxisTickLabelComputingKind.estimate, outerBoundsContain, false, layoutRef);
  552. var margin = [0, 0, 0, 0];
  553. fillLabelNameOverflowOnOneDimension(0);
  554. fillLabelNameOverflowOnOneDimension(1);
  555. // If axis is blank, no label can be used to detect overflow.
  556. // gridRect itself should not overflow.
  557. fillMarginOnOneDimension(gridRect, 0, NaN);
  558. fillMarginOnOneDimension(gridRect, 1, NaN);
  559. var noPxChange = find(margin, function (item) {
  560. return item > 0;
  561. }) == null;
  562. expandOrShrinkRect(gridRect, margin, true, true, outerBoundsClamp);
  563. updateAllAxisExtentTransByGridRect(axesMap, gridRect);
  564. return noPxChange;
  565. function fillLabelNameOverflowOnOneDimension(xyIdx) {
  566. each(axesMap[XY[xyIdx]], function (axis) {
  567. if (!shouldAxisShow(axis.model)) {
  568. return;
  569. }
  570. // FIXME: zr Group.union may wrongly union (0, 0, 0, 0) and not performant.
  571. // unionRect.union(axis.axisBuilder.group.getBoundingRect());
  572. // If ussing Group.getBoundingRect to calculate shrink space, it is not strictly accurate when
  573. // the outermost label is ignored and the secondary label is very long and contribute to the
  574. // union extension:
  575. // -|---|---|---|
  576. // 1,000,000,000
  577. // Therefore we calculate them one by one.
  578. // Also considered axis may be blank or no labels.
  579. var sharedRecord = axisBuilderSharedCtx.ensureRecord(axis.model);
  580. var labelInfoList = sharedRecord.labelInfoList;
  581. if (labelInfoList) {
  582. for (var idx = 0; idx < labelInfoList.length; idx++) {
  583. var labelInfo = labelInfoList[idx];
  584. var proportion = axis.scale.normalize(getLabelInner(labelInfo.label).tickValue);
  585. proportion = xyIdx === 1 ? 1 - proportion : proportion;
  586. // xAxis use proportion on x, yAxis use proprotion on y, otherwise not.
  587. fillMarginOnOneDimension(labelInfo.rect, xyIdx, proportion);
  588. fillMarginOnOneDimension(labelInfo.rect, 1 - xyIdx, NaN);
  589. }
  590. }
  591. var nameLayout = sharedRecord.nameLayout;
  592. if (nameLayout) {
  593. var proportion = isNameLocationCenter(sharedRecord.nameLocation) ? 0.5 : NaN;
  594. fillMarginOnOneDimension(nameLayout.rect, xyIdx, proportion);
  595. fillMarginOnOneDimension(nameLayout.rect, 1 - xyIdx, NaN);
  596. }
  597. });
  598. }
  599. function fillMarginOnOneDimension(itemRect, xyIdx, proportion // NaN mean no use proportion
  600. ) {
  601. var overflow1 = outerBoundsRect[XY[xyIdx]] - itemRect[XY[xyIdx]];
  602. var overflow2 = itemRect[WH[xyIdx]] + itemRect[XY[xyIdx]] - (outerBoundsRect[WH[xyIdx]] + outerBoundsRect[XY[xyIdx]]);
  603. overflow1 = applyProportion(overflow1, 1 - proportion);
  604. overflow2 = applyProportion(overflow2, proportion);
  605. var minIdx = XY_TO_MARGIN_IDX[xyIdx][0];
  606. var maxIdx = XY_TO_MARGIN_IDX[xyIdx][1];
  607. margin[minIdx] = mathMax(margin[minIdx], overflow1);
  608. margin[maxIdx] = mathMax(margin[maxIdx], overflow2);
  609. }
  610. function applyProportion(overflow, proportion) {
  611. // proportion is not likely to near zero. If so, give up shrink
  612. if (overflow > 0 && !eqNaN(proportion) && proportion > 1e-4) {
  613. overflow /= proportion;
  614. }
  615. return overflow;
  616. }
  617. }
  618. function createAxisBiulders(gridRect, cartesians, axesMap, optionContainLabel, api) {
  619. var axisBuilderSharedCtx = new AxisBuilderSharedContext(resolveAxisNameOverlapForGrid);
  620. each(axesMap, function (axisList) {
  621. return each(axisList, function (axis) {
  622. if (shouldAxisShow(axis.model)) {
  623. // See `AxisBaseOptionCommon['nameMoveOverlap']`.
  624. var defaultNameMoveOverlap = !optionContainLabel;
  625. axis.axisBuilder = createCartesianAxisViewCommonPartBuilder(gridRect, cartesians, axis.model, api, axisBuilderSharedCtx, defaultNameMoveOverlap);
  626. }
  627. });
  628. });
  629. return axisBuilderSharedCtx;
  630. }
  631. /**
  632. * Promote the axis-elements-building from "view render" stage to "coordinate system resize" stage.
  633. * This is aimed to resovle overlap across multiple axes, since currently it's hard to reconcile
  634. * multiple axes in "view render" stage.
  635. *
  636. * [CAUTION] But this promotion assumes that the subsequent "visual mapping" stage does not affect
  637. * this axis-elements-building; otherwise we have to refactor it again.
  638. */
  639. function createOrUpdateAxesView(gridRect, axesMap, kind, outerBoundsContain, noPxChange, layoutRef) {
  640. var isDetermine = kind === AxisTickLabelComputingKind.determine;
  641. each(axesMap, function (axisList) {
  642. return each(axisList, function (axis) {
  643. if (shouldAxisShow(axis.model)) {
  644. updateCartesianAxisViewCommonPartBuilder(axis.axisBuilder, gridRect, axis.model);
  645. axis.axisBuilder.build(isDetermine ? {
  646. axisTickLabelDetermine: true
  647. } : {
  648. axisTickLabelEstimate: true
  649. }, {
  650. noPxChange: noPxChange
  651. });
  652. }
  653. });
  654. });
  655. var nameMarginLevelMap = {
  656. x: 0,
  657. y: 0
  658. };
  659. calcNameMarginLevel(0);
  660. calcNameMarginLevel(1);
  661. function calcNameMarginLevel(xyIdx) {
  662. nameMarginLevelMap[XY[1 - xyIdx]] = gridRect[WH[xyIdx]] <= layoutRef.refContainer[WH[xyIdx]] * 0.5 ? 0 : 1 - xyIdx === 1 ? 2 : 1;
  663. }
  664. each(axesMap, function (axisList, xy) {
  665. return each(axisList, function (axis) {
  666. if (shouldAxisShow(axis.model)) {
  667. if (outerBoundsContain === 'all' || isDetermine) {
  668. // To resolve overlap, `axisName` layout depends on `axisTickLabel` layout result
  669. // (all of the axes of the same `grid`; consider multiple x or y axes).
  670. axis.axisBuilder.build({
  671. axisName: true
  672. }, {
  673. nameMarginLevel: nameMarginLevelMap[xy]
  674. });
  675. }
  676. if (isDetermine) {
  677. axis.axisBuilder.build({
  678. axisLine: true
  679. });
  680. }
  681. }
  682. });
  683. });
  684. }
  685. function prepareOuterBounds(gridModel, rawRridRect, layoutRef) {
  686. var outerBoundsRect;
  687. var optionOuterBoundsMode = gridModel.get('outerBoundsMode', true);
  688. if (optionOuterBoundsMode === 'same') {
  689. outerBoundsRect = rawRridRect.clone();
  690. } else if (optionOuterBoundsMode == null || optionOuterBoundsMode === 'auto') {
  691. outerBoundsRect = getLayoutRect(gridModel.get('outerBounds', true) || OUTER_BOUNDS_DEFAULT, layoutRef.refContainer);
  692. } else if (optionOuterBoundsMode !== 'none') {
  693. if (process.env.NODE_ENV !== 'production') {
  694. error("Invalid grid[" + gridModel.componentIndex + "].outerBoundsMode.");
  695. }
  696. }
  697. var optionOuterBoundsContain = gridModel.get('outerBoundsContain', true);
  698. var parsedOuterBoundsContain;
  699. if (optionOuterBoundsContain == null || optionOuterBoundsContain === 'auto') {
  700. parsedOuterBoundsContain = 'all';
  701. } else if (indexOf(['all', 'axisLabel'], optionOuterBoundsContain) < 0) {
  702. if (process.env.NODE_ENV !== 'production') {
  703. error("Invalid grid[" + gridModel.componentIndex + "].outerBoundsContain.");
  704. }
  705. parsedOuterBoundsContain = 'all';
  706. } else {
  707. parsedOuterBoundsContain = optionOuterBoundsContain;
  708. }
  709. var outerBoundsClamp = [parsePositionSizeOption(retrieve2(gridModel.get('outerBoundsClampWidth', true), OUTER_BOUNDS_CLAMP_DEFAULT[0]), rawRridRect.width), parsePositionSizeOption(retrieve2(gridModel.get('outerBoundsClampHeight', true), OUTER_BOUNDS_CLAMP_DEFAULT[1]), rawRridRect.height)];
  710. return {
  711. outerBoundsRect: outerBoundsRect,
  712. parsedOuterBoundsContain: parsedOuterBoundsContain,
  713. outerBoundsClamp: outerBoundsClamp
  714. };
  715. }
  716. var resolveAxisNameOverlapForGrid = function (cfg, ctx, axisModel, nameLayoutInfo, nameMoveDirVec, thisRecord) {
  717. var perpendicularDim = axisModel.axis.dim === 'x' ? 'y' : 'x';
  718. resolveAxisNameOverlapDefault(cfg, ctx, axisModel, nameLayoutInfo, nameMoveDirVec, thisRecord);
  719. // If nameLocation 'center', and there are multiple axes parallel to this axis, do not adjust by
  720. // other axes, because the axis name should be close to its axis line as much as possible even
  721. // if overlapping; otherwise it might cause misleading.
  722. // If nameLocation 'center', do not adjust by perpendicular axes, since they are not likely to overlap.
  723. // If nameLocation 'start'/'end', move name within the same direction to escape overlap with the
  724. // perpendicular axes.
  725. if (!isNameLocationCenter(cfg.nameLocation)) {
  726. each(ctx.recordMap[perpendicularDim], function (perpenRecord) {
  727. // perpendicular axis may be no name.
  728. if (perpenRecord && perpenRecord.labelInfoList && perpenRecord.dirVec) {
  729. moveIfOverlapByLinearLabels(perpenRecord.labelInfoList, perpenRecord.dirVec, nameLayoutInfo, nameMoveDirVec);
  730. }
  731. });
  732. }
  733. };
  734. export default Grid;