helper.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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 { getPrecision, round, nice, quantityExponent, mathPow, mathMax, mathRound, mathLog, mathAbs, mathFloor, mathCeil } from '../util/number.js';
  41. import { isValidNumberForExtent } from '../util/model.js';
  42. import { getScaleExtentForTickUnsafe } from './scaleMapper.js';
  43. /**
  44. * See also method `nice` in `src/util/number.ts`.
  45. */
  46. // export function isValueNice(val: number) {
  47. // const exp10 = Math.pow(10, quantityExponent(Math.abs(val)));
  48. // const f = Math.abs(round(val / exp10, 0));
  49. // return f === 0
  50. // || f === 1
  51. // || f === 2
  52. // || f === 3
  53. // || f === 5;
  54. // }
  55. export function isIntervalOrLogScale(scale) {
  56. return isIntervalScale(scale) || isLogScale(scale);
  57. }
  58. export function isIntervalOrTimeScale(scale) {
  59. return isIntervalScale(scale) || isTimeScale(scale);
  60. }
  61. export function isIntervalScale(scale) {
  62. return scale.type === 'interval';
  63. }
  64. export function isTimeScale(scale) {
  65. return scale.type === 'time';
  66. }
  67. export function isLogScale(scale) {
  68. return scale.type === 'log';
  69. }
  70. export function isOrdinalScale(scale) {
  71. return scale.type === 'ordinal';
  72. }
  73. /**
  74. * @param extent Both extent[0] and extent[1] should be valid number.
  75. * Should be extent[0] < extent[1].
  76. * @param splitNumber splitNumber should be >= 1.
  77. */
  78. export function intervalScaleNiceTicks(extent, spanWithBreaks, splitNumber, minInterval, maxInterval) {
  79. var result = {};
  80. var interval = result.interval = nice(spanWithBreaks / splitNumber, true);
  81. if (minInterval != null && interval < minInterval) {
  82. interval = result.interval = minInterval;
  83. }
  84. if (maxInterval != null && interval > maxInterval) {
  85. interval = result.interval = maxInterval;
  86. }
  87. var precision = result.intervalPrecision = getIntervalPrecision(interval);
  88. // Niced extent inside original extent
  89. result.niceTickExtent = [round(mathCeil(extent[0] / interval) * interval, precision), round(mathFloor(extent[1] / interval) * interval, precision)];
  90. return result;
  91. }
  92. /**
  93. * The input `niceInterval` should be generated
  94. * from `nice` method in `src/util/number.ts`, or
  95. * from `increaseInterval` itself.
  96. */
  97. export function increaseInterval(niceInterval) {
  98. var exponent = quantityExponent(niceInterval);
  99. // No rounding error in Math.pow(10, integer).
  100. var exp10 = mathPow(10, exponent);
  101. // Fix IEEE 754 float rounding error
  102. var f = mathRound(niceInterval / exp10);
  103. if (!f) {
  104. f = 1;
  105. } else if (f === 2) {
  106. f = 3;
  107. } else if (f === 3) {
  108. f = 5;
  109. } else {
  110. // f is 1 or 5
  111. f *= 2;
  112. }
  113. // Fix IEEE 754 float rounding error
  114. return round(f * exp10, -exponent);
  115. }
  116. export function getIntervalPrecision(niceInterval) {
  117. // Tow more digital for tick.
  118. // NOTE: `2` was introduced in commit `af2a2a9f6303081d7c3b52f0a38add07b4c6e0c7`;
  119. // it works on "nice" interval, but seems not necessarily mathematically required.
  120. return getPrecision(niceInterval) + 2;
  121. }
  122. /**
  123. * NOTE:
  124. * - If `val` is `NaN`, return `NaN`.
  125. * - If `val` is `0`, return `-Infinity`.
  126. * - If `val` is negative, return `NaN`.
  127. *
  128. * @see {DataStore#getDataExtent} It handles non-positive values for logarithm scale.
  129. */
  130. export function logScaleLogTick(val, base) {
  131. // NOTE:
  132. // - rounding error may happen above, typically expecting `log10(1000)` but actually
  133. // getting `2.9999999999999996`, but generally it does not matter since they are not
  134. // used to display.
  135. // - Consider backward compatibility and other log bases, do not use `Math.log10`.
  136. return mathLog(val) / mathLog(base);
  137. }
  138. /**
  139. * Cumulative rounding errors cause the logarithm operation to become non-invertible by simply exponentiation.
  140. * - `Math.pow(10, integer)` itself has no rounding error. But,
  141. * - If `linearTickVal` is generated internally by `calcNiceTicks`, it may be still "not nice" (not an integer)
  142. * when it is `extent[i]`.
  143. * - If `linearTickVal` is generated outside (e.g., by `scaleCalcAlign`) and set by `setExtent`,
  144. * `logScaleLogTick` may already have introduced rounding errors even for "nice" values.
  145. * But invertible is required when the original `extent[i]` need to be respected, or "nice" ticks need to be
  146. * displayed instead of something like `5.999999999999999`, which is addressed in this function.
  147. * See also `#4158`.
  148. *
  149. * [CAUTION]:
  150. * Monotonicity may be broken on extent ends - callers must make sure it does not matter.
  151. */
  152. export function logScalePowTick(
  153. // `tickVal` should be in the linear space.
  154. linearTickVal, base, opt) {
  155. var lookup = opt && opt.lookup;
  156. if (lookup) {
  157. for (var i = 0; i < lookup.from.length; i++) {
  158. if (linearTickVal === lookup.from[i]) {
  159. return lookup.to[i];
  160. }
  161. }
  162. }
  163. return mathPow(base, linearTickVal);
  164. }
  165. /**
  166. * For `IntervalScale`, convert `rawExtent` to:
  167. * - Be no non-finite number.
  168. * - Be `extent[0] < extent[1]`- no equal; otherwise, additional handling is required
  169. * in "nice" and "align" ticks.
  170. */
  171. export function intervalScaleEnsureValidExtent(rawExtent, fixMinMax, rawExtentResult) {
  172. var extent = rawExtent.slice();
  173. // PENDING:
  174. // This implementation is not rigorous, but has long been in use.
  175. // If extent start and end are same, expand them
  176. if (extent[0] === extent[1]) {
  177. // If `containShape`, the extent must be evenly distributed to both sides;
  178. // otherwise, shape (e.g., bars) may be overflow and clipped.
  179. var containShapeRequired = rawExtentResult && rawExtentResult.ctnShp;
  180. if (extent[0] !== 0) {
  181. // Expand extent
  182. // Note that extents can be both negative. See #13154
  183. var expandSize = mathAbs(extent[0]);
  184. // In the fowllowing case
  185. // Axis has been fixed max 100
  186. // Plus data are all 100 and axis extent are [100, 100].
  187. // Extend to the both side will cause expanded max is larger than fixed max.
  188. // So only expand to the smaller side.
  189. if (!fixMinMax[1]) {
  190. extent[1] += expandSize / 2;
  191. extent[0] -= expandSize / 2;
  192. } else {
  193. extent[0] -= expandSize / 2;
  194. }
  195. } else {
  196. if (containShapeRequired) {
  197. extent[0] = -1;
  198. extent[1] = 1;
  199. } else {
  200. extent[1] = 1;
  201. }
  202. }
  203. }
  204. // For example, if there are no series data, extent may be `[Infinity, -Infinity]` here.
  205. if (!isValidNumberForExtent(extent[0]) || !isValidNumberForExtent(extent[1])) {
  206. extent[0] = 0;
  207. extent[1] = 1;
  208. }
  209. if (extent[1] < extent[0]) {
  210. extent.reverse();
  211. }
  212. return extent;
  213. }
  214. export function extentDiffers(extent1, extent2) {
  215. return [extent1[0] !== extent2[0], extent1[1] !== extent2[1]];
  216. }
  217. export function ensureValidSplitNumber(rawSplitNumber, defaultSplitNumber) {
  218. rawSplitNumber = rawSplitNumber || defaultSplitNumber;
  219. return mathRound(mathMax(rawSplitNumber, 1));
  220. }
  221. /**
  222. * NOTE: The result can have only one item, e.g., when `extent[0] === extent[1]`
  223. * and `categoryInterval === 0`.
  224. */
  225. export function ordinalScaleCreateTicks(ordinalScale,
  226. // `categoryInterval` is the number part of `CategoryTickLabelSplitIntervalOption`.
  227. categoryInterval, addItem) {
  228. var extent = getScaleExtentForTickUnsafe(ordinalScale);
  229. var startTick = extent[0];
  230. var tickCount = ordinalScale.count();
  231. var step = Math.max((categoryInterval || 0) + 1, 1);
  232. // Calculate start tick based on zero if possible to keep label consistent
  233. // while zooming and moving while interval > 0. Otherwise the selection
  234. // of displayable ticks and symbols probably keep changing.
  235. if (startTick !== 0 && step > 1 && tickCount / step > 2) {
  236. startTick = Math.round(Math.ceil(startTick / step) * step);
  237. }
  238. // min max labels may be excluded if `startTick > 0`, but they should be always
  239. // included and the label display strategy is adopted uniformly later in `AxisBuilder`.
  240. if (startTick !== extent[0]) {
  241. addItemInternally(extent[0], true, true);
  242. }
  243. var tickValue = startTick;
  244. for (; tickValue <= extent[1]; tickValue += step) {
  245. addItemInternally(tickValue, false, tickValue === extent[0] || tickValue === extent[1]);
  246. }
  247. if (tickValue - step !== extent[1]) {
  248. addItemInternally(extent[1], true, true);
  249. }
  250. function addItemInternally(tickValue, offInterval, isExtentBoundary) {
  251. addItem({
  252. value: tickValue,
  253. offInterval: offInterval
  254. }, isExtentBoundary);
  255. }
  256. }