Skip to content

Commit b13f8b9

Browse files
committed
First pass at #768, works for linear-integer
TODO * should trigger on the interval option being integer * band-integer (e.g., yearly-request) * find a nicer way to pass the information up * test for the [-10, 10] cases—maybe more what about 10-30?
1 parent 28b1a4c commit b13f8b9

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

src/marks/axis.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ function axisTextKy(
381381
dx: anchor === "left" ? +dx - tickSize - tickPadding + +insetLeft : +dx + +tickSize + +tickPadding - insetRight
382382
},
383383
function (scale, ticks, channels) {
384+
if (scale.type === "linear-integer" && tickFormat === undefined) tickFormat = "d";
384385
if (fontVariant === undefined) this.fontVariant = inferFontVariant(scale);
385386
if (text === undefined) channels.text = inferTextChannel(scale, ticks, tickFormat);
386387
}
@@ -428,6 +429,7 @@ function axisTextKx(
428429
dy: anchor === "bottom" ? +dy + +tickSize + +tickPadding - insetBottom : +dy - tickSize - tickPadding + +insetTop
429430
},
430431
function (scale, ticks, channels) {
432+
if (scale.type === "linear-integer" && tickFormat === undefined) tickFormat = "d";
431433
if (fontVariant === undefined) this.fontVariant = inferFontVariant(scale);
432434
if (text === undefined) channels.text = inferTextChannel(scale, ticks, tickFormat);
433435
}
@@ -505,12 +507,17 @@ function axisMark(mark, k, ariaLabel, data, options, initialize) {
505507
if (!scale) throw new Error(`missing scale: ${k}`);
506508
let {ticks, tickSpacing, interval} = options;
507509
if (isTemporalScale(scale) && typeof ticks === "string") (interval = ticks), (ticks = undefined);
510+
511+
const tickFunction =
512+
scale.type === "linear-integer"
513+
? (ticks) => scale.ticks(ticks).filter((d) => d === Math.floor(d))
514+
: scale.ticks;
508515
if (data == null) {
509516
if (isIterable(ticks)) {
510517
data = arrayify(ticks);
511518
} else if (scale.ticks) {
512519
if (ticks !== undefined) {
513-
data = scale.ticks(ticks);
520+
data = tickFunction(ticks);
514521
} else {
515522
interval = maybeInterval(interval === undefined ? scale.interval : interval, scale.type);
516523
if (interval !== undefined) {
@@ -523,7 +530,7 @@ function axisMark(mark, k, ariaLabel, data, options, initialize) {
523530
} else {
524531
const [min, max] = extent(scale.range());
525532
ticks = (max - min) / (tickSpacing === undefined ? (k === "x" ? 80 : 35) : tickSpacing);
526-
data = scale.ticks(ticks);
533+
data = tickFunction(ticks);
527534
}
528535
}
529536
} else {

src/scales.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function Scales(
6060
const scales = {};
6161
for (const [key, channels] of channelsByScale) {
6262
const scaleOptions = options[key];
63-
const scale = Scale(key, channels, {
63+
const mutableOptions = {
6464
round: registry.get(key) === position ? round : undefined, // only for position
6565
nice,
6666
clamp,
@@ -69,7 +69,8 @@ export function Scales(
6969
padding,
7070
projection,
7171
...scaleOptions
72-
});
72+
};
73+
const scale = Scale(key, channels, mutableOptions);
7374
if (scale) {
7475
// populate generic scale options (percent, transform, insets)
7576
let {
@@ -300,6 +301,7 @@ function Scale(key, channels = [], options = {}) {
300301
case "cyclical":
301302
case "sequential":
302303
case "linear":
304+
case "linear-integer":
303305
case "sqrt":
304306
case "threshold":
305307
case "quantile":
@@ -342,6 +344,7 @@ function Scale(key, channels = [], options = {}) {
342344
case "cyclical":
343345
case "sequential":
344346
case "linear":
347+
case "linear-integer":
345348
return ScaleLinear(key, channels, options);
346349
case "sqrt":
347350
return ScaleSqrt(key, channels, options);
@@ -446,7 +449,24 @@ function inferScaleType(key, channels, {type, domain, range, scheme, pivot, proj
446449
if (values.some(isOrdinal)) return asOrdinalType(kind);
447450
if (values.some(isTemporal)) return "utc";
448451
if (kind === color && (pivot != null || isDivergingScheme(scheme))) return "diverging";
449-
return "linear";
452+
453+
// If a large sample of values reveals integers all in [1500, 2200]—or all in
454+
// [-10, 10]—, we prefer an "integer" formatter without commas nor decimal
455+
// ticks.
456+
let testYear = true;
457+
let testSmallInt = true;
458+
for (const value of values) {
459+
let N = 40;
460+
for (const v of value) {
461+
if (v != null) {
462+
if (v !== Math.floor(v)) return "linear";
463+
testYear &= v < 1500 || v > 2200;
464+
testSmallInt &= v < -10 || v > 10;
465+
if (N-- < 0) continue;
466+
}
467+
}
468+
}
469+
return testYear || testSmallInt ? "linear-integer" : "linear";
450470
}
451471

452472
// Positional scales default to a point scale instead of an ordinal scale.

test/plots/energy-production.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ export default async function () {
3232
.map((d) => ({...d, Year: +d.YYYYMM.slice(0, 4), Value: +d.Value}));
3333
return Plot.plot({
3434
x: {
35-
tickFormat: "d",
3635
label: null
3736
},
3837
y: {

0 commit comments

Comments
 (0)