extended.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.ALL_Q = exports.DEFAULT_Q = void 0;
  4. var util_1 = require("@antv/util");
  5. var pretty_number_1 = require("./pretty-number");
  6. exports.DEFAULT_Q = [1, 5, 2, 2.5, 4, 3];
  7. exports.ALL_Q = [1, 5, 2, 2.5, 4, 3, 1.5, 7, 6, 8, 9];
  8. var eps = Number.EPSILON * 100;
  9. function mod(n, m) {
  10. return ((n % m) + m) % m;
  11. }
  12. function round(n) {
  13. return Math.round(n * 1e12) / 1e12;
  14. }
  15. function simplicity(q, Q, j, lmin, lmax, lstep) {
  16. var n = util_1.size(Q);
  17. var i = util_1.indexOf(Q, q);
  18. var v = 0;
  19. var m = mod(lmin, lstep);
  20. if ((m < eps || lstep - m < eps) && lmin <= 0 && lmax >= 0) {
  21. v = 1;
  22. }
  23. return 1 - i / (n - 1) - j + v;
  24. }
  25. function simplicityMax(q, Q, j) {
  26. var n = util_1.size(Q);
  27. var i = util_1.indexOf(Q, q);
  28. var v = 1;
  29. return 1 - i / (n - 1) - j + v;
  30. }
  31. function density(k, m, dMin, dMax, lMin, lMax) {
  32. var r = (k - 1) / (lMax - lMin);
  33. var rt = (m - 1) / (Math.max(lMax, dMax) - Math.min(dMin, lMin));
  34. return 2 - Math.max(r / rt, rt / r);
  35. }
  36. function densityMax(k, m) {
  37. if (k >= m) {
  38. return 2 - (k - 1) / (m - 1);
  39. }
  40. return 1;
  41. }
  42. function coverage(dMin, dMax, lMin, lMax) {
  43. var range = dMax - dMin;
  44. return 1 - (0.5 * (Math.pow((dMax - lMax), 2) + Math.pow((dMin - lMin), 2))) / Math.pow((0.1 * range), 2);
  45. }
  46. function coverageMax(dMin, dMax, span) {
  47. var range = dMax - dMin;
  48. if (span > range) {
  49. var half = (span - range) / 2;
  50. return 1 - Math.pow(half, 2) / Math.pow((0.1 * range), 2);
  51. }
  52. return 1;
  53. }
  54. function legibility() {
  55. return 1;
  56. }
  57. /**
  58. * An Extension of Wilkinson's Algorithm for Position Tick Labels on Axes
  59. * https://www.yuque.com/preview/yuque/0/2019/pdf/185317/1546999150858-45c3b9c2-4e86-4223-bf1a-8a732e8195ed.pdf
  60. * @param dMin 最小值
  61. * @param dMax 最大值
  62. * @param m tick个数
  63. * @param onlyLoose 是否允许扩展min、max,不绝对强制,例如[3, 97]
  64. * @param Q nice numbers集合
  65. * @param w 四个优化组件的权重
  66. */
  67. function extended(dMin, dMax, n, onlyLoose, Q, w) {
  68. if (n === void 0) { n = 5; }
  69. if (onlyLoose === void 0) { onlyLoose = true; }
  70. if (Q === void 0) { Q = exports.DEFAULT_Q; }
  71. if (w === void 0) { w = [0.25, 0.2, 0.5, 0.05]; }
  72. // 处理小于 0 和小数的 tickCount
  73. var m = n < 0 ? 0 : Math.round(n);
  74. // nan 也会导致异常
  75. if (Number.isNaN(dMin) || Number.isNaN(dMax) || typeof dMin !== 'number' || typeof dMax !== 'number' || !m) {
  76. return {
  77. min: 0,
  78. max: 0,
  79. ticks: [],
  80. };
  81. }
  82. // js 极大值极小值问题,差值小于 1e-15 会导致计算出错
  83. if (dMax - dMin < 1e-15 || m === 1) {
  84. return {
  85. min: dMin,
  86. max: dMax,
  87. ticks: [dMin],
  88. };
  89. }
  90. // js 超大值问题
  91. if (dMax - dMin > 1e148) {
  92. var count = n || 5;
  93. var step_1 = (dMax - dMin) / count;
  94. return {
  95. min: dMin,
  96. max: dMax,
  97. ticks: Array(count).fill(null).map(function (_, idx) {
  98. return pretty_number_1.prettyNumber(dMin + step_1 * idx);
  99. }),
  100. };
  101. }
  102. var best = {
  103. score: -2,
  104. lmin: 0,
  105. lmax: 0,
  106. lstep: 0,
  107. };
  108. var j = 1;
  109. while (j < Infinity) {
  110. for (var i = 0; i < Q.length; i += 1) {
  111. var q = Q[i];
  112. var sm = simplicityMax(q, Q, j);
  113. if (w[0] * sm + w[1] + w[2] + w[3] < best.score) {
  114. j = Infinity;
  115. break;
  116. }
  117. var k = 2;
  118. while (k < Infinity) {
  119. var dm = densityMax(k, m);
  120. if (w[0] * sm + w[1] + w[2] * dm + w[3] < best.score) {
  121. break;
  122. }
  123. var delta = (dMax - dMin) / (k + 1) / j / q;
  124. var z = Math.ceil(Math.log10(delta));
  125. while (z < Infinity) {
  126. var step = j * q * Math.pow(10, z);
  127. var cm = coverageMax(dMin, dMax, step * (k - 1));
  128. if (w[0] * sm + w[1] * cm + w[2] * dm + w[3] < best.score) {
  129. break;
  130. }
  131. var minStart = Math.floor(dMax / step) * j - (k - 1) * j;
  132. var maxStart = Math.ceil(dMin / step) * j;
  133. if (minStart <= maxStart) {
  134. var count = maxStart - minStart;
  135. for (var i_1 = 0; i_1 <= count; i_1 += 1) {
  136. var start = minStart + i_1;
  137. var lMin = start * (step / j);
  138. var lMax = lMin + step * (k - 1);
  139. var lStep = step;
  140. var s = simplicity(q, Q, j, lMin, lMax, lStep);
  141. var c = coverage(dMin, dMax, lMin, lMax);
  142. var g = density(k, m, dMin, dMax, lMin, lMax);
  143. var l = legibility();
  144. var score = w[0] * s + w[1] * c + w[2] * g + w[3] * l;
  145. if (score > best.score && (!onlyLoose || (lMin <= dMin && lMax >= dMax))) {
  146. best.lmin = lMin;
  147. best.lmax = lMax;
  148. best.lstep = lStep;
  149. best.score = score;
  150. }
  151. }
  152. }
  153. z += 1;
  154. }
  155. k += 1;
  156. }
  157. }
  158. j += 1;
  159. }
  160. // 处理精度问题,保证这三个数没有精度问题
  161. var lmax = pretty_number_1.prettyNumber(best.lmax);
  162. var lmin = pretty_number_1.prettyNumber(best.lmin);
  163. var lstep = pretty_number_1.prettyNumber(best.lstep);
  164. // 加 round 是为处理 extended(0.94, 1, 5)
  165. // 保证生成的 tickCount 没有精度问题
  166. var tickCount = Math.floor(round((lmax - lmin) / lstep)) + 1;
  167. var ticks = new Array(tickCount);
  168. // 少用乘法:防止出现 -1.2 + 1.2 * 3 = 2.3999999999999995 的情况
  169. ticks[0] = pretty_number_1.prettyNumber(lmin);
  170. for (var i = 1; i < tickCount; i++) {
  171. ticks[i] = pretty_number_1.prettyNumber(ticks[i - 1] + lstep);
  172. }
  173. return {
  174. min: Math.min(dMin, util_1.head(ticks)),
  175. max: Math.max(dMax, util_1.last(ticks)),
  176. ticks: ticks,
  177. };
  178. }
  179. exports.default = extended;
  180. //# sourceMappingURL=extended.js.map