Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optional log base for ticks functions #233

Closed
wants to merge 10 commits into from
18 changes: 9 additions & 9 deletions src/ticks.js
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other implicit dependencies on base 10 (e.g., 2 and 5 are factors of 10, and 10 appears elsewhere in code)

Could you point me to the other parts of the code that would need to be addressed?

Expand Up @@ -2,7 +2,7 @@ var e10 = Math.sqrt(50),
e5 = Math.sqrt(10),
e2 = Math.sqrt(2);

export default function ticks(start, stop, count) {
export default function ticks(start, stop, count, base = 10) {
var reverse,
i = -1,
n,
Expand All @@ -12,7 +12,7 @@ export default function ticks(start, stop, count) {
stop = +stop, start = +start, count = +count;
if (start === stop && count > 0) return [start];
if (reverse = stop < start) n = start, start = stop, stop = n;
if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
if ((step = tickIncrement(start, stop, count, base)) === 0 || !isFinite(step)) return [];

if (step > 0) {
let r0 = Math.round(start / step), r1 = Math.round(stop / step);
Expand All @@ -34,18 +34,18 @@ export default function ticks(start, stop, count) {
return ticks;
}

export function tickIncrement(start, stop, count) {
export function tickIncrement(start, stop, count, base = 10) {
var step = (stop - start) / Math.max(0, count),
power = Math.floor(Math.log(step) / Math.LN10),
error = step / Math.pow(10, power);
power = Math.floor(Math.log(step) / Math.log(base) + Number.EPSILON),
error = step / Math.pow(base, power);
return power >= 0
? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
: -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(base, power)
: -Math.pow(base, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
}

export function tickStep(start, stop, count) {
export function tickStep(start, stop, count, base = 10) {
var step0 = Math.abs(stop - start) / Math.max(0, count),
step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
step1 = Math.pow(base, Math.floor(Math.log(step0) / Math.log(base) + Number.EPSILON)),
error = step0 / step1;
if (error >= e10) step1 *= 10;
else if (error >= e5) step1 *= 5;
Expand Down
33 changes: 33 additions & 0 deletions test/tickIncrement-test.js
Expand Up @@ -60,3 +60,36 @@ it("tickIncrement(start, stop, count) returns approximately count + 1 tickIncrem
assert.strictEqual(tickIncrement(-10, 10, 2), 10);
assert.strictEqual(tickIncrement(-10, 10, 1), 20);
});

it("tickIncrement(start, stop, count, base) returns approximately count + 1 tickIncrement when start < stop", () => {
assert.strictEqual(tickIncrement( 0, 1, 10, 2), -8);
assert.strictEqual(tickIncrement( 0, 1, 9, 2), -8);
assert.strictEqual(tickIncrement( 0, 1, 8, 2), -8);
assert.strictEqual(tickIncrement( 0, 1, 7, 2), -8);
assert.strictEqual(tickIncrement( 0, 1, 6, 2), -8);
assert.strictEqual(tickIncrement( 0, 1, 5, 2), -4);
assert.strictEqual(tickIncrement( 0, 1, 4, 2), -4);
assert.strictEqual(tickIncrement( 0, 1, 3, 2), -4);
assert.strictEqual(tickIncrement( 0, 1, 2, 2), -2);
assert.strictEqual(tickIncrement( 0, 1, 1, 2), 1);
assert.strictEqual(tickIncrement( 0, 10, 10, 2), 1);
assert.strictEqual(tickIncrement( 0, 10, 9, 2), 1);
assert.strictEqual(tickIncrement( 0, 10, 8, 2), 1);
assert.strictEqual(tickIncrement( 0, 10, 7, 2), 2);
assert.strictEqual(tickIncrement( 0, 10, 6, 2), 2);
assert.strictEqual(tickIncrement( 0, 10, 5, 2), 2);
assert.strictEqual(tickIncrement( 0, 10, 4, 2), 2);
assert.strictEqual(tickIncrement( 0, 10, 3, 2), 4);
assert.strictEqual(tickIncrement( 0, 10, 2, 2), 4);
assert.strictEqual(tickIncrement( 0, 10, 1, 2), 8);
assert.strictEqual(tickIncrement(-10, 10, 10, 2), 2);
assert.strictEqual(tickIncrement(-10, 10, 9, 2), 2);
assert.strictEqual(tickIncrement(-10, 10, 8, 2), 2);
assert.strictEqual(tickIncrement(-10, 10, 7, 2), 4);
assert.strictEqual(tickIncrement(-10, 10, 6, 2), 4);
assert.strictEqual(tickIncrement(-10, 10, 5, 2), 4);
assert.strictEqual(tickIncrement(-10, 10, 4, 2), 4);
assert.strictEqual(tickIncrement(-10, 10, 3, 2), 8);
assert.strictEqual(tickIncrement(-10, 10, 2, 2), 8);
assert.strictEqual(tickIncrement(-10, 10, 1, 2), 16);
});
66 changes: 66 additions & 0 deletions test/tickStep-test.js
Expand Up @@ -93,3 +93,69 @@ it("tickStep(start, stop, count) returns -tickStep(stop, start, count)", () => {
assert.strictEqual(tickStep(-10, 10, 2), -tickStep(10, -10, 2));
assert.strictEqual(tickStep(-10, 10, 1), -tickStep(10, -10, 1));
});

it("tickStep(start, stop, count) with base 2 returns approximately count + 1 tickStep when start < stop", () => {
assert.strictEqual(tickStep( 0, 1, 10, 2), 0.125);
assert.strictEqual(tickStep( 0, 1, 9, 2), 0.125);
assert.strictEqual(tickStep( 0, 1, 8, 2), 0.125);
assert.strictEqual(tickStep( 0, 1, 7, 2), 0.125);
assert.strictEqual(tickStep( 0, 1, 6, 2), 0.125);
assert.strictEqual(tickStep( 0, 1, 5, 2), 0.25);
assert.strictEqual(tickStep( 0, 1, 4, 2), 0.25);
assert.strictEqual(tickStep( 0, 1, 3, 2), 0.25);
assert.strictEqual(tickStep( 0, 1, 2, 2), 0.5);
assert.strictEqual(tickStep( 0, 1, 1, 2), 1);
assert.strictEqual(tickStep( 0, 10, 10, 2), 1);
assert.strictEqual(tickStep( 0, 10, 9, 2), 1);
assert.strictEqual(tickStep( 0, 10, 8, 2), 1);
assert.strictEqual(tickStep( 0, 10, 7, 2), 2);
assert.strictEqual(tickStep( 0, 10, 6, 2), 2);
assert.strictEqual(tickStep( 0, 10, 5, 2), 2);
assert.strictEqual(tickStep( 0, 10, 4, 2), 2);
assert.strictEqual(tickStep( 0, 10, 3, 2), 4);
assert.strictEqual(tickStep( 0, 10, 2, 2), 4);
assert.strictEqual(tickStep( 0, 10, 1, 2), 8);
assert.strictEqual(tickStep(-10, 10, 10, 2), 2);
assert.strictEqual(tickStep(-10, 10, 9, 2), 2);
assert.strictEqual(tickStep(-10, 10, 8, 2), 2);
assert.strictEqual(tickStep(-10, 10, 7, 2), 4);
assert.strictEqual(tickStep(-10, 10, 6, 2), 4);
assert.strictEqual(tickStep(-10, 10, 5, 2), 4);
assert.strictEqual(tickStep(-10, 10, 4, 2), 4);
assert.strictEqual(tickStep(-10, 10, 3, 2), 8);
assert.strictEqual(tickStep(-10, 10, 2, 2), 8);
assert.strictEqual(tickStep(-10, 10, 1, 2), 16);
});

it("tickStep(start, stop, count, base) with base 2 returns -tickStep(stop, start, count)", () => {
assert.strictEqual(tickStep( 0, 1, 10, 2), -tickStep( 1, 0, 10, 2));
assert.strictEqual(tickStep( 0, 1, 9, 2), -tickStep( 1, 0, 9, 2));
assert.strictEqual(tickStep( 0, 1, 8, 2), -tickStep( 1, 0, 8, 2));
assert.strictEqual(tickStep( 0, 1, 7, 2), -tickStep( 1, 0, 7, 2));
assert.strictEqual(tickStep( 0, 1, 6, 2), -tickStep( 1, 0, 6, 2));
assert.strictEqual(tickStep( 0, 1, 5, 2), -tickStep( 1, 0, 5, 2));
assert.strictEqual(tickStep( 0, 1, 4, 2), -tickStep( 1, 0, 4, 2));
assert.strictEqual(tickStep( 0, 1, 3, 2), -tickStep( 1, 0, 3, 2));
assert.strictEqual(tickStep( 0, 1, 2, 2), -tickStep( 1, 0, 2, 2));
assert.strictEqual(tickStep( 0, 1, 1, 2), -tickStep( 1, 0, 1, 2));
assert.strictEqual(tickStep( 0, 10, 10, 2), -tickStep(10, 0, 10, 2));
assert.strictEqual(tickStep( 0, 10, 9, 2), -tickStep(10, 0, 9, 2));
assert.strictEqual(tickStep( 0, 10, 8, 2), -tickStep(10, 0, 8, 2));
assert.strictEqual(tickStep( 0, 10, 7, 2), -tickStep(10, 0, 7, 2));
assert.strictEqual(tickStep( 0, 10, 6, 2), -tickStep(10, 0, 6, 2));
assert.strictEqual(tickStep( 0, 10, 5, 2), -tickStep(10, 0, 5, 2));
assert.strictEqual(tickStep( 0, 10, 4, 2), -tickStep(10, 0, 4, 2));
assert.strictEqual(tickStep( 0, 10, 3, 2), -tickStep(10, 0, 3, 2));
assert.strictEqual(tickStep( 0, 10, 2, 2), -tickStep(10, 0, 2, 2));
assert.strictEqual(tickStep( 0, 10, 1, 2), -tickStep(10, 0, 1, 2));
assert.strictEqual(tickStep(-10, 10, 10, 2), -tickStep(10, -10, 10, 2));
assert.strictEqual(tickStep(-10, 10, 9, 2), -tickStep(10, -10, 9, 2));
assert.strictEqual(tickStep(-10, 10, 8, 2), -tickStep(10, -10, 8, 2));
assert.strictEqual(tickStep(-10, 10, 7, 2), -tickStep(10, -10, 7, 2));
assert.strictEqual(tickStep(-10, 10, 6, 2), -tickStep(10, -10, 6, 2));
assert.strictEqual(tickStep(-10, 10, 5, 2), -tickStep(10, -10, 5, 2));
assert.strictEqual(tickStep(-10, 10, 4, 2), -tickStep(10, -10, 4, 2));
assert.strictEqual(tickStep(-10, 10, 3, 2), -tickStep(10, -10, 3, 2));
assert.strictEqual(tickStep(-10, 10, 2, 2), -tickStep(10, -10, 2, 2));
assert.strictEqual(tickStep(-10, 10, 1, 2), -tickStep(10, -10, 1, 2));
});
7 changes: 7 additions & 0 deletions test/ticks-test.js
Expand Up @@ -109,3 +109,10 @@ it("ticks(start, stop, count) returns the reverse of ticks(stop, start, count)",
it("ticks(start, stop, count) handles precision problems", () => {
assert.deepStrictEqual(ticks(0.98, 1.14, 10), [0.98, 1, 1.02, 1.04, 1.06, 1.08, 1.1, 1.12, 1.14]);
});

it("ticks(start, stop, count, base) returns ticks in base 2", () => {
assert.deepStrictEqual(ticks(0, 10, 5, 2), [0, 2, 4, 6, 8, 10]);
assert.deepStrictEqual(ticks(0, 1024, 10, 2), [0, 128, 256, 384, 512, 640, 768, 896, 1024]);
assert.deepStrictEqual(ticks(-1024, 1024, 20, 2), [-1024, -896, -768, -640, -512, -384, -256, -128, 0, 128, 256, 384, 512, 640, 768, 896, 1024]);
assert.deepStrictEqual(ticks(0, 2000, 5, 2), [0, 512, 1024, 1536]);
});