Skip to content

Commit e1dba89

Browse files
authored
adopt density.contours (#954)
* adopt density.contours * range, not ticks
1 parent 40b0873 commit e1dba89

12 files changed

+166
-230
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1032,7 +1032,7 @@ If a **z** channel is specified, the input points are grouped by *z*, and separa
10321032
10331033
Draws contours representing the estimated density of the two-dimensional points given by the **x** and **y** channels, and possibly weighted by the **weight** channel. If either of the **x** or **y** channels are not specified, the corresponding position is controlled by the **frameAnchor** option.
10341034
1035-
The **thresholds** option, which defaults to 20, specifies the approximate number of contours that will be computed at even intervals between 0 (exclusive) and the maximum density. The **bandwidth** option, which defaults to 20, specifies the standard deviation of the Gaussian kernel used for estimation in pixels.
1035+
The **thresholds** option, which defaults to 20, specifies one more than the number of contours that will be computed at uniformly-spaced intervals between 0 (exclusive) and the maximum density (exclusive). The **bandwidth** option, which defaults to 20, specifies the standard deviation of the Gaussian kernel used for estimation in pixels.
10361036
10371037
If a **z**, **stroke** or **fill** channel is specified, the input points are grouped by series, and separate sets of contours are generated for each series. If the **stroke** or **fill** is specified as *density*, a color channel is constructed with values representing the density threshold value of each contour.
10381038

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"vite": "2"
5151
},
5252
"dependencies": {
53-
"d3": "^7.4.5",
53+
"d3": "^7.5.0",
5454
"interval-tree-1d": "1",
5555
"isoformat": "0.2"
5656
},

src/marks/density.js

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {contourDensity, create, geoPath} from "d3";
22
import {identity, maybeTuple, maybeZ, valueof} from "../options.js";
33
import {Mark} from "../plot.js";
44
import {coerceNumbers} from "../scales.js";
5-
import {applyFrameAnchor, applyDirectStyles, applyIndirectStyles, applyChannelStyles, applyTransform, distinct, groupZ} from "../style.js";
5+
import {applyFrameAnchor, applyDirectStyles, applyIndirectStyles, applyChannelStyles, applyTransform, groupZ} from "../style.js";
66
import {initializer} from "../transforms/basic.js";
77

88
const defaults = {
@@ -92,45 +92,36 @@ function densityInitializer(options, fillDensity, strokeDensity) {
9292
.y(Y ? i => Y[i] : cy)
9393
.weight(W ? i => W[i] : 1)
9494
.size([width, height])
95-
.bandwidth(bandwidth)
96-
.thresholds(thresholds);
95+
.bandwidth(bandwidth);
9796

98-
// If there are multiple facets or multiple series, first compute the
99-
// contours for each facet-series independently; choose the set of contours
100-
// with the maximum threshold value (density), and then apply this set’s
101-
// thresholds to all the other facet-series. TODO With API changes to
102-
// d3-contour, we could avoid recomputing the blurred grid and cache
103-
// individual contours, making this more efficient.
104-
if (facets.length > 1 || Z && facets.length > 0 && distinct(facets[0], Z)) {
105-
let maxValue = 0;
106-
let maxContours = [];
107-
for (const facet of facets) {
108-
for (const index of Z ? groupZ(facet, Z, z) : [facet]) {
109-
const C = density(index);
110-
if (C.length > 0) {
111-
const c = C[C.length - 1];
112-
if (c.value > maxValue) {
113-
maxValue = c.value;
114-
maxContours = C;
115-
}
116-
}
117-
}
97+
// Compute the grid for each facet-series; find the maximum density of all
98+
// grids and use this to compute contour thresholds.
99+
let maxValue = 0;
100+
const facetsContours = [];
101+
for (const facet of facets) {
102+
const facetContours = [];
103+
facetsContours.push(facetContours);
104+
for (const index of Z ? groupZ(facet, Z, z) : [facet]) {
105+
const contour = density.contours(index);
106+
const max = contour.max;
107+
if (max > maxValue) maxValue = max;
108+
facetContours.push([index, contour]);
118109
}
119-
density.thresholds(maxContours.map(c => c.value));
120110
}
121111

122112
// Generate contours for each facet-series.
113+
const T = Array.from({length: thresholds - 1}, (_, i) => maxValue * (i + 1) / thresholds);
123114
const newFacets = [];
124115
const contours = [];
125-
for (const facet of facets) {
126-
const contourFacet = [];
127-
newFacets.push(contourFacet);
128-
for (const index of Z ? groupZ(facet, Z, z) : [facet]) {
129-
for (const contour of density(index)) {
130-
contourFacet.push(contours.length);
131-
contours.push(contour);
132-
if (FD) FD.push(contour.value * k);
133-
if (SD) SD.push(contour.value * k);
116+
for (const facetContours of facetsContours) {
117+
const newFacet = [];
118+
newFacets.push(newFacet);
119+
for (const [index, contour] of facetContours) {
120+
for (const t of T) {
121+
newFacet.push(contours.length);
122+
contours.push(contour(t));
123+
if (FD) FD.push(t * k);
124+
if (SD) SD.push(t * k);
134125
for (const key in newChannels) {
135126
newChannels[key].value.push(channels[key].value[index[0]]);
136127
}

src/style.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -184,16 +184,6 @@ function groupAesthetics({ariaLabel: AL, title: T, fill: F, fillOpacity: FO, str
184184
return [AL, T, F, FO, S, SO, SW, O, H].filter(c => c !== undefined);
185185
}
186186

187-
export function distinct(I, X) {
188-
const x = keyof(X[0]);
189-
for (const i of I) {
190-
if (keyof(X[i]) !== x) {
191-
return true;
192-
}
193-
}
194-
return false;
195-
}
196-
197187
export function groupZ(I, Z, z) {
198188
const G = group(I, i => Z[i]);
199189
if (z === undefined && G.size > I.length >> 1) {

test/output/faithfulDensity.svg

Lines changed: 22 additions & 20 deletions
Loading

test/output/faithfulDensity1d.svg

Lines changed: 22 additions & 16 deletions
Loading

test/output/penguinDensity.svg

Lines changed: 19 additions & 17 deletions
Loading

test/output/penguinDensityFill.html

Lines changed: 26 additions & 35 deletions
Large diffs are not rendered by default.

test/output/penguinDensityZ.html

Lines changed: 24 additions & 69 deletions
Large diffs are not rendered by default.

test/output/seattlePrecipitationDensity.svg

Lines changed: 19 additions & 20 deletions
Loading

0 commit comments

Comments
 (0)