d3 / d3-shape

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

Cubic monotone interpolation. #38

Closed mbostock closed 8 years ago

mbostock commented 8 years ago
mbostock commented 8 years ago

Test case from Wikipedia:

screen shot 2015-11-26 at 1 33 23 pm

Seems to differ a bit from Fritsch–Carlson. And actually centripetal Catmull–Rom splines look a bit better in this case while coincidentally preserving monotonicty.

ghost commented 8 years ago

If you happen to be looking for data that produces some overshoot then at least points [[0, 224], [100, 200], [107, 180], [502, 300], [580, 120], [607, 148], [900, 400]] seem to do the trick with natural and catmullRom.

mbostock commented 8 years ago

Here’s your example:

screen shot 2015-11-27 at 3 33 40 pm

I agree that uniform Catmull–Rom splines don’t do very well here (red, α = 0), but centripetal does (green, α = 0.5). I also think from this example that using zero slope for the first and last point may not be the best default behavior, although it is simple…

mbostock commented 8 years ago

I changed the default α for Catmull–Rom splines to 0.5 in 5a21ab2feade0c6bd2db38f144a7666a475a8607. This is a better default behavior and also has the nice side-effect that curve(cardinal) and curve(catmullRom) now behave differently.

ghost commented 8 years ago

Yeah. The monotone ones (be it Steffen or Fritsch-Carlson) are somewhat useful for plotting smooth lines through data points where you might want to avoid overshoot but can't choose the curve parameters beforehand. They rarely provide the best looking solution though.

I'll have a look at replacing the zero slopes at boundaries with something better. For example just choosing the slope between the boundary point and its neighbor might be ok.

mbostock commented 8 years ago

Yes, the monotonicity guarantee is still useful for data visualization even if centripetal Catmull–Rom is perhaps the best in the general case.

And yep, that’s what I would try for the first and last slope, too. Send me a PR to the monotone branch if you get it working? Thanks.

ghost commented 8 years ago

I sent the PR (and of course messed up the target branch, sigh), but here's a comparison similar to the one you made with the different boundary handling applied:

curves
mbostock commented 8 years ago

Nice. This looks great!

screen shot 2015-11-27 at 8 30 40 pm

I noticed an edge case that needs testing. If two adjacent points have the same x-value, it has different behavior than if the two points are separated by a small ε. Here’s x₀ = x₁:

screen shot 2015-11-27 at 8 33 26 pm

And here’s x₁ - x₀ = 1e-6:

screen shot 2015-11-27 at 8 35 23 pm

I think this is just an issue with the first and last segment, though. The rest of the code seems to handle duplicate x-values okay. Though, it doesn’t appear to gracefully handle data that is not monotonic in x. But that’s probably fine to ignore for this curve, and document it as an assumption.

mbostock commented 8 years ago

Okay, pushed a fix in c1a9743.

ghost commented 8 years ago

Great, thank you!