From 8832a801af90ac0cf3cdcaf550f7bacb355168d4 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Sat, 5 Dec 2020 12:33:11 -0800 Subject: [PATCH] dotbin --- src/defined.js | 4 ++++ src/marks/dot.js | 4 ++-- src/scales/quantitative.js | 5 ++++- src/transforms/bin.js | 13 ++++++------ test/diamonds-carat-price-dots.html | 32 +++++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 test/diamonds-carat-price-dots.html diff --git a/src/defined.js b/src/defined.js index 8b847e6015..99730f54de 100644 --- a/src/defined.js +++ b/src/defined.js @@ -18,3 +18,7 @@ export function filter(index, ...channels) { } return index; } + +export function positive(x) { + return x > 0 ? x : NaN; +} diff --git a/src/marks/dot.js b/src/marks/dot.js index f21f1f2d91..ae72f2aa8d 100644 --- a/src/marks/dot.js +++ b/src/marks/dot.js @@ -1,6 +1,6 @@ import {ascending} from "d3-array"; import {create} from "d3-selection"; -import {filter, nonempty} from "../defined.js"; +import {filter, nonempty, positive} from "../defined.js"; import {Mark, indexOf, identity, first, second, maybeColor, maybeNumber} from "../mark.js"; import {Style, applyDirectStyles, applyIndirectStyles, applyBandTransform} from "../style.js"; @@ -48,7 +48,7 @@ export class Dot extends Mark { {x, y, r, color}, {x: X, y: Y, z: Z, r: R, title: L, fill: F, stroke: S} ) { - const index = filter(I, X, Y, R, F, S); + const index = filter(I, X, Y, F, S).filter(i => positive(R[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); return create("svg:g") .call(applyIndirectStyles, this) diff --git a/src/scales/quantitative.js b/src/scales/quantitative.js index ecdf626874..36df6a59da 100644 --- a/src/scales/quantitative.js +++ b/src/scales/quantitative.js @@ -50,6 +50,7 @@ import { } from "d3-scale-chromatic"; import {scaleDiverging, scaleLinear, scaleLog, scalePow, scaleSymlog} from "d3-scale"; import {registry, radius, color} from "./index.js"; +import {positive} from "../defined.js"; const constant = x => () => x; const flip = i => t => i(1 - t); @@ -213,9 +214,11 @@ function inferDomain(channels) { ]; } +// We don’t want the upper bound of the radial domain to be zero, as this would +// be degenerate, so we ignore nonpositive values. function inferRadialDomain(channels) { return [ 0, - quantile(channels, 0.5, ({value}) => value === undefined ? value : quantile(value, 0.25)) + quantile(channels, 0.5, ({value}) => value === undefined ? NaN : quantile(value, 0.25, positive)) ]; } diff --git a/src/transforms/bin.js b/src/transforms/bin.js index 7f088615a9..693f3e8e44 100644 --- a/src/transforms/bin.js +++ b/src/transforms/bin.js @@ -26,10 +26,8 @@ export function bin1(options = {}) { } export function bin2({x = {}, y = {}, domain, thresholds} = {}) { - if (isvalue(x)) x = {value: x}; - if (isvalue(y)) y = {value: y}; - const binX = bin1({domain, thresholds, value: first, ...x}); - const binY = bin1({domain, thresholds, value: second, ...y}); + const binX = bin1({domain, thresholds, value: first, ...maybeValue(x)}); + const binY = bin1({domain, thresholds, value: second, ...maybeValue(y)}); return data => { return cross(binX(data), binY(data).map(binset), (x, y) => { return { @@ -43,8 +41,11 @@ export function bin2({x = {}, y = {}, domain, thresholds} = {}) { }; } -function isvalue(x) { - return typeof x === "string" || typeof x === "function"; +// The value may be defined as a string or function, rather than an object with +// a value property. TODO Allow value to be specified as array, too? This would +// require promoting the array to an accessor for compatibility with d3.bin. +function maybeValue(x) { + return typeof x === "string" || typeof x === "function" ? {value: x} : x; } function binset(bin) { diff --git a/test/diamonds-carat-price-dots.html b/test/diamonds-carat-price-dots.html new file mode 100644 index 0000000000..399e758bf8 --- /dev/null +++ b/test/diamonds-carat-price-dots.html @@ -0,0 +1,32 @@ + + + + + +