Checking if two cubic Bézier curves intersect

前端 未结 8 560
星月不相逢
星月不相逢 2020-12-04 14:42

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

8条回答
  •  不知归路
    2020-12-04 15:18

    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:

    • P0=Q0, or more generally, if P0 lies on the second curve (or its extension). One can try to swap the curves.
    • an extremely rare cases, when both curves belong to one K-family (eg. their infinite extensions are the same).
    • keep an eyes on (sqr(c3x)+sqr(c3y))=0 or (sqr(d3x)+sqr(3y))=0 cases, here a quadratic are pretending to be a cubic Bezier curves.

    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.

提交回复
热议问题