Skip to content

Commit 0040be8

Browse files
committed
add TODO
1 parent 461ab09 commit 0040be8

File tree

1 file changed

+34
-9
lines changed

1 file changed

+34
-9
lines changed

src/plot.js

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {InternMap, InternSet, select, sort, sum} from "d3";
1+
import {InternMap, InternSet, cross, select, sort, sum} from "d3";
22
import {Axes, autoAxisTicks, autoScaleLabels} from "./axes.js";
33
import {Channel, Channels, channelDomain, valueObject} from "./channel.js";
44
import {Context, create} from "./context.js";
@@ -440,28 +440,35 @@ export function plot(options = {}) {
440440
// Create the facet scales and array of subplots
441441
const facetScales = Scales(channelsByScale);
442442

443-
let facets;
444-
if (facetScales.fx) facets = (facets || [{}]).flatMap((d) => facetScales.fx.domain.map((x) => ({...d, x})));
445-
if (facetScales.fy) facets = (facets || [{}]).flatMap((d) => facetScales.fy.domain.map((y) => ({...d, y})));
446-
if (facets) facets.forEach((d, j) => (d.j = j));
443+
// TODO Avoid j here? TODO Document this data structure. Note that empty is
444+
// mutated below after we’ve computed the per-mark facet channels.
445+
let facets =
446+
facetScales.fx && facetScales.fy
447+
? cross(facetScales.fx.domain, facetScales.fy.domain).map(([x, y], j) => ({x, y, j, empty: true}))
448+
: facetScales.fx
449+
? facetScales.fx.domain.map((x, j) => ({x, j, empty: true}))
450+
: facetScales.fy
451+
? facetScales.fy.domain.map((y, j) => ({y, j, empty: true}))
452+
: null;
447453
const facetLength = facets && facets.length;
448454

449455
// Compute the top-level facet index
450456
if (facetDataIndex) {
451457
const {fx, fy} = facetChannels;
452458
facetsIndex = [];
453-
for (const {x, y} of facets)
459+
for (const {x, y} of facets) {
454460
facetsIndex.push(
455461
facetDataIndex.filter((i) => (!fx || facetKeyEquals(fx.value[i], x)) && (!fy || facetKeyEquals(fy.value[i], y)))
456462
);
463+
}
457464
}
458465

459466
// Compute a facet index for each mark
460467
for (const mark of marks) {
461468
if (mark.facet === null) continue;
462469
const state = stateByMark.get(mark);
463-
464470
const {x, y, method} = mark.facet;
471+
465472
// Mark-level facet ? Compute an index for that mark’s data and options
466473
if (x !== undefined || y !== undefined) {
467474
if (facets) state.facetsIndex = filterFacets(facets, state, facetChannels);
@@ -474,12 +481,30 @@ export function plot(options = {}) {
474481
state.facetsIndex = facetsIndex;
475482
}
476483

477-
// A facet is empty if none of the faceted index has contents for any mark
484+
// When faceting by both x and y (i.e., when both fx and fy scales are
485+
// present), then the cross product of the domains of fx and fy can include
486+
// fx-fy combinations for which no mark has an instance associated with that
487+
// combination of fx and fy, and therefore we don’t want to render this facet
488+
// (not even the frame). The same can occur if you specify the domain of fx
489+
// and fy explicitly, but there is no mark instance associated with some
490+
// values in the domain.
491+
//
492+
// TODO We need to do two (or three?) passes here. First, we need determine
493+
// the domains of the fx and fy scales (as needed) based on the union of
494+
// distinct channel values, including both mark-level facets and top-level
495+
// facets. Then we need to check whether we have any mark instances (or
496+
// top-level facet data “instances”) associated with each value, or
497+
// cross-product of values, in the fx and fy scale domains. This probably
498+
// means having an InternSet of fx?+fx? keys recording which facets are
499+
// non-empty, similar to the FacetMap data structure we had before.
500+
501+
// A facet is empty if none of the faceted index has contents for any mark.
502+
// TODO Can we do this more declaratively rather than re-assigning facets?
478503
facets =
479504
facets &&
480505
facets.filter((_, j) => {
481506
let nonFaceted = true;
482-
for (const [, {facetsIndex}] of stateByMark) {
507+
for (const {facetsIndex} of stateByMark.values()) {
483508
if (facetsIndex) {
484509
nonFaceted = false;
485510
if (facetsIndex?.[j].length) return true;

0 commit comments

Comments
 (0)