views:

254

answers:

1

Project a cubic bezier p1,p2,p3,p4 onto the line p1,p4. When p2 or p3 does not project onto the line segment between p1 and p4, the curve will bulge out from the anchor points. Is there a way to calculate the T value where the tangent of the curve is perpendicular to the anchor line?

This could also be stated as finding the T values where the projected curve is farthest from the center of the line segment p1,p4. When p2 and p3 project onto the line segment, then the solutions are 0 and 1 respectively. Is there an equation for solving the more interesting case?

The T value seems to depend only on the distance of the mapped control points from the anchor line segment.

I can determine the value by refining guesses, but I am hoping there is a better way.

Edit:

Starting with p1,..,p4 in 2d with values x1,y1, ..., x4,y4 I use the following code based on the answer from Philippe:

dx = x4 - x1;
dy = y4 - y1;
d2 = dx*dx + dy*dy;
p1 = ( (x2-x1)*dx + (y2-y1)*dy ) / d2;
p2 = ( (x3-x1)*dx + (y3-y1)*dy ) / d2;
tr = sqrt( p1*p1 - p1*p2 - p1 + p2*p2 );
t1 = ( 2*p1 - p2 - tr ) / ( 3*p1 - 3*p2 + 1 );
t2 = ( 2*p1 - p2 + tr ) / ( 3*p1 - 3*p2 + 1 );

In the sample I looked at, t2 had to be subtracted from 1.0 before it was correct.

+1  A: 

Let's assume you got a 1D cubic Bézier curve with P0 = 0 and P3 = 1 then the curve is:

P(t) = b0,3(t)*0 + b1,3(t)*P1 + b2,3(t)*P2 + b3,3(t)*1

Where bi,3(t) are the Bernstein polynomials of degree 3. Then we're looking for the value of t where this P(t) is minimal and maximal, so we derive:

P'(t) = b1,3'(t)*P1 + b2,3'(t)*P2 + b3,3'(t)
      = (3 - 12t + 9t^2)*P1 + (6t - 9t^2)*P2 + 3t^2
      = 0

This has a closed-form but nontrivial solution. According to WolframAlpha, when 3P1 - 3P2 +1 != 0 it's:

t = [2*P1 - P2 +/- sqrt(P1^2-P1*P2-P1+P2^2)] / (3*P1 - 3*P2 + 1)

Otherwise it's:

t = 3P1 / (6P1 - 2)

For a general n-dimensional cubic Bézier P0*, P1*, P2*, P3* compute:

P1 = proj(P1*, P03*) / |P3* - P0*|
P2 = proj(P2*, P03*) / |P3* - P0*|

Where proj(P, P03*) is the signed distance from P0* to the point P projected on the line passing through P0* and P3*.

(I haven't checked this, so please confirm there is nothing wrong in my reasoning.)

Philippe Beaudoin
sqrt( ... P2*2 ) should be P2^2
drawnonward
Thank you, this works as expected. When getting two valid values from the +/- the + result needs to be subtracted from 1.0 in my trials. It may be the other way round sometimes.
drawnonward
It could come from the way you compute `proj()`. It needs to be the signed distance from `P0*`, meaning if it projects to the side of `P0*` away from `P3*` it should be negative.
Philippe Beaudoin