Skip to content

Commit

Permalink
Fix #57 - replace curveMonotone with curveMonotone{X,Y}.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Jan 28, 2016
1 parent 27130a9 commit 510357d
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 22 deletions.
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ Equivalent to [*line*.defined](#line_defined).

<a name="radialLine_curve" href="#radialLine_curve">#</a> <i>radialLine</i>.<b>curve</b>([<i>curve</i>[, <i>parameters…</i>]])

Equivalent to [*line*.curve](#line_curve). Note that [curveMonotone](#curveMonotone) is not recommended for radial lines because it assumes that the data is monotonic in *x*, which is typically untrue of radial lines.
Equivalent to [*line*.curve](#line_curve). Note that [curveMonotoneX](#curveMonotoneX) or [curveMonotoneY](#curveMonotoneY) are not recommended for radial lines because they assume that the data is monotonic in *x* or *y*, which is typically untrue of radial lines.

<a name="radialLine_context" href="#radialLine_context">#</a> <i>radialLine</i>.<b>context</b>([<i>context</i>])

Expand Down Expand Up @@ -591,7 +591,7 @@ Equivalent to [*area*.defined](#area_defined).

<a name="radialArea_curve" href="#radialArea_curve">#</a> <i>radialArea</i>.<b>curve</b>([<i>curve</i>[, <i>parameters…</i>]])

Equivalent to [*area*.curve](#area_curve). Note that [curveMonotone](#curveMonotone) is not recommended for radial areas because it assumes that the data is monotonic in *x*, which is typically untrue of radial areas.
Equivalent to [*area*.curve](#area_curve). Note that [curveMonotoneX](#curveMonotoneX) or [curveMonotoneY](#curveMonotoneY) are not recommended for radial areas because they assume that the data is monotonic in *x* or *y*, which is typically untrue of radial areas.

<a name="radialArea_context" href="#radialArea_context">#</a> <i>radialArea</i>.<b>context</b>([<i>context</i>])

Expand Down Expand Up @@ -682,11 +682,17 @@ Produces a polyline through the specified points.

Produces a closed polyline through the specified points by repeating the first point when the line segment ends.

<a name="curveMonotone" href="#curveMonotone">#</a> d3.<b>curveMonotone</b>(<i>context</i>)
<a name="curveMonotoneX" href="#curveMonotoneX">#</a> d3.<b>curveMonotoneX</b>(<i>context</i>)

<img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/monotone.png" width="888" height="240" alt="monotone">
<img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/monotoneX.png" width="888" height="240" alt="monotoneX">

Produces a cubic spline that [preserves monotonicity](https://en.wikipedia.org/wiki/Monotone_cubic_interpolation) in *y*, as proposed by Steffen in [A simple method for monotonic interpolation in one dimension](http://adsabs.harvard.edu/full/1990A%26A...239..443S): “a smooth curve with continuous first-order derivatives that passes through any given set of data points without spurious oscillations. Local extrema can occur only at grid points where they are given by the data, but not in between two adjacent grid points.” Assumes that the input data is monotonic in *x*.
Produces a cubic spline that [preserves monotonicity](https://en.wikipedia.org/wiki/Monotone_cubic_interpolation) in *y*, assuming monotonicity in *x*, as proposed by Steffen in [A simple method for monotonic interpolation in one dimension](http://adsabs.harvard.edu/full/1990A%26A...239..443S): “a smooth curve with continuous first-order derivatives that passes through any given set of data points without spurious oscillations. Local extrema can occur only at grid points where they are given by the data, but not in between two adjacent grid points.”

<a name="curveMonotoneY" href="#curveMonotoneY">#</a> d3.<b>curveMonotoneY</b>(<i>context</i>)

<img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/monotoneY.png" width="888" height="240" alt="monotoneY">

Produces a cubic spline that [preserves monotonicity](https://en.wikipedia.org/wiki/Monotone_cubic_interpolation) in *x*, assuming monotonicity in *y*, as proposed by Steffen in [A simple method for monotonic interpolation in one dimension](http://adsabs.harvard.edu/full/1990A%26A...239..443S): “a smooth curve with continuous first-order derivatives that passes through any given set of data points without spurious oscillations. Local extrema can occur only at grid points where they are given by the data, but not in between two adjacent grid points.”

<a name="curveNatural" href="#curveNatural">#</a> d3.<b>curveNatural</b>(<i>context</i>)

Expand All @@ -704,13 +710,13 @@ Produces a piecewise constant function (a [step function](https://en.wikipedia.o

<img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/stepAfter.png" width="888" height="240" alt="stepAfter">

Produces a piecewise constant function (a [step function](https://en.wikipedia.org/wiki/Step_function)) consisting of alternating horizontal and vertical lines. The *y*-value changes after the *x*-value.
Produces a piecewise constant function (a [step function](https://en.wikipedia.org/wiki/Step_function)) consisting of alternating horizontal and vertical lines. If the change in *x* is positive, the *y*-value changes after the *x*-value; otherwise the *y*-value changes before the *x*-value.

<a name="curveStepBefore" href="#curveStepBefore">#</a> d3.<b>curveStepBefore</b>(<i>context</i>)

<img src="https://raw.githubusercontent.com/d3/d3-shape/master/img/stepBefore.png" width="888" height="240" alt="stepBefore">

Produces a piecewise constant function (a [step function](https://en.wikipedia.org/wiki/Step_function)) consisting of alternating horizontal and vertical lines. The *y*-value changes before the *x*-value.
Produces a piecewise constant function (a [step function](https://en.wikipedia.org/wiki/Step_function)) consisting of alternating horizontal and vertical lines. If the change in *x* is positive, the *y*-value changes before the *x*-value; otherwise the *y*-value changes after the *x*-value.

### Custom Curves

Expand Down
Binary file removed img/monotone.png
Binary file not shown.
Binary file added img/monotoneX.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/monotoneY.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export {default as curveCatmullRomOpen} from "./src/curve/catmullRomOpen";
export {default as curveCatmullRom} from "./src/curve/catmullRom";
export {default as curveLinearClosed} from "./src/curve/linearClosed";
export {default as curveLinear} from "./src/curve/linear";
export {default as curveMonotone} from "./src/curve/monotone";
export {monotoneX as curveMonotoneX, monotoneY as curveMonotoneY} from "./src/curve/monotone";
export {default as curveNatural} from "./src/curve/natural";
export {default as curveStep, stepAfter as curveStepAfter, stepBefore as curveStepBefore} from "./src/curve/step";

Expand Down
31 changes: 27 additions & 4 deletions src/curve/monotone.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ function point(that, t0, t1) {
that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);
}

function Monotone(context) {
function MonotoneX(context) {
this._context = context;
}

Monotone.prototype = {
MonotoneX.prototype = {
areaStart: function() {
this._line = 0;
},
Expand Down Expand Up @@ -76,6 +76,29 @@ Monotone.prototype = {
}
}

export default function(context) {
return new Monotone(context);
function MonotoneY(context) {
this._context = new ReflectContext(context);
}

(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {
MonotoneX.prototype.point.call(this, y, x);
};

function ReflectContext(context) {
this._context = context;
}

ReflectContext.prototype = {
moveTo: function(x, y) { this._context.moveTo(y, x); },
closePath: function() { this._context.closePath(); },
lineTo: function(x, y) { this._context.lineTo(y, x); },
bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }
};

export function monotoneX(context) {
return new MonotoneX(context);
}

export function monotoneY(context) {
return new MonotoneY(context);
}
20 changes: 10 additions & 10 deletions test/curve/monotone-test.js → test/curve/monotoneX-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ var tape = require("tape"),

require("../pathEqual");

tape("line.curve(curveMonotone)(data) generates the expected path", function(test) {
var l = shape.line().curve(shape.curveMonotone);
tape("line.curve(curveMonotoneX)(data) generates the expected path", function(test) {
var l = shape.line().curve(shape.curveMonotoneX);
test.equal(l([]), null);
test.pathEqual(l([[0, 1]]), "M0,1Z");
test.pathEqual(l([[0, 1], [1, 3]]), "M0,1L1,3");
Expand All @@ -13,22 +13,22 @@ tape("line.curve(curveMonotone)(data) generates the expected path", function(tes
test.end();
});

tape("line.curve(curveMonotone)(data) preserves monotonicity in y", function(test) {
var l = shape.line().curve(shape.curveMonotone);
tape("line.curve(curveMonotoneX)(data) preserves monotonicity in y", function(test) {
var l = shape.line().curve(shape.curveMonotoneX);
test.pathEqual(l([[0, 200], [100, 100], [200, 100], [300, 300], [400, 300]]), "M0,200C33.333333,150,66.666667,100,100,100C133.333333,100,166.666667,100,200,100C233.333333,100,266.666667,300,300,300C333.333333,300,366.666667,300,400,300");
test.end();
});

tape("line.curve(curveMonotone)(data) handles duplicate x-values", function(test) {
var l = shape.line().curve(shape.curveMonotone);
tape("line.curve(curveMonotoneX)(data) handles duplicate x-values", function(test) {
var l = shape.line().curve(shape.curveMonotoneX);
test.pathEqual(l([[0, 200], [0, 100], [100, 100], [200, 0]]), "M0,200C0,200,0,100,0,100C33.333333,100,66.666667,100,100,100C133.333333,100,166.666667,50,200,0");
test.pathEqual(l([[0, 200], [100, 100], [100, 0], [200, 0]]), "M0,200C33.333333,183.333333,66.666667,166.666667,100,100C100,100,100,0,100,0C133.333333,0,166.666667,0,200,0");
test.pathEqual(l([[0, 200], [100, 100], [200, 100], [200, 0]]), "M0,200C33.333333,150,66.666667,100,100,100C133.333333,100,166.666667,100,200,100C200,100,200,0,200,0");
test.end();
});

tape("line.curve(curveMonotone)(data) ignores coincident points", function(test) {
var l = shape.line().curve(shape.curveMonotone),
tape("line.curve(curveMonotoneX)(data) ignores coincident points", function(test) {
var l = shape.line().curve(shape.curveMonotoneX),
p = l([[0, 200], [50, 200], [100, 100], [150, 0], [200, 0]]);
test.equal(l([[0, 200], [0, 200], [50, 200], [100, 100], [150, 0], [200, 0]]), p);
test.equal(l([[0, 200], [50, 200], [50, 200], [100, 100], [150, 0], [200, 0]]), p);
Expand All @@ -38,8 +38,8 @@ tape("line.curve(curveMonotone)(data) ignores coincident points", function(test)
test.end();
});

tape("area.curve(curveMonotone)(data) generates the expected path", function(test) {
var a = shape.area().curve(shape.curveMonotone);
tape("area.curve(curveMonotoneX)(data) generates the expected path", function(test) {
var a = shape.area().curve(shape.curveMonotoneX);
test.equal(a([]), null);
test.pathEqual(a([[0, 1]]), "M0,1L0,0Z");
test.pathEqual(a([[0, 1], [1, 3]]), "M0,1L1,3L1,0L0,0Z");
Expand Down
53 changes: 53 additions & 0 deletions test/curve/monotoneY-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
var tape = require("tape"),
shape = require("../../");

require("../pathEqual");

tape("line.curve(curveMonotoneY)(data) generates the expected path", function(test) {
var l = shape.line().curve(shape.curveMonotoneY);
test.equal(l([]), null);
test.pathEqual(l([[0, 1]].map(reflect)), "M1,0Z");
test.pathEqual(l([[0, 1], [1, 3]].map(reflect)), "M1,0L3,1");
test.pathEqual(l([[0, 1], [1, 3], [2, 1]].map(reflect)), "M1,0C2,0.333333,3,0.666667,3,1C3,1.333333,2,1.666667,1,2");
test.pathEqual(l([[0, 1], [1, 3], [2, 1], [3, 3]].map(reflect)), "M1,0C2,0.333333,3,0.666667,3,1C3,1.333333,1,1.666667,1,2C1,2.333333,2,2.666667,3,3");
test.end();
});

tape("line.curve(curveMonotoneY)(data) preserves monotonicity in y", function(test) {
var l = shape.line().curve(shape.curveMonotoneY);
test.pathEqual(l([[0, 200], [100, 100], [200, 100], [300, 300], [400, 300]].map(reflect)), "M200,0C150,33.333333,100,66.666667,100,100C100,133.333333,100,166.666667,100,200C100,233.333333,300,266.666667,300,300C300,333.333333,300,366.666667,300,400");
test.end();
});

tape("line.curve(curveMonotoneY)(data) handles duplicate x-values", function(test) {
var l = shape.line().curve(shape.curveMonotoneY);
test.pathEqual(l([[0, 200], [0, 100], [100, 100], [200, 0]].map(reflect)), "M200,0C200,0,100,0,100,0C100,33.333333,100,66.666667,100,100C100,133.333333,50,166.666667,0,200");
test.pathEqual(l([[0, 200], [100, 100], [100, 0], [200, 0]].map(reflect)), "M200,0C183.333333,33.333333,166.666667,66.666667,100,100C100,100,0,100,0,100C0,133.333333,0,166.666667,0,200");
test.pathEqual(l([[0, 200], [100, 100], [200, 100], [200, 0]].map(reflect)), "M200,0C150,33.333333,100,66.666667,100,100C100,133.333333,100,166.666667,100,200C100,200,0,200,0,200");
test.end();
});

tape("line.curve(curveMonotoneY)(data) ignores coincident points", function(test) {
var l = shape.line().curve(shape.curveMonotoneY),
p = l([[0, 200], [50, 200], [100, 100], [150, 0], [200, 0]].map(reflect));
test.equal(l([[0, 200], [0, 200], [50, 200], [100, 100], [150, 0], [200, 0]].map(reflect)), p);
test.equal(l([[0, 200], [50, 200], [50, 200], [100, 100], [150, 0], [200, 0]].map(reflect)), p);
test.equal(l([[0, 200], [50, 200], [100, 100], [100, 100], [150, 0], [200, 0]].map(reflect)), p);
test.equal(l([[0, 200], [50, 200], [100, 100], [150, 0], [150, 0], [200, 0]].map(reflect)), p);
test.equal(l([[0, 200], [50, 200], [100, 100], [150, 0], [200, 0], [200, 0]].map(reflect)), p);
test.end();
});

tape("area.curve(curveMonotoneY)(data) generates the expected path", function(test) {
var a = shape.area().curve(shape.curveMonotoneY);
test.equal(a([].map(reflect)), null);
test.pathEqual(a([[0, 1]].map(reflect)), "M1,0L1,0Z");
test.pathEqual(a([[0, 1], [1, 3]].map(reflect)), "M1,0L3,1L3,0L1,0Z");
test.pathEqual(a([[0, 1], [1, 3], [2, 1]].map(reflect)), "M1,0C2,0.333333,3,0.666667,3,1C3,1.333333,2,1.666667,1,2L1,0C1,0,3,0,3,0C3,0,1,0,1,0Z");
test.pathEqual(a([[0, 1], [1, 3], [2, 1], [3, 3]].map(reflect)), "M1,0C2,0.333333,3,0.666667,3,1C3,1.333333,1,1.666667,1,2C1,2.333333,2,2.666667,3,3L3,0C3,0,1,0,1,0C1,0,3,0,3,0C3,0,1,0,1,0Z");
test.end();
});

function reflect(p) {
return [p[1], p[0]];
}

0 comments on commit 510357d

Please sign in to comment.