d3 / d3-shape

Graphical primitives for visualization, such as lines and areas.
https://d3js.org/d3-shape
ISC License
2.47k stars 307 forks source link

Step curve could allow NaN x or y for discontinuities. #65

Open mbostock opened 8 years ago

mbostock commented 8 years ago

From d3/d3#2832: if the y-value is NaN, we can still extend the line using the previous x-value. But we’d have to make sure that it’s symmetric for x and y rather than only supporting undefined y. This would be an alternative to using line.defined and area.defined for missing data.

Presumably we’d only want to extend the previous value for a single data point, however? Like if you get two points in a row with NaN y, you’d want to extend a horizontal line from the previous x to the first x with undefined y, but not the second x.

mbostock commented 8 years ago

This might require using a different trick to make the step curve symmetric:

var t = x > this._x ? this._t : 1 - this._t;

Perhaps something like curve.reverse() which returns a reverse curve to be used as the area’s baseline. If undefined, the curve is used.

mbostock commented 8 years ago

I’ve simplified the step curve in e3b1b0ae89259c971cd24a279950c838c439c725, but there’s another issue that makes this difficult to implement: if the curve processes partially-defined points, it must use context.moveTo to render a discontinuity for a line, and likewise it must generate discontiguous area segments for an area.

It’s probably not possible for the step curve to produce discontiguous area segments internally; that’s primarily the responsibility of the area shape, which buffers the input coordinates so that it can pass the reversed xy coordinates of the baseline to the curve.

mbostock commented 8 years ago

I think you could maybe implement this by first detecting whether x or y was NaN inside the area and line shapes, and treating that as similar to the defined accessor returning falsey. Then, the shape would call curve.lineEnd (or maybe curve.point again?), passing in the partially-defined x and y, before terminating the line segment. That would give the curve the opportunity to draw the terminating tail to a partially-defined value, while keeping the responsibility to generate discontiguous line and area segments with the shape. You’d need to do something similar on curve.lineStart, passing in the last (possibly) partially-defined x and y before the first point in the new segment; this way the stepBefore curve could render a preceding horizontal line before the first defined value.