Algorithm to cut a catmull-rom spline? - cut

I have a catmull-rom spline with 6 CVs and want to cut it at an arbitrary point along the spline.
How do I calculate the end points of the two new splines so that they keep the shape as they had before they were cut in two?
So, imagine this being the spline with 6 points:
p0 p2 p4
\ / \ / \
\ / \C / \
\ / \ / \
p1 p3 p5
if I want to cut this spline at point C, I will then have two new splines with I guess 5 CVs each?
Spline #1: p0, p1, p2, X, C
Spline #2: C, X, p3, p4, p5
Where X is a CV that I need to add in order to maintain the shape of the spline. But how do I calculate the position that X needs to be at?
Or is my logic wrong altogether and there is a whole different solution to the problem?
Thank you!

Catmull-Rom spline is essentially a spline composed of multiple cubic Bezier curves. So, if you want to divide it at any parameter t, you just need to find out which Bezier curve the parameter lies on, then divide that Bezier curve using the famous De Casteljau algorithm. The divided results are still cubic Bezier curves. However, if you want to find new set of CVs from which the derived Catmull-Rom spline will have exactly the same shape as the split spline from original spline, I would say that is not possible. I will explain in details below.
From Catmull-Rom spline's definition, the original spline defined by 6 CVs:p0, p1,..., p5 will have tangent at p2 in the direction of vector(p1,p3). For a new Catmull-Rom spline defined by 5 CVs: p0, p1, p2, X and C, where C is any point on the original CR spline, the tangent at p2 will be in the direction of vector(p1,X).
If we want the new CR spline to exactly represent the split spline #1 of the original spline, then tangent at p2 from these two splines should at least be in the same direction, which means that point X should lie on the line defined by p1 and p3. This criteria makes it impossible for the new Catmull-Rom spline to exactly represent the shape of split spline #1 from original spline.

Related

Subdivide multi segmented Cubic Bezier spline

First of let me apologies for a bad English and probably not very straight forward question, as I am not really sure how to call it.
I have a multi segmented Cubic Bezier curve In After Effects, it is is defined by 5 vertices with IN & OUT tangents. My task is to subdivide it into N small linear chunks in Java Script.
EDIT submited more info.
Given a multi segmented Cubic Bezier spline defined by 5 points with In & Out tangents, I need to get a linear representation of it. Where N is number of linear segments, defined by user.
Cubic Bezier Spline:
Segment1: P0, P0out, P1in, P1;
Segment2: P1, P1out, P2in, P2;
Segment3: P2, P2out, P3in, P3;
Segment4: P3, P3out, P4in, P4;
Expected output:
N = 1: linear spline with 2 anchors representing entire shape;
N = 2: linear spline with 3 anchors representing entire shape;
N = 3: linear spline with 4 anchors representing entire shape;
N = 4: linear spline with 5 anchors representing entire shape;
...
N = 8: linear spline with 9 anchors representing entire shape;
distance(L0,L1) = distance(L1,L2) = distance(L2,L3) = ... = distance(L-n, Ln)
In example image I use 4-segmented spline, where segment length is equal to one another - this is just easier to draw to explain my task. But in real project those segments will not be equal, and there will be more then 4 segments total.
I have looked at de Casteljau method, but as I can understand, it works with one segment spline. My math skills are dusty, so I am not really sure if I can use de Casteljau in my example.
This is conceptually straight forward, although it might involve quite a bit of code for reasons explained a little later on. What you're trying to do is flatten a (cubic) poly-Bezier, so let's start with that:
Individual cubic Bezier curves are generated by four points: a start point, a control point that determines the tangent at the start point, an end point, and a control point that determines the tangent at the end point. The curve, then, is a plot of the cubic Bezier function:
Bx(t) = p1.x × (1-t)³ + 2 × p2.x × (1-t)² × t + 2 × p3.x × (1-t) × t² + p4.x × t³
By(t) = p1.y × (1-t)³ + 2 × p2.y × (1-t)² × t + 2 × p3.y × (1-t) × t² + p4.y × t³
A single Bezier curve is plotted over the interval t=[0,1], so a poly-Bezier of N segments is plotted over a total interval N × [0,1].
First, the simple case: simple flattening. Bezier curves are non-linear curves and so let's first not bother to enforce "same length for each of the line segments". Given an N-segment poly-Bezier:
S = number of segments we want
points = empty list
for (s = 0):(s = S):(step = S/N):
v = s * step
segmentid = floor(v)
segment = polycurve.segments[segmentid]
t = v % 1
points.push(
segment.pointAt(t)
)
We now have all the points we need, and we just connect them with lines. Done.
However, Bezier curves are non-linear curves, so flattening in this way does not yield equidistant segments in the slightest. If we want to do that, we need to work with distance along the curve rather than t values.
S = number of segments we want
points = empty list
for (s = 0):(s = S):(step = S/N):
v = s * step
segmentid = floor(v)
segment = polycurve.segments[segmentid]
distanceRatio = v % 1
t = segment.getTforDistanceRatio(distanceRatio)
points.push(
segment.pointAt(t)
)
This will work exactly as you want, but getTforDistanceRatio is the hard part, because what we're doing here is reparameterizing the curve for distance, rather than time, and that is a very hard mathematical problem (for which no general symbolic solution exists). The cheapest way to do this is using a Lookup Table (LUT), which is explained in the link above, for "distance along the curve".
The de Casteljau method is used to compute a point on the Bezier curve and also obtain the control points for the two subdivided curves in the process. So, yes you should be able to use de Cateljau method to evaluate as many points as you want on a Bezier curve if you know the control points.
From the picture you show and the fact that your "cubic Bezier spline" takes in/out tangents as input, I think your spline is actually "cubic Hermite spline", in which each segment is indeed a cubic Bezier curve. You can convert each segment of your spline to a cubic Bezier curve, then use de Cateljau method to evaluate as many points as you want, then connect these points by straight lines.

How do Catmull-Rom and Hermite splines relate?

I'm a little confused as to how they relate. From my class, my professor asked how to fit C1 continuous piecewise Hermite curves to x amount of points with automatically computed tangents. Can someone explain how this works?
Hermite spline is a method of representation of cubic curve with two endpoints and two tangent vectors at these endpoints.
Note that there are many approaches to represent the same curve- power basis (at^3+bt^2+ct+d), Bernstein polynomials (for Bezier curves) and so on.
Catmull-Rom spline is a method of construction of a cubic curve (C1 continuous, if some pieces used). Every Catmull-Rom segment is a Hermite spline. If we want to represent a Catmull-Rom spline for the P1-P2 segment of the (P0,P1,P2,P3) point sequence as a Hermite one, we just use P1 and P2 as endpoints, and V1 and V2 as tangent vectors, where
V1 = tau * (P2-P0)
V2 = tau * (P3-P1)
(good article here)
and tau is parameter of tension (rigidness)

Simplify high-order Bezier curve

I have an array of control points that represent a Bezier curve. It could be a fifth-order or 100-order Bezier curve, or anything in-between. I am looking for a way to simplify that Bezier curve into multiple cubic Bezier curves. An illustration below shows how tenth-degree curve can be simplified to three-degree curve, but I want to go further and simplify it to several cubic Bezier curves to achieve better approximation.
Code example would be very helpful.
As mohsenmadi already pointed out: in general this is not a thing you can do without coming up with your own error metric. Another idea is to go "well let's just approximate this curve as a sequence of lower order curves", so that we get something that looks better, and doesn't really require error metrics. This is a bit like "flattening" the curve to lines, except instead of lines we're going to use cubic Bezier segments instead, which gives nice looking curves, while keeping everything "tractable" as far as modern graphics libraries are concerned.
Then what we can do is: split up that "100th order curve" into a sequence of cubic Beziers by sampling the curve at regular intervals and then running those points through a Catmull-Rom algorithm. The procedure's pretty simple:
Pick some regularly spaced values for t, like 0, 0.2, 0.4, 0.6, 0.8 and 1, then
create the set of points tvalues.map(t => getCoordinate(curve, t)). Then,
build a virtual start and end point: forming a point 0 by starting at point 1 and moving back along its tangent, and forminga point n+1 by starting at n and following its tangent. We do this, because:
build the poly-Catmull-Rom, starting at virtual point 0 and ending at virtual point n+1.
Let's do this in pictures. Let's start with an 11th order Bezier curve:
And then let's just sample that at regular intervals:
We invent a 0th and n+1st point:
And then we run the Catmull-Rom procedure:
i = 0
e = points.length-4
curves = []
do {
crset = points.subset(i, 4)
curves.push(formCRCurve(crset))
} while(i++<e)
What does formCRCurve do? Good question:
formCRCurve(points: p1, p2, p3, p4):
d_start = vector(p2.x - p1.x, p2.y - p1.y)
d_end = vector(p4.x - p3.x, p4.y - p3.y)
return Curve(p2, d_start, d_end, p3)
So we see why we need those virtual points: given four points, we can form a Catmull-Rom curve from points 2 to point 3, using the tangent information we get with a little help from points 1 and 4.
Of course, we actualy want Bezier curves, not Catmull-Rom curves, but because they're the same "kind" of curve, we can freely convert between the two, so:
i = 0
e = points.length-4
bcurves = []
do {
pointset = points.subset(i, 4)
bcurves.push(formBezierCurve(pointset))
} while(i++<e)
formBezierCurve(points: p1, p2, p3, p4):
return bezier(
p2,
p2 + (p3 - p1)/6
p3 - (p4 - p2)/6
p3
)
So a Catmull-Rom curve based on points {p1,p2,p3,p4}, which passes through points p2 and p3, can be written as an equivalent Bezier curve that uses the start/control1/control2/end coodinates p2, p2 + (p3 - p1)/6, p3 - (p4 - p2)/6, and p3.
First, you have to know that there are no approximating lower degree curves that would do you justice! You are bound to introduce errors no escape. The questions then is: how to approximate such that the original and resultant curves are visually similar?
Assume your original curve is of degree n. First, subdivide it. You can subdivide a curve as many times as you want without introducing any errors. Here, the degree of each subdivisions is still n, but the geometric complexity and rate of curvature are reduced considerably. Second, you reduce the degree of each subdivision which is by now a simple shape with no high curvature that would introduce approximation errors.

ccBezierConfig as a quadratic, not cubic.

So I'm trying to animate a sprite, and I just found the CCBezierBy/CCBezierTo methods, which are saving me a lot of trouble, but they only work as cubic functions.
HOw can I make them quadratics? (I need s-curves)
Having only 3 controlpoints limits severely what you can do.
Thank you.
I don't think you understand Bezier curves here: S curves cannot be modeled by quadratic Beziers, they can only do curves with a single inflection point. Cubic curves can have two inflection points, so quadratic curves can only model curves that look like | or C, and cubic curves can model anything that looks like |, C or S
Cubic functions have a starting on-curve point, two control points, and an ending on-curve point, for a total of four control points; Quadratic functions have a starting on-curve point, one control point, and an ending on-curve point, for a total of three control points.
So what you want is a cubic curve, and that's exactly what the CCBezierBy/CCBezierTo methods offer. They let you specify a bezier curve as the two control points and end point, because you'll already have the starting point.

Find bezier control-points for curve passing through N points

Considering the following nice solution for finding cubic Bézier control points for a curve passing through 4 points:
How to find control points for a BezierSegment given Start, End, and 2 Intersection Pts in C# - AKA Cubic Bezier 4-point Interpolation
I wonder, if there is a straightforward extension to this for making the Bézier curve pass through N points, for N > 2 and maybe N ≤ 20?
This is a really old question, but I'm leaving this here for people who have the same question in the future.
#divanov has mentioned that there's no Bezier curve passing through N arbitrary points for N >4.
I think the OP was asking how to compute the control points to join multiple bezier curves to produce a single curve that looks smooth.
This pdf will show you how to compute the control points: http://www.math.ucla.edu/~baker/149.1.02w/handouts/dd_splines.pdf
which I found on this writeup https://developer.squareup.com/blog/smoother-signatures/ from Square about how they render a smooth curve that passes through all the sampled points of a mouse drawn signature.
In general, there is no Bezier curve passing through N arbitrary points, where N > 4. One should consider curve fitting to minimize least square error between computed Bezier curve and given N data points. Which is discussed, for example, here.