number.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. /**
  2. * @fileOverview 自动计算数字坐标轴
  3. * @author dxq613@gmail.com
  4. */
  5. const isNil = require('@antv/util/lib/type/is-nil');
  6. const isNumber = require('@antv/util/lib/type/is-number');
  7. const AutoUtil = require('./util');
  8. const MIN_COUNT = 5;
  9. const MAX_COUNT = 7;
  10. const SNAP_COUNT_ARRAY = [ 0, 1, 1.2, 1.5, 1.6, 2, 2.2, 2.4, 2.5, 3, 4, 5, 6, 7.5, 8, 10 ];
  11. const SNAP_ARRAY = [ 0, 1, 2, 4, 5, 10 ];
  12. const EPS = 1e-12;
  13. module.exports = function(info) {
  14. let min = info.min;
  15. let max = info.max;
  16. let interval = info.interval;
  17. const minTickInterval = info.minTickInterval;
  18. const ticks = [];
  19. const minCount = info.minCount || MIN_COUNT;
  20. const maxCount = info.maxCount || MAX_COUNT;
  21. const isFixedCount = minCount === maxCount; // 是否限定死了个数
  22. const minLimit = isNil(info.minLimit) ? -Infinity : info.minLimit; // 限定的最小值
  23. const maxLimit = isNil(info.maxLimit) ? Infinity : info.maxLimit; // 限定最大值
  24. let avgCount = (minCount + maxCount) / 2;
  25. let count = avgCount;
  26. // 用户传入的逼近数组
  27. const snapArray = info.snapArray ? info.snapArray : (isFixedCount ? SNAP_COUNT_ARRAY : SNAP_ARRAY);
  28. // 如果限定大小范围,同时大小范围等于用户传入的范围,同时限定了个数,interval 按照个数均分
  29. if (min === minLimit && max === maxLimit && isFixedCount) {
  30. interval = (max - min) / (count - 1);
  31. }
  32. if (isNil(min)) {
  33. min = 0;
  34. }
  35. if (isNil(max)) {
  36. max = 0;
  37. }
  38. if (Math.abs(max - min) < EPS) {
  39. if (min === 0) {
  40. max = 1;
  41. } else {
  42. if (min > 0) {
  43. min = 0;
  44. } else {
  45. max = 0;
  46. }
  47. }
  48. if (max - min < 5 && !interval && max - min >= 1) {
  49. interval = 1;
  50. }
  51. }
  52. if (isNil(interval)) {
  53. // 计算间距
  54. const temp = (max - min) / (avgCount - 1);
  55. interval = AutoUtil.snapFactorTo(temp, snapArray, 'ceil');
  56. if (maxCount !== minCount) {
  57. count = parseInt((max - min) / interval, 10);
  58. if (count > maxCount) {
  59. count = maxCount;
  60. }
  61. if (count < minCount) {
  62. count = minCount;
  63. }
  64. // 不确定tick的个数时,使得tick偏小
  65. interval = AutoUtil.snapFactorTo((max - min) / (count - 1), snapArray, 'floor');
  66. }
  67. }
  68. // interval should not be less than minTickInterval
  69. if (isNumber(minTickInterval) && interval < minTickInterval) {
  70. interval = minTickInterval;
  71. }
  72. if (info.interval || maxCount !== minCount) {
  73. // 校正 max 和 min
  74. max = Math.min(AutoUtil.snapMultiple(max, interval, 'ceil'), maxLimit); // 向上逼近
  75. min = Math.max(AutoUtil.snapMultiple(min, interval, 'floor'), minLimit); // 向下逼近
  76. count = Math.round((max - min) / interval);
  77. min = AutoUtil.fixedBase(min, interval);
  78. max = AutoUtil.fixedBase(max, interval);
  79. } else {
  80. avgCount = parseInt(avgCount, 10); // 取整
  81. const avg = (max + min) / 2;
  82. const avgTick = AutoUtil.snapMultiple(avg, interval, 'ceil');
  83. const sideCount = Math.floor((avgCount - 2) / 2);
  84. let maxTick = avgTick + sideCount * interval;
  85. let minTick;
  86. if (avgCount % 2 === 0) {
  87. minTick = avgTick - sideCount * interval;
  88. } else {
  89. minTick = avgTick - (sideCount + 1) * interval;
  90. }
  91. let prevMaxTick = null;
  92. // 如果减去intervl, fixBase后,新的minTick没有大于之前的值,就退出,防止死循环
  93. while (maxTick < max && (prevMaxTick === null || maxTick > prevMaxTick)) { // 保证计算出来的刻度最大值 maxTick 不小于数据最大值 max
  94. prevMaxTick = maxTick;
  95. maxTick = AutoUtil.fixedBase(maxTick + interval, interval);
  96. }
  97. let prevMinTick = null;
  98. // 如果减去intervl, fixBase后,新的minTick没有小于之前的值,就退出,防止死循环
  99. while (minTick > min && (prevMinTick === null || minTick < prevMinTick)) { // 保证计算出来的刻度最小值 minTick 不小于数据最大值 min
  100. prevMinTick = minTick;
  101. minTick = AutoUtil.fixedBase(minTick - interval, interval); // 防止超常浮点数计算问题
  102. }
  103. max = maxTick;
  104. min = minTick;
  105. }
  106. max = Math.min(max, maxLimit);
  107. min = Math.max(min, minLimit);
  108. ticks.push(min);
  109. for (let i = 1; i < count; i++) {
  110. const tickValue = AutoUtil.fixedBase(interval * i + min, interval);
  111. if (tickValue < max) {
  112. ticks.push(tickValue);
  113. }
  114. }
  115. if (ticks[ticks.length - 1] < max) {
  116. ticks.push(max);
  117. }
  118. return {
  119. min,
  120. max,
  121. interval,
  122. count,
  123. ticks
  124. };
  125. };