Skip to content

Commit 9fc3a10

Browse files
committed
x-facet!
1 parent ef1338f commit 9fc3a10

File tree

3 files changed

+49
-25
lines changed

3 files changed

+49
-25
lines changed

src/axes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export function Axes(
1111
return {
1212
x: xScale && xAxis ? new AxisX({grid, ...x}) : null,
1313
y: yScale && yAxis ? new AxisY({grid, ...y}) : null,
14-
fx: fxScale && fxAxis ? new AxisY({name: "fx", ...fx}) : null,
14+
fx: fxScale && fxAxis ? new AxisX({name: "fx", ...fx}) : null,
1515
fy: fyScale && fyAxis ? new AxisY({name: "fy", ...fy}) : null
1616
};
1717
}

src/marks/facet.js

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
1-
import {group} from "d3-array";
1+
import {group, groups} from "d3-array";
22
import {create} from "d3-selection";
33
import {Mark} from "../mark.js";
44
import {autoScaleRange} from "../scales.js";
55

6-
// TODO facet-x
7-
export class Facet extends Mark {
8-
constructor(data, {y, transform} = {}, marks = []) {
6+
class Facet extends Mark {
7+
constructor(data, {x, y, transform} = {}, marks = []) {
98
super(
109
data,
1110
[
12-
{name: "fy", value: y, scale: "fy", type: "band"}
11+
{name: "fx", value: x, scale: "fx", type: "band", optional: true},
12+
{name: "fy", value: y, scale: "fy", type: "band", optional: true}
1313
],
1414
transform
1515
);
1616
this.marks = marks;
1717
this.facets = undefined; // set by initialize
1818
}
1919
initialize(data) {
20-
const {index, channels: [fy]} = super.initialize(data);
21-
const [, {value: FY}] = fy;
20+
const {index, channels} = super.initialize(data);
2221
const subchannels = [];
2322
const facets = this.facets = new Map();
24-
25-
for (const [facetKey, facetIndex] of group(index, i => FY[i])) {
23+
for (const [facetKey, facetIndex] of facetGroups(index, channels)) {
2624
const facetData = Array.from(facetIndex, i => data[i]);
2725
const markIndex = new Map();
2826
const markChannels = new Map();
@@ -40,30 +38,29 @@ export class Facet extends Mark {
4038
}
4139
facets.set(facetKey, {markIndex, markChannels});
4240
}
43-
44-
return {index, channels: [fy, ...subchannels]};
41+
return {index, channels: [...channels, ...subchannels]};
4542
}
4643
render(index, scales, channels, options) {
4744
const {marks, facets} = this;
48-
const {fy} = scales;
49-
const {y, marginRight, marginLeft, width} = options;
45+
const {fx, fy} = scales;
46+
const {x, y, marginTop, marginRight, marginBottom, marginLeft, width, height} = options;
5047

5148
const subdimensions = {
52-
marginTop: 0,
53-
marginRight,
54-
marginBottom: 0,
55-
marginLeft,
56-
width,
57-
height: fy.bandwidth()
49+
...fy
50+
? {marginTop: 0, marginBottom: 0, height: fy.bandwidth()}
51+
: {marginTop, marginBottom, height},
52+
...fx
53+
? {marginRight: 0, marginLeft: 0, width: fx.bandwidth()}
54+
: {marginRight, marginLeft, width}
5855
};
5956

60-
autoScaleRange({y}, subdimensions);
57+
autoScaleRange({x, y}, subdimensions);
6158

6259
return create("svg:g")
6360
.call(g => g.selectAll()
64-
.data(fy.domain())
61+
.data(facets.keys())
6562
.join("g")
66-
.attr("transform", (key) => `translate(0,${fy(key)})`)
63+
.attr("transform", facetTranslate(fx, fy))
6764
.each(function(key) {
6865
const {markIndex, markChannels} = facets.get(key);
6966
for (const mark of marks) {
@@ -79,3 +76,30 @@ export class Facet extends Mark {
7976
.node();
8077
}
8178
}
79+
80+
export function facets(data, {x, y, ...options}, marks) {
81+
return x === undefined && y === undefined
82+
? marks // if no facets are specified, ignore!
83+
: [new Facet(data, {x, y, ...options}, marks)];
84+
}
85+
86+
function facetGroups(index, channels) {
87+
return (channels.length > 1 ? facetGroup2 : facetGroup1)(index, ...channels);
88+
}
89+
90+
function facetGroup1(index, [, {value: F}]) {
91+
return group(index, i => F[i]);
92+
}
93+
94+
function facetGroup2(index, [, {value: FX}], [, {value: FY}]) {
95+
return groups(index, i => FX[i], i => FY[i])
96+
.flatMap(([x, xgroup]) => xgroup
97+
.map(([y, ygroup]) => [[x, y], ygroup]));
98+
}
99+
100+
// This must match the key structure returned by facetGroups.
101+
function facetTranslate(fx, fy) {
102+
return fx && fy ? ([kx, ky]) => `translate(${fx(kx)},${fy(ky)})`
103+
: fx ? kx => `translate(${fx(kx)},0)`
104+
: ky => `translate(0,${fy(ky)})`;
105+
}

src/plot.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {create} from "d3-selection";
22
import {Axes, autoAxisTicks, autoAxisLabels} from "./axes.js";
3-
import {Facet} from "./marks/facet.js";
3+
import {facets} from "./marks/facet.js";
44
import {Scales, autoScaleRange} from "./scales.js";
55

66
export function plot(options = {}) {
@@ -10,7 +10,7 @@ export function plot(options = {}) {
1010
if (facet !== undefined) {
1111
const {marks} = options;
1212
const {data} = facet;
13-
options = {...options, marks: [new Facet(data, facet, marks)]};
13+
options = {...options, marks: facets(data, facet, marks)};
1414
}
1515

1616
const {

0 commit comments

Comments
 (0)