Skip to content

Commit 7dcc35a

Browse files
committed
fix(stack): match d3 offset none baseline for value order
Narrow STACK_ORDER index type for svelte-check. Use single cumulative baseline (d3 stackOffsetNone) instead of py/ny split when offset is none.
1 parent 93d2153 commit 7dcc35a

2 files changed

Lines changed: 31 additions & 13 deletions

File tree

packages/svelteplot/src/transforms/stack.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -272,16 +272,16 @@ describe('stackY transform', () => {
272272
fill: d[stacked.fill as string]
273273
}));
274274

275-
const d3Stack = simplify(stackY<DataRecord>(args, { order: 'sum' }));
276275
const valueStack = simplify(stackY<DataRecord>(args, { order: 'value' }));
277276

278-
// same py/ny split as d3 offset none; only sort order within the bucket differs
279-
expect(valueStack).toEqual([
280-
{ y1: -100, y2: 0, fill: 'A' },
281-
{ y1: -101, y2: -100, fill: 'B' },
282-
{ y1: 0, y2: 10, fill: 'C' }
283-
]);
284-
expect(d3Stack.map((d) => d.fill)).toEqual(valueStack.map((d) => d.fill));
277+
const byFill = (rows: typeof valueStack) =>
278+
Object.fromEntries(rows.map((d) => [d.fill, d]));
279+
// value-sorted bucket with offset none: d3 stackOffsetNone cumulative baseline
280+
expect(byFill(valueStack)).toEqual({
281+
A: { y1: 0, y2: -100, fill: 'A' },
282+
B: { y1: -100, y2: -101, fill: 'B' },
283+
C: { y1: -101, y2: -91, fill: 'C' }
284+
});
285285
});
286286

287287
it('order value on unit stacking sorts rows within each x bucket', () => {

packages/svelteplot/src/transforms/stack.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ function stackBucketByValue<T extends DataRecord>(
7878
byDim: 'x' | 'y',
7979
byLow: 'x1' | 'y1',
8080
byHigh: 'x2' | 'y2',
81-
reverse: boolean
81+
reverse: boolean,
82+
offset: 'none' | 'diverging'
8283
): T[] {
8384
const sorted = [...items].sort((a, b) => {
8485
const av = toStackNumber(a[S[byDim]]);
@@ -94,16 +95,22 @@ function stackBucketByValue<T extends DataRecord>(
9495
return (a[INDEX] as number) - (b[INDEX] as number);
9596
});
9697

98+
const out: T[] = [];
99+
let pos = 0;
97100
let py = 0;
98101
let ny = 0;
99-
const out: T[] = [];
102+
100103
for (const d of sorted) {
101104
const value = toStackNumber(d[S[byDim]]);
102105
let low: number;
103106
let high: number;
104107
if (value == null || (typeof value === 'number' && Number.isNaN(value))) {
105-
low = py;
108+
low = offset === 'none' ? pos : py;
106109
high = NaN;
110+
} else if (offset === 'none') {
111+
// d3 stackOffsetNone: single cumulative baseline per bucket
112+
low = pos;
113+
high = pos += value;
107114
} else if (value >= 0) {
108115
low = py;
109116
high = py += value;
@@ -255,13 +262,24 @@ function stackXY<T>(
255262
} else {
256263
itemsToStack = [...bucketItems];
257264
}
265+
const valueOffset = (offset === 'diverging' ? 'diverging' : 'none') as
266+
| 'none'
267+
| 'diverging';
258268
out.push(
259-
...stackBucketByValue(itemsToStack, byDim, byLow, byHigh, options.reverse)
269+
...stackBucketByValue(
270+
itemsToStack,
271+
byDim,
272+
byLow,
273+
byHigh,
274+
options.reverse,
275+
valueOffset
276+
)
260277
);
261278
}
262279
} else {
280+
const d3Order = (options.order ?? 'none') as Exclude<StackOrder, 'value'>;
263281
const stackOrder = (series: any[]) => {
264-
const f = STACK_ORDER[options.order || 'none'];
282+
const f = STACK_ORDER[d3Order];
265283
return options.reverse ? f(series).reverse() : f(series);
266284
};
267285

0 commit comments

Comments
 (0)