UNPKG

5.88 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.wilkinsonExtended = exports.ALL_Q = exports.DEFAULT_Q = void 0;
4const util_1 = require("@antv/util");
5const pretty_number_1 = require("../utils/pretty-number");
6exports.DEFAULT_Q = [1, 5, 2, 2.5, 4, 3];
7exports.ALL_Q = [1, 5, 2, 2.5, 4, 3, 1.5, 7, 6, 8, 9];
8const eps = Number.EPSILON * 100;
9function mod(n, m) {
10 return ((n % m) + m) % m;
11}
12function round(n) {
13 return Math.round(n * 1e12) / 1e12;
14}
15function simplicity(q, Q, j, lmin, lmax, lstep) {
16 const n = (0, util_1.size)(Q);
17 const i = (0, util_1.indexOf)(Q, q);
18 let v = 0;
19 const 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}
25function simplicityMax(q, Q, j) {
26 const n = (0, util_1.size)(Q);
27 const i = (0, util_1.indexOf)(Q, q);
28 const v = 1;
29 return 1 - i / (n - 1) - j + v;
30}
31function density(k, m, dMin, dMax, lMin, lMax) {
32 const r = (k - 1) / (lMax - lMin);
33 const rt = (m - 1) / (Math.max(lMax, dMax) - Math.min(dMin, lMin));
34 return 2 - Math.max(r / rt, rt / r);
35}
36function densityMax(k, m) {
37 if (k >= m) {
38 return 2 - (k - 1) / (m - 1);
39 }
40 return 1;
41}
42function coverage(dMin, dMax, lMin, lMax) {
43 const range = dMax - dMin;
44 return 1 - (0.5 * ((dMax - lMax) ** 2 + (dMin - lMin) ** 2)) / (0.1 * range) ** 2;
45}
46function coverageMax(dMin, dMax, span) {
47 const range = dMax - dMin;
48 if (span > range) {
49 const half = (span - range) / 2;
50 return 1 - half ** 2 / (0.1 * range) ** 2;
51 }
52 return 1;
53}
54function 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 */
67const wilkinsonExtended = (dMin, dMax, n = 5, onlyLoose = true, Q = exports.DEFAULT_Q, w = [0.25, 0.2, 0.5, 0.05]) => {
68 const m = n < 0 ? 0 : Math.round(n);
69 // nan 也会导致异常
70 if (Number.isNaN(dMin) || Number.isNaN(dMax) || typeof dMin !== 'number' || typeof dMax !== 'number' || !m) {
71 return [];
72 }
73 // js 极大值极小值问题,差值小于 1e-15 会导致计算出错
74 if (dMax - dMin < 1e-15 || m === 1) {
75 return [dMin];
76 }
77 const best = {
78 score: -2,
79 lmin: 0,
80 lmax: 0,
81 lstep: 0,
82 };
83 let j = 1;
84 while (j < Infinity) {
85 // for (const q of Q)
86 for (let i = 0; i < Q.length; i += 1) {
87 const q = Q[i];
88 const sm = simplicityMax(q, Q, j);
89 if (w[0] * sm + w[1] + w[2] + w[3] < best.score) {
90 j = Infinity;
91 break;
92 }
93 let k = 2;
94 while (k < Infinity) {
95 const dm = densityMax(k, m);
96 if (w[0] * sm + w[1] + w[2] * dm + w[3] < best.score) {
97 break;
98 }
99 const delta = (dMax - dMin) / (k + 1) / j / q;
100 let z = Math.ceil(Math.log10(delta));
101 while (z < Infinity) {
102 const step = j * q * 10 ** z;
103 const cm = coverageMax(dMin, dMax, step * (k - 1));
104 if (w[0] * sm + w[1] * cm + w[2] * dm + w[3] < best.score) {
105 break;
106 }
107 const minStart = Math.floor(dMax / step) * j - (k - 1) * j;
108 const maxStart = Math.ceil(dMin / step) * j;
109 if (minStart <= maxStart) {
110 const count = maxStart - minStart;
111 for (let i = 0; i <= count; i += 1) {
112 const start = minStart + i;
113 const lMin = start * (step / j);
114 const lMax = lMin + step * (k - 1);
115 const lStep = step;
116 const s = simplicity(q, Q, j, lMin, lMax, lStep);
117 const c = coverage(dMin, dMax, lMin, lMax);
118 const g = density(k, m, dMin, dMax, lMin, lMax);
119 const l = legibility();
120 const score = w[0] * s + w[1] * c + w[2] * g + w[3] * l;
121 if (score > best.score && (!onlyLoose || (lMin <= dMin && lMax >= dMax))) {
122 best.lmin = lMin;
123 best.lmax = lMax;
124 best.lstep = lStep;
125 best.score = score;
126 }
127 }
128 }
129 z += 1;
130 }
131 k += 1;
132 }
133 }
134 j += 1;
135 }
136 // 处理精度问题,保证这三个数没有精度问题
137 const lmax = (0, pretty_number_1.prettyNumber)(best.lmax);
138 const lmin = (0, pretty_number_1.prettyNumber)(best.lmin);
139 const lstep = (0, pretty_number_1.prettyNumber)(best.lstep);
140 // 加 round 是为处理 extended(0.94, 1, 5)
141 // 保证生成的 tickCount 没有精度问题
142 const tickCount = Math.floor(round((lmax - lmin) / lstep)) + 1;
143 const ticks = new Array(tickCount);
144 // 少用乘法:防止出现 -1.2 + 1.2 * 3 = 2.3999999999999995 的情况
145 ticks[0] = (0, pretty_number_1.prettyNumber)(lmin);
146 for (let i = 1; i < tickCount; i += 1) {
147 ticks[i] = (0, pretty_number_1.prettyNumber)(ticks[i - 1] + lstep);
148 }
149 return ticks;
150};
151exports.wilkinsonExtended = wilkinsonExtended;
152//# sourceMappingURL=wilkinson-extended.js.map
\No newline at end of file