For a personal project, I\'d need to find out if two cubic Bézier curves intersect. I don\'t need to know where: I just need to know if they do. However, I\'d need to do it
Yes, I know, this thread is accepted and closed for a long time, but...
First, I'd like to thank you, zneak, for an inspiration. Your effort allows to find the right way.
Second, I was not quite happy with the accepted solution. This kind is used in my favorite freeware IPE, and its bugzilla is plenty of complains for a low accuracy and reliability about an intersection issue, my among them.
The missing trick which allows to solve the problem in a manner proposed by zneak : It is enough to shorten one of curves by a factor k>0, then the determinant of Sylvester matrix will be equal to zero. It is obvious, if a shortened curve will intersect, then original will too. Now, the task is changed into the search for a suitable value for k factor. This has led to the problem of solving an univariate polynomial of 9 degree. A real and positive roots of this polynomial are responsible for potential points of intersection. (This shouldn't be a surprise, two cubic Bezier curves can intersect up to 9 times.) The final selection is performed to find only those k factors, which provide 0<=t<=1 for both curves.
Now the Maxima code, where the starting point is set of 8 points provided by zneak :
p0x:1; p0y:1;
p1x:2; p1y:4;
p2x:3; p2y:4;
p3x:4; p3y:3;
q0x:3; q0y:5;
q1x:3; q1y:6;
q2x:0; q2y:1;
q3x:3; q3y:1;
c0x:p0x;
c0y:p0y;
c1x:3*(p1x-p0x);
c1y:3*(p1y-p0y);
c2x:3*(p2x+p0x)-6*p1x;
c2y:3*(p2y+p0y)-6*p1y;
c3x:3*(p1x-p2x)+p3x-p0x;
c3y:3*(p1y-p2y)+p3y-p0y;
d0x:q0x;
d0y:q0y;
d1x:3*(q1x-q0x);
d1y:3*(q1y-q0y);
d2x:3*(q2x+q0x)-6*q1x;
d2y:3*(q2y+q0y)-6*q1y;
d3x:3*(q1x-q2x)+q3x-q0x;
d3y:3*(q1y-q2y)+q3y-q0y;
x:c0x-d0x + (c1x-d1x*k)*t+ (c2x-d2x*k^2)*t^2+ (c3x-d3x*k^3)*t^3;
y:c0y-d0y + (c1y-d1y*k)*t+ (c2y-d2y*k^2)*t^2+ (c3y-d3y*k^3)*t^3;
z:resultant(x,y,t);
At this point, Maxima answered:
(%o35)−2*(1004*k^9−5049*k^8+5940*k^7−1689*k^6+10584*k^5−8235*k^4−2307*k^3+1026*k^2+108*k+76)
Let Maxima solve this equation:
rr: float( realroots(z,1e-20))
The answer is:
(%o36) [k=−0.40256438624399,k=0.43261490325108,k=0.84718739982868,k=2.643321910825066,k=2.71772491293651]
Now the code to select a right value of k:
for item in rr do (
evk:ev(k,item),
if evk>0 then (
/*print("k=",evk),*/
xx:ev(x,item), rx:float( realroots(xx,1e-20)),/*print("x(t)=",xx," roots: ",rx),*/
yy:ev(y,item), ry:float( realroots(yy,1e-20)),/*print("y(t)=",yy," roots: ",ry),*/
for it1 in rx do ( t1:ev(t,it1),
for it2 in ry do ( t2:ev(t,it2),
dt:abs(t1-t2),
if dt<1e-10 then (
/*print("Common root=",t1," delta t=",dt),*/
if (t1>0) and (t1<=1) then ( t2:t1*evk,
if (t2>0) and (t2<=1) then (
x1:c0x + c1x*t1+ c2x*t1^2+ c3x*t1^3,
y1:c0y + c1y*t1+ c2y*t1^2+ c3y*t1^3,
print("Intersection point: x=",x1, " y=",y1)
)))))/*,disp ("-----")*/
));
Maxima's answer:
"Intersection point: x="1.693201254437358" y="2.62375005067273
(%o37) done
Theres not only a honey, though. The presented method is unusable, if:
One can ask, why a shortening is performed only once. It's enough because of reverse-inverse law, which was discovered en passant, but this is an another story.