Skip to content

Commit b3cdc4a

Browse files
committed
fix facet exclude
1 parent 47972e4 commit b3cdc4a

File tree

4 files changed

+131
-2
lines changed

4 files changed

+131
-2
lines changed

src/plot.js

+47-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import {
99
arrayify,
1010
isDomainSort,
1111
isScaleOptions,
12+
isTypedArray,
1213
keyword,
1314
map,
1415
maybeNamed,
1516
range,
1617
second,
18+
slice,
1719
where,
1820
yes
1921
} from "./options.js";
@@ -686,9 +688,42 @@ export class Mark {
686688
}
687689
initialize(facets, facetChannels) {
688690
let data = arrayify(this.data);
691+
let channels = this.channels;
689692
if (facets === undefined && data != null) facets = [range(data)];
690-
if (this.transform != null) ({facets, data} = this.transform(data, facets)), (data = arrayify(data));
691-
const channels = Channels(this.channels, data);
693+
694+
if (this.transform != null) {
695+
// If the mark has a transform, reindex facets that overlap
696+
const overlap = new Set();
697+
const reindex = new Map();
698+
let j = data.length;
699+
for (const facet of facets) {
700+
for (let k = 0; k < facet.length; ++k) {
701+
const i = facet[k];
702+
if (overlap.has(i)) {
703+
facet[k] = j;
704+
reindex.set(j, i);
705+
++j;
706+
}
707+
overlap.add(i);
708+
}
709+
}
710+
// If necessary, expand data and any channel defined as an array
711+
if (reindex.size > 0) {
712+
data = expandArray(data, data.length + reindex.size);
713+
for (const [j, i] of reindex) data[j] = data[i];
714+
for (const key in channels) {
715+
const A = channels[key].value;
716+
if (Array.isArray(A) || isTypedArray(A)) {
717+
channels[key].value = (_, i) => A[reindex.has(i) ? reindex.get(i) : i];
718+
}
719+
}
720+
}
721+
722+
({facets, data} = this.transform(data, facets));
723+
data = arrayify(data);
724+
}
725+
726+
channels = Channels(channels, data);
692727
if (this.sort != null) channelDomain(channels, facetChannels, data, this.sort);
693728
return {data, facets, channels};
694729
}
@@ -869,3 +904,13 @@ class FacetMap2 extends FacetMap {
869904
return this;
870905
}
871906
}
907+
908+
// expands an array or typed array to make room for n values
909+
function expandArray(values, n) {
910+
if (isTypedArray(values)) {
911+
const d = new values.constructor(n);
912+
d.set(values);
913+
return d;
914+
}
915+
return slice(values);
916+
}

test/output/stackExclude.svg

+65
Loading

test/plots/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ export {default as singleValueBar} from "./single-value-bar.js";
206206
export {default as singleValueBin} from "./single-value-bin.js";
207207
export {default as softwareVersions} from "./software-versions.js";
208208
export {default as sparseCell} from "./sparse-cell.js";
209+
export {default as stackExclude} from "./stack-exclude.js";
209210
export {default as stackedBar} from "./stacked-bar.js";
210211
export {default as stackedRect} from "./stacked-rect.js";
211212
export {default as stargazers} from "./stargazers.js";

test/plots/stack-exclude.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as Plot from "@observablehq/plot";
2+
3+
export default async function () {
4+
const data = Float64Array.of(1, 2, 3);
5+
const facets = ["a", "b", "c"];
6+
return Plot.plot({
7+
height: 180,
8+
facet: {data, x: facets},
9+
marks: [
10+
Plot.barY(data, {
11+
stroke: (d) => d, // channel as accessor
12+
fill: data, // channel as array
13+
fillOpacity: 0.5,
14+
facet: "exclude"
15+
})
16+
]
17+
});
18+
}

0 commit comments

Comments
 (0)