diff --git a/README.md b/README.md index 45eb099..bc95a6f 100644 --- a/README.md +++ b/README.md @@ -902,7 +902,13 @@ Rounding is sometimes useful for avoiding antialiasing artifacts, though also co # band.round([round]) · [Source](https://github.com/d3/d3-scale/blob/master/src/band.js), [Examples](https://observablehq.com/@d3/d3-scaleband) -If *round* is specified, enables or disables rounding accordingly. If rounding is enabled, the start and stop of each band will be integers. Rounding is sometimes useful for avoiding antialiasing artifacts, though also consider the [shape-rendering](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering) “crispEdges” styles. Note that if the width of the domain is not a multiple of the cardinality of the range, there may be leftover unused space, even without padding! Use [*band*.align](#band_align) to specify how the leftover space is distributed. +If *round* is specified, enables or disables rounding accordingly. If rounding is enabled, the start and stop of each band will be multiples of the rounding precision. Rounding is sometimes useful for avoiding antialiasing artifacts, though also consider the [shape-rendering](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering) “crispEdges” styles. Note that if the width of the domain is not a multiple of the cardinality of the range, there may be leftover unused space, even without padding! Use [*band*.align](#band_align) to specify how the leftover space is distributed. + +To avoid collapsing the scale, rounding is not applied when the range is smaller than the length of the domain times the rounding precision. + +# band.roundingPrecision([precision]) · [Source](https://github.com/d3/d3-scale/blob/master/src/band.js) + +Sets or reads the rounding precision. A precision of 0.5 is convenient to avoid aliasing artifacts on screens with a pixel density of 2. # band.paddingInner([padding]) · [Source](https://github.com/d3/d3-scale/blob/master/src/band.js), [Examples](https://observablehq.com/@d3/d3-scaleband) @@ -968,7 +974,13 @@ Rounding is sometimes useful for avoiding antialiasing artifacts, though also co # point.round([round]) · [Source](https://github.com/d3/d3-scale/blob/master/src/band.js), [Examples](https://observablehq.com/@d3/d3-scalepoint) -If *round* is specified, enables or disables rounding accordingly. If rounding is enabled, the position of each point will be integers. Rounding is sometimes useful for avoiding antialiasing artifacts, though also consider the [shape-rendering](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering) “crispEdges” styles. Note that if the width of the domain is not a multiple of the cardinality of the range, there may be leftover unused space, even without padding! Use [*point*.align](#point_align) to specify how the leftover space is distributed. +If *round* is specified, enables or disables rounding accordingly. If rounding is enabled, the position of each point will be multiples of the rounding precision. Rounding is sometimes useful for avoiding antialiasing artifacts, though also consider the [shape-rendering](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering) “crispEdges” styles. Note that if the width of the domain is not a multiple of the cardinality of the range, there may be leftover unused space, even without padding! Use [*point*.align](#point_align) to specify how the leftover space is distributed. + +To avoid collapsing the scale, rounding is not applied when the range is smaller than the length of the domain times the rounding precision. + +# point.roundingPrecision([precision]) · [Source](https://github.com/d3/d3-scale/blob/master/src/band.js) + +Sets or reads the rounding precision. A precision of 0.5 is convenient to avoid aliasing artifacts on screens with a pixel density of 2. # point.padding([padding]) · [Source](https://github.com/d3/d3-scale/blob/master/src/band.js), [Examples](https://observablehq.com/@d3/d3-scalepoint) diff --git a/src/band.js b/src/band.js index 88e1fbd..4703259 100644 --- a/src/band.js +++ b/src/band.js @@ -11,6 +11,7 @@ export default function band() { step, bandwidth, round = false, + roundingPrecision = 1, paddingInner = 0, paddingOuter = 0, align = 0.5; @@ -23,10 +24,12 @@ export default function band() { start = reverse ? r1 : r0, stop = reverse ? r0 : r1; step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2); - if (round) step = Math.floor(step); + var r = round && step >= roundingPrecision; + if (r) step = roundingPrecision * Math.floor(step / roundingPrecision); start += (stop - start - step * (n - paddingInner)) * align; bandwidth = step * (1 - paddingInner); - if (round) start = Math.round(start), bandwidth = Math.round(bandwidth); + if (round) start = roundingPrecision * Math.round(start / roundingPrecision); + if (r) bandwidth = roundingPrecision * Math.round(bandwidth / roundingPrecision); var values = sequence(n).map(function(i) { return start + step * i; }); return ordinalRange(reverse ? values.reverse() : values); } @@ -55,6 +58,10 @@ export default function band() { return arguments.length ? (round = !!_, rescale()) : round; }; + scale.roundingPrecision = function(_) { + return arguments.length ? (roundingPrecision = +_, rescale()) : roundingPrecision; + }; + scale.padding = function(_) { return arguments.length ? (paddingInner = Math.min(1, paddingOuter = +_), rescale()) : paddingInner; }; diff --git a/test/band-test.js b/test/band-test.js index 2fef80e..bea11de 100644 --- a/test/band-test.js +++ b/test/band-test.js @@ -181,6 +181,31 @@ tape("band.rangeRound(values) coerces values to numbers", function(test) { test.end(); }); +tape("band.round is ignored when the range is too small and the scale would collapse", function(test) { + var domain = ["A", "B", "C", "D", "E", "F"]; + var s1 = scale.scaleBand().domain(domain).rangeRound([0, 3]); + test.deepEqual(domain.map(s1), [ 0, 0.5, 1, 1.5, 2, 2.5 ]); + test.equal(s1.bandwidth(), 0.5); + var s2 = scale.scaleBand().domain(domain).rangeRound([0, 30]); + test.deepEqual(domain.map(s2), [ 0, 5, 10, 15, 20, 25 ]); + var s3 = scale.scaleBand().domain(domain).rangeRound([0, 10]); + test.deepEqual(domain.map(s3), [ 2, 3, 4, 5, 6, 7 ]); + var s4 = scale.scaleBand().domain(domain).range([0, 3]).round(true); + test.deepEqual(domain.map(s4), [ 0, 0.5, 1, 1.5, 2, 2.5 ]); + var s5 = scale.scaleBand().domain(domain).range([0, 1.5]).round(true); + test.deepEqual(domain.map(s5), [ 0, 0.25, 0.5, 0.75, 1, 1.25 ]); + test.equal(s5.bandwidth(), 0.25); + test.end(); +}); + +tape("band.roundingPrecision adjusts the rounding precision", function(test) { + var domain = ["A", "B", "C", "D", "E", "F"]; + var s1 = scale.scaleBand().domain(domain).rangeRound([0, 5]).roundingPrecision(0.5); + test.deepEqual(domain.map(s1), [ 1, 1.5, 2, 2.5, 3, 3.5 ]); + test.equal(s1.bandwidth(), 0.5); + test.end(); +}); + tape("band.paddingInner(p) specifies the inner padding p", function(test) { var s = scale.scaleBand().domain(["a", "b", "c"]).range([120, 0]).paddingInner(0.1).round(true); test.deepEqual(s.domain().map(s), [83, 42, 1]);