A centripetal Catmull-Rom spline: like Curves.Spline.Type.CatmullRom, but it takes the
distances between the knots into account, so it never makes little loops or sharp points
(cusps), even when some knots are much closer together than others.
You only give the knots, and the curve passes through every one of them. The :alpha option,
between 0.0 and 1.0, sets how much the distances count:
:alpha | Name | The curve... |
|---|---|---|
0.0 | uniform | is exactly a Curves.Spline.Type.CatmullRom spline |
0.5 (the default) | centripetal | never loops or makes cusps within a segment |
1.0 | chordal | bends even more gently near knots that are far apart |
A spline with n knots has n - 1 segments, like Catmull-Rom.
# Two knots close together between two far away: plain Catmull-Rom makes a little loop here.
curve = Curves.define_centripetal_catmull_rom([{0, 0}, {10, 10}, {10.5, 10}, {20, 0}])
# Find any point along the curve (u goes from 0.0 to Curves.max_u(curve))...
Curves.solve!(curve, 1.5)
# ...or draw the whole curve as an SVG image
Curves.Svg.save!(curve, "centripetal_catmull_rom.svg")| Plain Catmull-Rom | Centripetal |
|---|---|
How it is built
Each segment, from knot p1 to knot p2, looks at the knots either side, p0 and p3. With
d01, d12 and d23 for the distances between them, each raised to the power alpha:
leaving p1 = p2 - p1 + d12 * ((p1 - p0) / d01 - (p2 - p0) / (d01 + d12))
arriving p2 = p2 - p1 + d12 * ((p3 - p2) / d23 - (p3 - p1) / (d12 + d23))Each segment is the Hermite segment with those tangents, stored as a cubic Bezier curve, and
u still adds one per segment. A knot's tangent can be longer on one side than the other, so the
speed along the curve can jump at a knot, but its direction never does.
The first and last knots have no knot on one side, so they get a made-up one, mirrored through the end (the same as Catmull-Rom). Two knots in the same place make a segment that stays on that point; a knot's neighbour in the same place is treated like the end of the curve.