Skip to content

Commit c19e5c0

Browse files
committed
stack
1 parent 4dd62d2 commit c19e5c0

File tree

8 files changed

+1870
-8
lines changed

8 files changed

+1870
-8
lines changed

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ export {Text, text, textX, textY} from "./marks/text.js";
1515
export {TickX, TickY, tickX, tickY} from "./marks/tick.js";
1616
export {bin1, bin2} from "./transforms/bin.js";
1717
export {group1, group2} from "./transforms/group.js";
18+
export {stackX, stackY} from "./transforms/stack.js";

src/mark.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,12 @@ export class Mark {
7777

7878
// TODO Type coercion?
7979
function Channel(data, {scale, type, value}) {
80-
let label;
81-
if (typeof value === "function") {
82-
label = value.label;
83-
value = Array.from(data, value);
84-
} else {
85-
value = arrayify(value);
86-
}
87-
return {scale, type, value, label};
80+
return {
81+
scale,
82+
type,
83+
value: typeof value === "function" ? Array.from(data, value) : arrayify(value),
84+
label: value ? value.label : undefined
85+
};
8886
}
8987

9088
// This allows transforms to behave equivalently to channels.

src/transforms/stack.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {valueof} from "../mark";
2+
3+
// TODO configurable series order
4+
export function stackX(data, {x, y, ...options}) {
5+
const X = valueof(data, x);
6+
const Y = valueof(data, y);
7+
const n = X.length;
8+
const X0 = new Map();
9+
const X1 = new Float64Array(n);
10+
const X2 = new Float64Array(n);
11+
for (let i = 0; i < n; ++i) {
12+
const k = Y[i] && Y[i].valueOf();
13+
const x1 = X1[i] = X0.has(k) ? X0.get(k) : 0;
14+
const x2 = X2[i] = x1 + +X[i];
15+
X0.set(k, isNaN(x2) ? x1 : x2);
16+
}
17+
return [data, {...options, x1: maybeLabel(X1, x), x2: X2, y: maybeLabel(Y, y)}];
18+
}
19+
20+
// TODO configurable series order
21+
export function stackY(data, {x, y, ...options}) {
22+
const X = valueof(data, x);
23+
const Y = valueof(data, y);
24+
const n = X.length;
25+
const Y0 = new Map();
26+
const Y1 = new Float64Array(n);
27+
const Y2 = new Float64Array(n);
28+
for (let i = 0; i < n; ++i) {
29+
const k = X[i] && X[i].valueOf();
30+
const y1 = Y1[i] = Y0.has(k) ? Y0.get(k) : 0;
31+
const y2 = Y2[i] = y1 + +Y[i];
32+
Y0.set(k, isNaN(y2) ? y1 : y2);
33+
}
34+
return [data, {...options, x: maybeLabel(X, x), y1: maybeLabel(Y1, y), y2: Y2}];
35+
}
36+
37+
// If x is a labeled value, propagate the label to the returned value array.
38+
function maybeLabel(X, x) {
39+
const label = typeof x === "string" ? x : x ? x.label : undefined;
40+
if (label !== undefined) X.label = label;
41+
return X;
42+
}

0 commit comments

Comments
 (0)