axisStatistics.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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 { assert, createHashMap, each, retrieve2 } from 'zrender/lib/core/util.js';
  41. import { makeCallOnlyOnce, makeInner } from '../util/model.js';
  42. import { getCachePerECFullUpdate, getCachePerECPrepare } from '../util/cycleCache.js';
  43. var callOnlyOnce = makeCallOnlyOnce();
  44. // Ensure that it never appears in internal generated uid and pre-defined coordSysType.
  45. export var AXIS_STAT_KEY_DELIMITER = '|&';
  46. var ecModelCacheFullUpdateInner = makeInner();
  47. // In this case, there are one or multiple valid data value but all the same.
  48. export var LINEAR_POSITIVE_MIN_GAP_SINGLE_VALID_VALUE = -2;
  49. export var LINEAR_POSITIVE_MIN_GAP_NO_VALID_VALUE = -1;
  50. var ecModelCachePrepareInner = makeInner();
  51. var validateInputAxis;
  52. if (process.env.NODE_ENV !== 'production') {
  53. validateInputAxis = function (axis) {
  54. assert(axis && axis.model && axis.model.uid && axis.model.ecModel);
  55. };
  56. }
  57. function getAxisStatPerKeyPerAxis(axis, axisStatKey) {
  58. var axisModel = axis.model;
  59. var keyed = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(axisModel.ecModel)).keyed;
  60. var perKey = keyed && keyed.get(axisStatKey);
  61. return perKey && perKey.get(axisModel.uid);
  62. }
  63. export function getAxisStat(axis, axisStatKey
  64. // Return: Never return null/undefined.
  65. ) {
  66. if (process.env.NODE_ENV !== 'production') {
  67. assert(axisStatKey != null);
  68. validateInputAxis(axis);
  69. }
  70. return wrapStatResult(getAxisStatPerKeyPerAxis(axis, axisStatKey));
  71. }
  72. export function getAxisStatBySeries(axis, seriesList
  73. // Return: Never be null/undefined; never contain null/undefined.
  74. ) {
  75. if (process.env.NODE_ENV !== 'production') {
  76. validateInputAxis(axis);
  77. }
  78. var result = [];
  79. eachKeyEachAxis(axis.model.ecModel, function (perKeyPerAxis) {
  80. for (var idx = 0; idx < seriesList.length; idx++) {
  81. if (seriesList[idx] && perKeyPerAxis.serByIdx[seriesList[idx].seriesIndex]) {
  82. result.push(wrapStatResult(perKeyPerAxis));
  83. }
  84. }
  85. });
  86. return result;
  87. }
  88. function eachKeyEachAxis(ecModel, cb) {
  89. var keyed = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)).keyed;
  90. keyed && keyed.each(function (perKey, axisStatKey) {
  91. perKey.each(function (perKeyPerAxis, axisModelUid) {
  92. cb(perKeyPerAxis, axisStatKey, axisModelUid);
  93. });
  94. });
  95. }
  96. function wrapStatResult(record) {
  97. return {
  98. liPosMinGap: record ? record.liPosMinGap : undefined
  99. };
  100. }
  101. export function eachSeriesOnAxis(axis, cb) {
  102. if (process.env.NODE_ENV !== 'production') {
  103. validateInputAxis(axis);
  104. }
  105. var ecModel = axis.model.ecModel;
  106. var seriesOnAxisMap = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)).axSer;
  107. seriesOnAxisMap && eachSeriesDealForAxisStat(ecModel, seriesOnAxisMap.get(axis.model.uid), cb);
  108. }
  109. /**
  110. * NOTE:
  111. * - series declaration order is respected (some ec option precedence matters, e.g., bar series).
  112. * - series filtered out are excluded.
  113. */
  114. export function eachSeriesOnAxisOnKey(axis, axisStatKey, cb) {
  115. if (process.env.NODE_ENV !== 'production') {
  116. assert(axisStatKey != null);
  117. validateInputAxis(axis);
  118. }
  119. var perKeyPerAxis = getAxisStatPerKeyPerAxis(axis, axisStatKey);
  120. perKeyPerAxis && eachSeriesDealForAxisStat(axis.model.ecModel, perKeyPerAxis.sers, cb);
  121. }
  122. export function eachSeriesDealForAxisStat(ecModel, seriesList, cb) {
  123. if (!seriesList) {
  124. return;
  125. }
  126. for (var i = 0; i < seriesList.length; i++) {
  127. var seriesModel = seriesList[i];
  128. // Legend-filtered series need to be ignored since series are registered before `legendFilter`.
  129. if (!ecModel.isSeriesFiltered(seriesModel)) {
  130. cb(seriesModel);
  131. }
  132. }
  133. }
  134. /**
  135. * NOTE:
  136. * - series filtered out are excluded.
  137. */
  138. export function countSeriesOnAxisOnKey(axis, axisStatKey) {
  139. if (process.env.NODE_ENV !== 'production') {
  140. assert(axisStatKey != null);
  141. validateInputAxis(axis);
  142. }
  143. var perKeyPerAxis = getAxisStatPerKeyPerAxis(axis, axisStatKey);
  144. if (!perKeyPerAxis || !perKeyPerAxis.sers.length) {
  145. return 0;
  146. }
  147. var count = 0;
  148. eachSeriesDealForAxisStat(axis.model.ecModel, perKeyPerAxis.sers, function () {
  149. count++;
  150. });
  151. return count;
  152. }
  153. /**
  154. * NOTICE: Available after `CoordinateSystem['create']` (not included).
  155. *
  156. * Query all axes that have at least one associated series (via `associateSeriesWithAxis`)
  157. * by the given key.
  158. */
  159. export function eachAxisOnKey(ecModel, axisStatKey, cb) {
  160. if (process.env.NODE_ENV !== 'production') {
  161. assert(axisStatKey != null);
  162. }
  163. var keyed = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)).keyed;
  164. var perKey = keyed && keyed.get(axisStatKey);
  165. perKey && perKey.each(function (perKeyPerAxis) {
  166. if (process.env.NODE_ENV !== 'production') {
  167. assert(perKeyPerAxis.sers.length > 0); // This is to avoid irrelevant axes to enter `cb`.
  168. }
  169. cb(perKeyPerAxis.axis);
  170. });
  171. }
  172. /**
  173. * NOTICE: Available after `CoordinateSystem['create']` (not included).
  174. *
  175. * Query all `AxisStatKey`s that have at least one associated series (via `associateSeriesWithAxis`)
  176. * by the given axis.
  177. */
  178. export function eachKeyOnAxis(axis, cb) {
  179. if (process.env.NODE_ENV !== 'production') {
  180. validateInputAxis(axis);
  181. }
  182. var model = axis.model;
  183. var keysByAxisModelUid = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(model.ecModel)).keys;
  184. keysByAxisModelUid && each(keysByAxisModelUid.get(model.uid), function (axisStatKey) {
  185. if (process.env.NODE_ENV !== 'production') {
  186. var stat = getAxisStatPerKeyPerAxis(axis, axisStatKey);
  187. assert(stat && stat.sers.length > 0); // This is to avoid irrelevant `AxisStatKey` to enter `cb`.
  188. }
  189. cb(axisStatKey);
  190. });
  191. }
  192. /**
  193. * NOTICE: this processor may be omitted - it is registered only if required.
  194. */
  195. function performAxisStatisticsOnOverallReset(ecModel) {
  196. var ecPrepareCache = ecModelCachePrepareInner(getCachePerECPrepare(ecModel));
  197. var ecPrepareCacheKeyed = ecPrepareCache.keyed || (ecPrepareCache.keyed = createHashMap());
  198. eachKeyEachAxis(ecModel, function (perKeyPerAxis, axisStatKey, axisModelUid) {
  199. var ecPrepareCachePerKey = ecPrepareCacheKeyed.get(axisStatKey) || ecPrepareCacheKeyed.set(axisStatKey, createHashMap());
  200. var ecPreparePerKeyPerAxis = ecPrepareCachePerKey.get(axisModelUid) || ecPrepareCachePerKey.set(axisModelUid, {});
  201. if (perKeyPerAxis.metrics.liPosMinGap) {
  202. // We should assert the impl exists -- fail-fast if missing `registerMetricImpl`.
  203. _metricImpl.liPosMinGap(ecModel, perKeyPerAxis, ecPreparePerKeyPerAxis);
  204. }
  205. });
  206. }
  207. // To reduce code size from unnecessary metrics.
  208. export function registerMetricImpl(metricType, impl) {
  209. _metricImpl[metricType] = impl;
  210. }
  211. var _metricImpl = {};
  212. /**
  213. * NOTICE:
  214. * - It must be called in `CoordinateSystem['create']`, before series filtering.
  215. * - It must be called in `seriesIndex` ascending order (series declaration order).
  216. * i.e., iterated by `ecModel.eachSeries`.
  217. * - Every <axis, series> pair can only call this method once.
  218. *
  219. * @see scaleRawExtentInfoCreate in `scaleRawExtentInfo.ts`
  220. */
  221. export function associateSeriesWithAxis(axis, seriesModel, coordSysType) {
  222. if (!axis) {
  223. return;
  224. }
  225. var ecModel = seriesModel.ecModel;
  226. var ecFullUpdateCache = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel));
  227. var axisModelUid = axis.model.uid;
  228. if (process.env.NODE_ENV !== 'production') {
  229. validateInputAxis(axis);
  230. // - An axis can be associated with multiple `axisStatKey`s. For example, if `axisStatKey`s are
  231. // "candlestick" and "bar", they can be associated with the same "xAxis".
  232. // - Within an individual axis, it is a typically incorrect usage if a <axis, series> pair is
  233. // associated with multiple `perKeyPerAxis`, which may cause repeated calculation and
  234. // performance degradation, had hard to be found without the checking below. For example, If
  235. // `axisStatKey` are "grid-bar" (see `barGrid.ts`) and "polar-bar" (see `barPolar.ts`), and
  236. // a <xAxis-series> pair is wrongly associated with both "polar-bar" and "grid-bar", the
  237. // relevant statistics will be computed twice.
  238. var axSerPairCheck = ecFullUpdateCache.axSerPairCheck || (ecFullUpdateCache.axSerPairCheck = createHashMap());
  239. var pairKey = "" + axisModelUid + AXIS_STAT_KEY_DELIMITER + seriesModel.uid;
  240. assert(!axSerPairCheck.get(pairKey));
  241. axSerPairCheck.set(pairKey, 1);
  242. }
  243. var seriesOnAxisMap = ecFullUpdateCache.axSer || (ecFullUpdateCache.axSer = createHashMap());
  244. var seriesListPerAxis = seriesOnAxisMap.get(axisModelUid) || seriesOnAxisMap.set(axisModelUid, []);
  245. if (process.env.NODE_ENV !== 'production') {
  246. var lastSeries = seriesListPerAxis[seriesListPerAxis.length - 1];
  247. if (lastSeries) {
  248. // Series order should respect to the input order, since it matters in some cases
  249. // (e.g., see `barGrid.ts` and `barPolar.ts` - ec option declaration order matters).
  250. assert(lastSeries.seriesIndex < seriesModel.seriesIndex);
  251. }
  252. }
  253. seriesListPerAxis.push(seriesModel);
  254. var seriesType = seriesModel.subType;
  255. var isBaseAxis = seriesModel.getBaseAxis() === axis;
  256. var client = clientsForLookup.get(makeClientLookupKey(seriesType, isBaseAxis, coordSysType)) || clientsForLookup.get(makeClientLookupKey(seriesType, isBaseAxis, null));
  257. if (!client) {
  258. return;
  259. }
  260. var keyed = ecFullUpdateCache.keyed || (ecFullUpdateCache.keyed = createHashMap());
  261. var keys = ecFullUpdateCache.keys || (ecFullUpdateCache.keys = createHashMap());
  262. var axisStatKey = client.key;
  263. var perKey = keyed.get(axisStatKey) || keyed.set(axisStatKey, createHashMap());
  264. var perKeyPerAxis = perKey.get(axisModelUid);
  265. if (!perKeyPerAxis) {
  266. perKeyPerAxis = perKey.set(axisModelUid, {
  267. axis: axis,
  268. sers: [],
  269. serByIdx: []
  270. });
  271. // They should only be executed for each <key, axis> pair once:
  272. perKeyPerAxis.metrics = client.getMetrics(axis);
  273. (keys.get(axisModelUid) || keys.set(axisModelUid, [])).push(axisStatKey);
  274. }
  275. // series order should respect to the input order.
  276. perKeyPerAxis.sers.push(seriesModel);
  277. perKeyPerAxis.serByIdx[seriesModel.seriesIndex] = seriesModel;
  278. }
  279. /**
  280. * NOTE: Currently, the scenario is simple enough to look up clients by hash map.
  281. * Otherwise, a caller-provided `filter` may be an alternative if more complex requirements arise.
  282. */
  283. function makeClientLookupKey(seriesType, isBaseAxis, coordSysType) {
  284. return seriesType + AXIS_STAT_KEY_DELIMITER + retrieve2(isBaseAxis, true) + AXIS_STAT_KEY_DELIMITER + (coordSysType || '');
  285. }
  286. /**
  287. * NOTICE: Can only be called in "install" stage.
  288. *
  289. * See `axisSnippets.ts` for some commonly used clients.
  290. */
  291. export function requireAxisStatistics(registers, client) {
  292. var clientKey = makeClientLookupKey(client.seriesType, client.baseAxis, client.coordSysType);
  293. if (process.env.NODE_ENV !== 'production') {
  294. assert(client.seriesType && client.key && !clientsForCheckingStatKey.get(client.key) && !clientsForLookup.get(clientKey)); // More checking is performed in `axSerPairCheck`.
  295. clientsForCheckingStatKey.set(client.key, 1);
  296. }
  297. clientsForLookup.set(clientKey, client);
  298. callOnlyOnce(registers, function () {
  299. registers.registerProcessor(registers.PRIORITY.PROCESSOR.AXIS_STATISTICS, {
  300. // NOTE: Theoretically, `appendData` requires `dirtyOnOverallProgress: true` here to re-calculate them.
  301. // But this OVERALL_STAGE_TASK is applied to all series (no `getTargetSeries` specified),
  302. // `dirtyOnOverallProgress: true` can cause irrelevant series (e.g., series on geo)
  303. // to be re-rendered when `appendData` is called, which cause `appendData` meaningless,
  304. // thereby not setting `dirtyOnOverallProgress: true`.
  305. overallReset: performAxisStatisticsOnOverallReset
  306. });
  307. });
  308. }
  309. var clientsForCheckingStatKey;
  310. if (process.env.NODE_ENV !== 'production') {
  311. clientsForCheckingStatKey = createHashMap();
  312. }
  313. var clientsForLookup = createHashMap();