diff --git a/src/axis.js b/src/axis.js index 345cd61a27..18f296cab8 100644 --- a/src/axis.js +++ b/src/axis.js @@ -1,6 +1,6 @@ import {axisTop, axisBottom, axisRight, axisLeft, create, format, utcFormat} from "d3"; import {formatIsoDate} from "./format.js"; -import {boolean, number, string, keyword, maybeKeyword, constant, isTemporal} from "./mark.js"; +import {boolean, take, number, string, keyword, maybeKeyword, constant, isTemporal} from "./mark.js"; export class AxisX { constructor({ @@ -67,7 +67,7 @@ export class AxisX { .attr("font-family", null) .call(!line ? g => g.select(".domain").remove() : () => {}) .call(!grid ? () => {} - : fy ? gridFacetX(fy, -ty) + : fy ? gridFacetX(index, fy, -ty) : gridX(offsetSign * (marginBottom + marginTop - height))) .call(!label ? () => {} : g => g.append("text") .attr("fill", "currentColor") @@ -148,7 +148,7 @@ export class AxisY { .attr("font-family", null) .call(!line ? g => g.select(".domain").remove() : () => {}) .call(!grid ? () => {} - : fx ? gridFacetY(fx, -tx) + : fx ? gridFacetY(index, fx, -tx) : gridY(offsetSign * (marginLeft + marginRight - width))) .call(!label ? () => {} : g => g.append("text") .attr("fill", "currentColor") @@ -182,22 +182,24 @@ function gridY(x2) { .attr("x2", x2); } -function gridFacetX(fy, ty) { +function gridFacetX(index, fy, ty) { const dy = fy.bandwidth(); + const domain = fy.domain(); return g => g.selectAll(".tick") .append("path") .attr("stroke", "currentColor") .attr("stroke-opacity", 0.1) - .attr("d", fy.domain().map(v => `M0,${fy(v) + ty}v${dy}`).join("")); + .attr("d", (index ? take(domain, index) : domain).map(v => `M0,${fy(v) + ty}v${dy}`).join("")); } -function gridFacetY(fx, tx) { +function gridFacetY(index, fx, tx) { const dx = fx.bandwidth(); + const domain = fx.domain(); return g => g.selectAll(".tick") .append("path") .attr("stroke", "currentColor") .attr("stroke-opacity", 0.1) - .attr("d", fx.domain().map(v => `M${fx(v) + tx},0h${dx}`).join("")); + .attr("d", (index ? take(domain, index) : domain).map(v => `M${fx(v) + tx},0h${dx}`).join("")); } function createAxis(axis, scale, {ticks, tickSize, tickPadding, tickFormat}) { diff --git a/src/facet.js b/src/facet.js index f42d944530..62b198f2b4 100644 --- a/src/facet.js +++ b/src/facet.js @@ -1,6 +1,6 @@ import {cross, difference, groups, InternMap} from "d3"; import {create} from "d3"; -import {Mark, first, second, markify} from "./mark.js"; +import {Mark, first, second, markify, where} from "./mark.js"; import {applyScales} from "./scales.js"; import {filterStyles} from "./style.js"; @@ -24,7 +24,6 @@ class Facet extends Mark { this.marks = marks.flat(Infinity).map(markify); // The following fields are set by initialize: this.marksChannels = undefined; // array of mark channels - this.marksIndex = undefined; // array of mark indexes (for non-faceted marks) this.marksIndexByFacet = undefined; // map from facet key to array of mark indexes } initialize() { @@ -34,7 +33,6 @@ class Facet extends Mark { const facetsIndex = Array.from(facets, second); const subchannels = []; const marksChannels = this.marksChannels = []; - const marksIndex = this.marksIndex = new Array(this.marks.length); const marksIndexByFacet = this.marksIndexByFacet = facetMap(channels); for (const facetKey of facetsKeys) { marksIndexByFacet.set(facetKey, new Array(this.marks.length)); @@ -57,12 +55,10 @@ class Facet extends Mark { for (let j = 0; j < facetsKeys.length; ++j) { marksIndexByFacet.get(facetsKeys[j])[i] = I[j]; } - marksIndex[i] = []; // implicit empty index for sparse facets } else { for (let j = 0; j < facetsKeys.length; ++j) { marksIndexByFacet.get(facetsKeys[j])[i] = I; } - marksIndex[i] = I; } } for (const [, channel] of markChannels) { @@ -73,8 +69,10 @@ class Facet extends Mark { return {index, channels: [...channels, ...subchannels]}; } render(I, scales, channels, dimensions, axes) { - const {marks, marksChannels, marksIndex, marksIndexByFacet} = this; + const {marks, marksChannels, marksIndexByFacet} = this; const {fx, fy} = scales; + const fyDomain = fy && fy.domain(); + const fxDomain = fx && fx.domain(); const fyMargins = fy && {marginTop: 0, marginBottom: 0, height: fy.bandwidth()}; const fxMargins = fx && {marginRight: 0, marginLeft: 0, width: fx.bandwidth()}; const subdimensions = {...dimensions, ...fxMargins, ...fyMargins}; @@ -82,35 +80,43 @@ class Facet extends Mark { return create("svg:g") .call(g => { if (fy && axes.y) { - const domain = fy.domain(); const axis1 = axes.y, axis2 = nolabel(axis1); - const j = axis1.labelAnchor === "bottom" ? domain.length - 1 : axis1.labelAnchor === "center" ? domain.length >> 1 : 0; + const j = axis1.labelAnchor === "bottom" ? fyDomain.length - 1 : axis1.labelAnchor === "center" ? fyDomain.length >> 1 : 0; const fyDimensions = {...dimensions, ...fyMargins}; g.selectAll() - .data(domain) + .data(fyDomain) .join("g") .attr("transform", ky => `translate(0,${fy(ky)})`) - .append((_, i) => (i === j ? axis1 : axis2).render(null, scales, null, fyDimensions)); + .append((ky, i) => (i === j ? axis1 : axis2).render( + fx && where(fxDomain, kx => marksIndexByFacet.has([kx, ky])), + scales, + null, + fyDimensions + )); } if (fx && axes.x) { - const domain = fx.domain(); const axis1 = axes.x, axis2 = nolabel(axis1); - const j = axis1.labelAnchor === "right" ? domain.length - 1 : axis1.labelAnchor === "center" ? domain.length >> 1 : 0; + const j = axis1.labelAnchor === "right" ? fxDomain.length - 1 : axis1.labelAnchor === "center" ? fxDomain.length >> 1 : 0; const {marginLeft, marginRight} = dimensions; const fxDimensions = {...dimensions, ...fxMargins, labelMarginLeft: marginLeft, labelMarginRight: marginRight}; g.selectAll() - .data(domain) + .data(fxDomain) .join("g") .attr("transform", kx => `translate(${fx(kx)},0)`) - .append((_, i) => (i === j ? axis1 : axis2).render(null, scales, null, fxDimensions)); + .append((kx, i) => (i === j ? axis1 : axis2).render( + fy && where(fyDomain, ky => marksIndexByFacet.has([kx, ky])), + scales, + null, + fxDimensions + )); } }) .call(g => g.selectAll() - .data(facetKeys(scales)) + .data(facetKeys(scales).filter(marksIndexByFacet.has, marksIndexByFacet)) .join("g") .attr("transform", facetTranslate(fx, fy)) .each(function(key) { - const marksFacetIndex = marksIndexByFacet.get(key) || marksIndex; + const marksFacetIndex = marksIndexByFacet.get(key); for (let i = 0; i < marks.length; ++i) { const values = marksValues[i]; const index = filterStyles(marksFacetIndex[i], values); diff --git a/src/mark.js b/src/mark.js index 1b670dad6e..19e5706793 100644 --- a/src/mark.js +++ b/src/mark.js @@ -230,6 +230,11 @@ export function range(data) { return Uint32Array.from(data, indexOf); } +// Returns a filtered range of data given the test function. +export function where(data, test) { + return range(data).filter(i => test(data[i], i, data)); +} + // Returns an array [values[index[0]], values[index[1]], …]. export function take(values, index) { return Array.from(index, i => values[i]); diff --git a/test/output/penguinCulmen.svg b/test/output/penguinCulmen.svg index d98f6a61e4..b579f95286 100644 --- a/test/output/penguinCulmen.svg +++ b/test/output/penguinCulmen.svg @@ -50,23 +50,23 @@ 35 - + 40 - + 45 - + 50 - + 55 - + @@ -122,11 +122,11 @@ 15 - + 20 - + culmen_depth_mm → @@ -2573,11 +2573,6 @@ - - - - - diff --git a/test/output/penguinCulmenArray.svg b/test/output/penguinCulmenArray.svg index 9f89051fc0..4519fca6ce 100644 --- a/test/output/penguinCulmenArray.svg +++ b/test/output/penguinCulmenArray.svg @@ -50,23 +50,23 @@ 35 - + 40 - + 45 - + 50 - + 55 - + @@ -122,11 +122,11 @@ 15 - + 20 - + @@ -2904,353 +2904,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/output/penguinMassSexSpecies.svg b/test/output/penguinMassSexSpecies.svg index 3eb26dd0d2..984e5f4ab2 100644 --- a/test/output/penguinMassSexSpecies.svg +++ b/test/output/penguinMassSexSpecies.svg @@ -185,12 +185,6 @@ - - - - - -