Skip to content

Commit

Permalink
fix(ticks): tickMethods for tiny numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
pearmini committed Jun 30, 2021
1 parent 5cc43d0 commit 00756cf
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 56 deletions.
6 changes: 5 additions & 1 deletion __tests__/unit/tick-methods/r-pretty.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('rPretty ticks', () => {

it('rPretty for c(0.0002, 0.001)', () => {
const res = rPretty(0.0002, 0.001);
expect(res).toEqual([0.0002, 0.0004, 0.0006, 0.0008, 0.001]);
expect(res).toEqual([0.0002, 0.0004, 0.0006, 0.0008, 0.001, 0.0012]);
});

it('rPretty for c(-5, 605)', () => {
Expand All @@ -65,4 +65,8 @@ describe('rPretty ticks', () => {
// same as d3
expect(scale2).toEqual([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]);
});

it('rPretty for tiny number', () => {
expect(rPretty(9.899999999999999, 9.9)).toStrictEqual([9.899999999999999, 9.899999999999999, 9.9, 9.9]);
});
});
1 change: 1 addition & 0 deletions __tests__/unit/tick-methods/wilkinson-extended.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describe('wilkinson-extended test', () => {
]);
expect(wilkinsonExtended(0, 0.000001, 6)).toStrictEqual([0, 0.0000002, 0.0000004, 0.0000006, 0.0000008, 0.000001]);
expect(wilkinsonExtended(0, 1e-15, 6)).toStrictEqual([0, 2e-16, 4e-16, 6e-16, 8e-16, 1e-15]);
expect(wilkinsonExtended(9.899999999999999, 9.9)).toStrictEqual([9.899999999999999, 9.899999999999999, 9.9, 9.9]);
});

test('precision', () => {
Expand Down
8 changes: 8 additions & 0 deletions __tests__/unit/utils/pretty-number.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { prettyNumber } from '../../../src/utils/pretty-number';

describe('prettyNumber', () => {
test('prettyNumber number', () => {
expect(prettyNumber(1e-16)).toBe(1e-16);
expect(prettyNumber(0.1 + 0.2)).toBe(0.3);
});
});
26 changes: 8 additions & 18 deletions src/tick-methods/r-pretty.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TickMethod } from '../types';
import { prettyNumber } from '../utils/pretty-number';

/**
* 创建分割点
Expand All @@ -10,12 +11,6 @@ import { TickMethod } from '../types';
* @see R pretty https://www.rdocumentation.org/packages/base/versions/3.5.2/topics/pretty
*/
export const rPretty: TickMethod = (min, max, n = 5) => {
const res = {
max: 0,
min: 0,
ticks: [],
};

if (min === max) {
return [min];
}
Expand All @@ -36,7 +31,6 @@ export const rPretty: TickMethod = (min, max, n = 5) => {
// }

const base = 10 ** Math.floor(Math.log10(c));
const toFixed = base < 1 ? Math.ceil(Math.abs(Math.log10(base))) : 0;
let unit = base;
if (2 * base - c < h * (c - unit)) {
unit = 2 * base;
Expand All @@ -50,18 +44,14 @@ export const rPretty: TickMethod = (min, max, n = 5) => {
const nu = Math.ceil(max / unit);
const ns = Math.floor(min / unit);

res.max = Math.max(nu * unit, max);
res.min = Math.min(ns * unit, min);
const hi = Math.max(nu * unit, max);
const lo = Math.min(ns * unit, min);

let x = Number.parseFloat((ns * unit).toFixed(toFixed));
while (x < max) {
res.ticks.push(x);
x += unit;
if (toFixed) {
x = Number.parseFloat(x.toFixed(toFixed));
}
const size = Math.floor((hi - lo) / unit) + 1;
const ticks = new Array(size);
for (let i = 0; i < size; i += 1) {
ticks[i] = prettyNumber(lo + i * unit);
}
res.ticks.push(x);

return res.ticks;
return ticks;
};
64 changes: 27 additions & 37 deletions src/tick-methods/wilkinson-extended.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { indexOf, size } from '@antv/util';
import { TickMethod } from '../types';
import { prettyNumber } from '../utils/pretty-number';

export const DEFAULT_Q = [1, 5, 2, 2.5, 4, 3];

Expand Down Expand Up @@ -60,11 +61,6 @@ function legibility() {
return 1;
}

// 为了解决 js 运算的精度问题
function pretty(n: number) {
return n < 1e-15 ? n : parseFloat(n.toFixed(15));
}

/**
* An Extension of Wilkinson's Algorithm for Position Tick Labels on Axes
* https://www.yuque.com/preview/yuque/0/2019/pdf/185317/1546999150858-45c3b9c2-4e86-4223-bf1a-8a732e8195ed.pdf
Expand Down Expand Up @@ -131,27 +127,26 @@ export const wilkinsonExtended: TickMethod = (
const minStart = Math.floor(dMax / step) * j - (k - 1) * j;
const maxStart = Math.ceil(dMin / step) * j;

if (minStart > maxStart) {
z += 1;
// eslint-disable-next-line no-continue
continue;
}
for (let start = minStart; start <= maxStart; start += 1) {
const lMin = start * (step / j);
const lMax = lMin + step * (k - 1);
const lStep = step;

const s = simplicity(q, Q, j, lMin, lMax, lStep);
const c = coverage(dMin, dMax, lMin, lMax);
const g = density(k, m, dMin, dMax, lMin, lMax);
const l = legibility();

const score = w[0] * s + w[1] * c + w[2] * g + w[3] * l;
if (score > best.score && (!onlyLoose || (lMin <= dMin && lMax >= dMax))) {
best.lmin = lMin;
best.lmax = lMax;
best.lstep = lStep;
best.score = score;
if (minStart <= maxStart) {
const count = maxStart - minStart;
for (let i = 0; i <= count; i += 1) {
const start = minStart + i;
const lMin = start * (step / j);
const lMax = lMin + step * (k - 1);
const lStep = step;

const s = simplicity(q, Q, j, lMin, lMax, lStep);
const c = coverage(dMin, dMax, lMin, lMax);
const g = density(k, m, dMin, dMax, lMin, lMax);
const l = legibility();

const score = w[0] * s + w[1] * c + w[2] * g + w[3] * l;
if (score > best.score && (!onlyLoose || (lMin <= dMin && lMax >= dMax))) {
best.lmin = lMin;
best.lmax = lMax;
best.lstep = lStep;
best.score = score;
}
}
}
z += 1;
Expand All @@ -162,17 +157,12 @@ export const wilkinsonExtended: TickMethod = (
j += 1;
}

let i = 0;

const size = (best.lmax - best.lmin) / best.lstep;

// 步长为浮点数时处理精度
const range = new Array(Math.floor(size));

for (let tick = best.lmin; tick <= best.lmax; tick += best.lstep) {
range[i] = pretty(tick);
i += 1;
const { lmax, lmin, lstep } = best;
const tickCount = Math.floor((lmax - lmin) / lstep) + 1;
const ticks = new Array(tickCount);
for (let i = 0; i < tickCount; i += 1) {
ticks[i] = prettyNumber(lmin + i * lstep);
}

return range;
return ticks;
};
4 changes: 4 additions & 0 deletions src/utils/pretty-number.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// 为了解决 js 运算的精度问题
export function prettyNumber(n: number) {
return n < 1e-15 ? n : parseFloat(n.toFixed(15));
}

0 comments on commit 00756cf

Please sign in to comment.