Grid.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  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 } from 'zrender/lib/core/util';
  46. import { getLayoutRect } from '../../util/layout';
  47. import { createScaleByModel, ifAxisCrossZero, niceScaleExtent, estimateLabelUnionRect, getDataDimensionsOnAxis } from '../../coord/axisHelper';
  48. import Cartesian2D, { cartesian2DDimensions } from './Cartesian2D';
  49. import Axis2D from './Axis2D';
  50. import { SINGLE_REFERRING } from '../../util/model';
  51. import { isCartesian2DSeries, findAxisModels } from './cartesianAxisHelper';
  52. var Grid =
  53. /** @class */
  54. function () {
  55. function Grid(gridModel, ecModel, api) {
  56. // FIXME:TS where used (different from registered type 'cartesian2d')?
  57. this.type = 'grid';
  58. this._coordsMap = {};
  59. this._coordsList = [];
  60. this._axesMap = {};
  61. this._axesList = [];
  62. this.axisPointerEnabled = true;
  63. this.dimensions = cartesian2DDimensions;
  64. this._initCartesian(gridModel, ecModel, api);
  65. this.model = gridModel;
  66. }
  67. Grid.prototype.getRect = function () {
  68. return this._rect;
  69. };
  70. Grid.prototype.update = function (ecModel, api) {
  71. var axesMap = this._axesMap;
  72. this._updateScale(ecModel, this.model);
  73. each(axesMap.x, function (xAxis) {
  74. niceScaleExtent(xAxis.scale, xAxis.model);
  75. });
  76. each(axesMap.y, function (yAxis) {
  77. niceScaleExtent(yAxis.scale, yAxis.model);
  78. }); // Key: axisDim_axisIndex, value: boolean, whether onZero target.
  79. var onZeroRecords = {};
  80. each(axesMap.x, function (xAxis) {
  81. fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords);
  82. });
  83. each(axesMap.y, function (yAxis) {
  84. fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords);
  85. }); // Resize again if containLabel is enabled
  86. // FIXME It may cause getting wrong grid size in data processing stage
  87. this.resize(this.model, api);
  88. };
  89. /**
  90. * Resize the grid
  91. */
  92. Grid.prototype.resize = function (gridModel, api, ignoreContainLabel) {
  93. var boxLayoutParams = gridModel.getBoxLayoutParams();
  94. var isContainLabel = !ignoreContainLabel && gridModel.get('containLabel');
  95. var gridRect = getLayoutRect(boxLayoutParams, {
  96. width: api.getWidth(),
  97. height: api.getHeight()
  98. });
  99. this._rect = gridRect;
  100. var axesList = this._axesList;
  101. adjustAxes(); // Minus label size
  102. if (isContainLabel) {
  103. each(axesList, function (axis) {
  104. if (!axis.model.get(['axisLabel', 'inside'])) {
  105. var labelUnionRect = estimateLabelUnionRect(axis);
  106. if (labelUnionRect) {
  107. var dim = axis.isHorizontal() ? 'height' : 'width';
  108. var margin = axis.model.get(['axisLabel', 'margin']);
  109. gridRect[dim] -= labelUnionRect[dim] + margin;
  110. if (axis.position === 'top') {
  111. gridRect.y += labelUnionRect.height + margin;
  112. } else if (axis.position === 'left') {
  113. gridRect.x += labelUnionRect.width + margin;
  114. }
  115. }
  116. }
  117. });
  118. adjustAxes();
  119. }
  120. each(this._coordsList, function (coord) {
  121. // Calculate affine matrix to accelerate the data to point transform.
  122. // If all the axes scales are time or value.
  123. coord.calcAffineTransform();
  124. });
  125. function adjustAxes() {
  126. each(axesList, function (axis) {
  127. var isHorizontal = axis.isHorizontal();
  128. var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];
  129. var idx = axis.inverse ? 1 : 0;
  130. axis.setExtent(extent[idx], extent[1 - idx]);
  131. updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y);
  132. });
  133. }
  134. };
  135. Grid.prototype.getAxis = function (dim, axisIndex) {
  136. var axesMapOnDim = this._axesMap[dim];
  137. if (axesMapOnDim != null) {
  138. return axesMapOnDim[axisIndex || 0]; // if (axisIndex == null) {
  139. // Find first axis
  140. // for (let name in axesMapOnDim) {
  141. // if (axesMapOnDim.hasOwnProperty(name)) {
  142. // return axesMapOnDim[name];
  143. // }
  144. // }
  145. // }
  146. // return axesMapOnDim[axisIndex];
  147. }
  148. };
  149. Grid.prototype.getAxes = function () {
  150. return this._axesList.slice();
  151. };
  152. Grid.prototype.getCartesian = function (xAxisIndex, yAxisIndex) {
  153. if (xAxisIndex != null && yAxisIndex != null) {
  154. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  155. return this._coordsMap[key];
  156. }
  157. if (isObject(xAxisIndex)) {
  158. yAxisIndex = xAxisIndex.yAxisIndex;
  159. xAxisIndex = xAxisIndex.xAxisIndex;
  160. }
  161. for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {
  162. if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) {
  163. return coordList[i];
  164. }
  165. }
  166. };
  167. Grid.prototype.getCartesians = function () {
  168. return this._coordsList.slice();
  169. };
  170. /**
  171. * @implements
  172. */
  173. Grid.prototype.convertToPixel = function (ecModel, finder, value) {
  174. var target = this._findConvertTarget(finder);
  175. return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null;
  176. };
  177. /**
  178. * @implements
  179. */
  180. Grid.prototype.convertFromPixel = function (ecModel, finder, value) {
  181. var target = this._findConvertTarget(finder);
  182. return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null;
  183. };
  184. Grid.prototype._findConvertTarget = function (finder) {
  185. var seriesModel = finder.seriesModel;
  186. var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0];
  187. var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0];
  188. var gridModel = finder.gridModel;
  189. var coordsList = this._coordsList;
  190. var cartesian;
  191. var axis;
  192. if (seriesModel) {
  193. cartesian = seriesModel.coordinateSystem;
  194. indexOf(coordsList, cartesian) < 0 && (cartesian = null);
  195. } else if (xAxisModel && yAxisModel) {
  196. cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  197. } else if (xAxisModel) {
  198. axis = this.getAxis('x', xAxisModel.componentIndex);
  199. } else if (yAxisModel) {
  200. axis = this.getAxis('y', yAxisModel.componentIndex);
  201. } // Lowest priority.
  202. else if (gridModel) {
  203. var grid = gridModel.coordinateSystem;
  204. if (grid === this) {
  205. cartesian = this._coordsList[0];
  206. }
  207. }
  208. return {
  209. cartesian: cartesian,
  210. axis: axis
  211. };
  212. };
  213. /**
  214. * @implements
  215. */
  216. Grid.prototype.containPoint = function (point) {
  217. var coord = this._coordsList[0];
  218. if (coord) {
  219. return coord.containPoint(point);
  220. }
  221. };
  222. /**
  223. * Initialize cartesian coordinate systems
  224. */
  225. Grid.prototype._initCartesian = function (gridModel, ecModel, api) {
  226. var _this = this;
  227. var grid = this;
  228. var axisPositionUsed = {
  229. left: false,
  230. right: false,
  231. top: false,
  232. bottom: false
  233. };
  234. var axesMap = {
  235. x: {},
  236. y: {}
  237. };
  238. var axesCount = {
  239. x: 0,
  240. y: 0
  241. }; /// Create axis
  242. ecModel.eachComponent('xAxis', createAxisCreator('x'), this);
  243. ecModel.eachComponent('yAxis', createAxisCreator('y'), this);
  244. if (!axesCount.x || !axesCount.y) {
  245. // Roll back when there no either x or y axis
  246. this._axesMap = {};
  247. this._axesList = [];
  248. return;
  249. }
  250. this._axesMap = axesMap; /// Create cartesian2d
  251. each(axesMap.x, function (xAxis, xAxisIndex) {
  252. each(axesMap.y, function (yAxis, yAxisIndex) {
  253. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  254. var cartesian = new Cartesian2D(key);
  255. cartesian.master = _this;
  256. cartesian.model = gridModel;
  257. _this._coordsMap[key] = cartesian;
  258. _this._coordsList.push(cartesian);
  259. cartesian.addAxis(xAxis);
  260. cartesian.addAxis(yAxis);
  261. });
  262. });
  263. function createAxisCreator(dimName) {
  264. return function (axisModel, idx) {
  265. if (!isAxisUsedInTheGrid(axisModel, gridModel)) {
  266. return;
  267. }
  268. var axisPosition = axisModel.get('position');
  269. if (dimName === 'x') {
  270. // Fix position
  271. if (axisPosition !== 'top' && axisPosition !== 'bottom') {
  272. // Default bottom of X
  273. axisPosition = axisPositionUsed.bottom ? 'top' : 'bottom';
  274. }
  275. } else {
  276. // Fix position
  277. if (axisPosition !== 'left' && axisPosition !== 'right') {
  278. // Default left of Y
  279. axisPosition = axisPositionUsed.left ? 'right' : 'left';
  280. }
  281. }
  282. axisPositionUsed[axisPosition] = true;
  283. var axis = new Axis2D(dimName, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition);
  284. var isCategory = axis.type === 'category';
  285. axis.onBand = isCategory && axisModel.get('boundaryGap');
  286. axis.inverse = axisModel.get('inverse'); // Inject axis into axisModel
  287. axisModel.axis = axis; // Inject axisModel into axis
  288. axis.model = axisModel; // Inject grid info axis
  289. axis.grid = grid; // Index of axis, can be used as key
  290. axis.index = idx;
  291. grid._axesList.push(axis);
  292. axesMap[dimName][idx] = axis;
  293. axesCount[dimName]++;
  294. };
  295. }
  296. };
  297. /**
  298. * Update cartesian properties from series.
  299. */
  300. Grid.prototype._updateScale = function (ecModel, gridModel) {
  301. // Reset scale
  302. each(this._axesList, function (axis) {
  303. axis.scale.setExtent(Infinity, -Infinity);
  304. if (axis.type === 'category') {
  305. var categorySortInfo = axis.model.get('categorySortInfo');
  306. axis.scale.setSortInfo(categorySortInfo);
  307. }
  308. });
  309. ecModel.eachSeries(function (seriesModel) {
  310. if (isCartesian2DSeries(seriesModel)) {
  311. var axesModelMap = findAxisModels(seriesModel);
  312. var xAxisModel = axesModelMap.xAxisModel;
  313. var yAxisModel = axesModelMap.yAxisModel;
  314. if (!isAxisUsedInTheGrid(xAxisModel, gridModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel)) {
  315. return;
  316. }
  317. var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  318. var data = seriesModel.getData();
  319. var xAxis = cartesian.getAxis('x');
  320. var yAxis = cartesian.getAxis('y');
  321. if (data.type === 'list') {
  322. unionExtent(data, xAxis);
  323. unionExtent(data, yAxis);
  324. }
  325. }
  326. }, this);
  327. function unionExtent(data, axis) {
  328. each(getDataDimensionsOnAxis(data, axis.dim), function (dim) {
  329. axis.scale.unionExtentFromData(data, dim);
  330. });
  331. }
  332. };
  333. /**
  334. * @param dim 'x' or 'y' or 'auto' or null/undefined
  335. */
  336. Grid.prototype.getTooltipAxes = function (dim) {
  337. var baseAxes = [];
  338. var otherAxes = [];
  339. each(this.getCartesians(), function (cartesian) {
  340. var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis();
  341. var otherAxis = cartesian.getOtherAxis(baseAxis);
  342. indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);
  343. indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);
  344. });
  345. return {
  346. baseAxes: baseAxes,
  347. otherAxes: otherAxes
  348. };
  349. };
  350. Grid.create = function (ecModel, api) {
  351. var grids = [];
  352. ecModel.eachComponent('grid', function (gridModel, idx) {
  353. var grid = new Grid(gridModel, ecModel, api);
  354. grid.name = 'grid_' + idx; // dataSampling requires axis extent, so resize
  355. // should be performed in create stage.
  356. grid.resize(gridModel, api, true);
  357. gridModel.coordinateSystem = grid;
  358. grids.push(grid);
  359. }); // Inject the coordinateSystems into seriesModel
  360. ecModel.eachSeries(function (seriesModel) {
  361. if (!isCartesian2DSeries(seriesModel)) {
  362. return;
  363. }
  364. var axesModelMap = findAxisModels(seriesModel);
  365. var xAxisModel = axesModelMap.xAxisModel;
  366. var yAxisModel = axesModelMap.yAxisModel;
  367. var gridModel = xAxisModel.getCoordSysModel();
  368. if (process.env.NODE_ENV !== 'production') {
  369. if (!gridModel) {
  370. throw new Error('Grid "' + retrieve3(xAxisModel.get('gridIndex'), xAxisModel.get('gridId'), 0) + '" not found');
  371. }
  372. if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {
  373. throw new Error('xAxis and yAxis must use the same grid');
  374. }
  375. }
  376. var grid = gridModel.coordinateSystem;
  377. seriesModel.coordinateSystem = grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  378. });
  379. return grids;
  380. }; // For deciding which dimensions to use when creating list data
  381. Grid.dimensions = cartesian2DDimensions;
  382. return Grid;
  383. }();
  384. /**
  385. * Check if the axis is used in the specified grid.
  386. */
  387. function isAxisUsedInTheGrid(axisModel, gridModel) {
  388. return axisModel.getCoordSysModel() === gridModel;
  389. }
  390. function fixAxisOnZero(axesMap, otherAxisDim, axis, // Key: see `getOnZeroRecordKey`
  391. onZeroRecords) {
  392. axis.getAxesOnZeroOf = function () {
  393. // TODO: onZero of multiple axes.
  394. return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : [];
  395. }; // onZero can not be enabled in these two situations:
  396. // 1. When any other axis is a category axis.
  397. // 2. When no axis is cross 0 point.
  398. var otherAxes = axesMap[otherAxisDim];
  399. var otherAxisOnZeroOf;
  400. var axisModel = axis.model;
  401. var onZero = axisModel.get(['axisLine', 'onZero']);
  402. var onZeroAxisIndex = axisModel.get(['axisLine', 'onZeroAxisIndex']);
  403. if (!onZero) {
  404. return;
  405. } // If target axis is specified.
  406. if (onZeroAxisIndex != null) {
  407. if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) {
  408. otherAxisOnZeroOf = otherAxes[onZeroAxisIndex];
  409. }
  410. } else {
  411. // Find the first available other axis.
  412. for (var idx in otherAxes) {
  413. if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx]) // Consider that two Y axes on one value axis,
  414. // if both onZero, the two Y axes overlap.
  415. && !onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]) {
  416. otherAxisOnZeroOf = otherAxes[idx];
  417. break;
  418. }
  419. }
  420. }
  421. if (otherAxisOnZeroOf) {
  422. onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true;
  423. }
  424. function getOnZeroRecordKey(axis) {
  425. return axis.dim + '_' + axis.index;
  426. }
  427. }
  428. function canOnZeroToAxis(axis) {
  429. return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis);
  430. }
  431. function updateAxisTransform(axis, coordBase) {
  432. var axisExtent = axis.getExtent();
  433. var axisExtentSum = axisExtent[0] + axisExtent[1]; // Fast transform
  434. axis.toGlobalCoord = axis.dim === 'x' ? function (coord) {
  435. return coord + coordBase;
  436. } : function (coord) {
  437. return axisExtentSum - coord + coordBase;
  438. };
  439. axis.toLocalCoord = axis.dim === 'x' ? function (coord) {
  440. return coord - coordBase;
  441. } : function (coord) {
  442. return axisExtentSum - coord + coordBase;
  443. };
  444. }
  445. export default Grid;