algorithmgeometryprocessingcollisionsegments

Segment intersection implementation


I was trying to implement the vector based segment intersection algorithm described here(the most up-voted answer) in Java but as it usually goes with implementing algorithms you don't fully understand, I keep failing. I would very much appreciate if someone could proofread my code and tell me what I'm doing wrong.

boolean intersect(PVector p, PVector r, PVector q, PVector s){
  // r x s
  double rxs = r.x*s.y + r.y*s.x;
  //(q-p) x r
  double qpxr = (q.x-p.x)*r.y + (q.y-p.y)*r.x;

  PVector qp = q.sub(p).copy(); //q - p
  //case one lines parallel might intersect:
  if(rxs==0 && qpxr==0){
    println("case one: collinear might intersect");
    double t0 = qp.dot(r);
    double t1 = qp.dot(r)/r.dot(r)+(s.dot(r)/r.dot(r));
    return max((float)t0 , 0.f) <= min((float)t1, 1.f);//if ranges intersect the segments intersect
  }else if(rxs==0 && qpxr!=0){
    println("case two: parallel no intersect");
    return false;
  }else if(rxs!=0){
    println("case three");
    double u = qpxr/rxs;
    double t = (qp.x*r.y + qp.y*r.x)/rxs;
    if((u>=0 && u<=1) && (t<=1 && t>=0)){
      PVector intersect = p.copy();
      intersect.add(r.copy().mult((float)t));
      point(intersect.x, intersect.y);
      return true;
    }else{
      println("no intersect");
      print(u);
      print(" ");
      println(t);
    }
  }else{
    println("case four no intersect");
    return false;
  }
  return false;
}

Also I tried working some answers by hand and still seemed to fail, so now I'm sort of lost. For example:

p=(0;0), r=(10;10)
q=(10;0), s=(-10;10)

then the r x s = 10*10 + (-10*10) = 0 which would pass up to the second case where parallelity is assumed, even though the segments are not.

P.S. Is there a way to make this code more readable?


Solution

  • There's a reference on topcoder that describes how to get where 2 segments intersect. If you're just wanting to know if the lines intersect you check if A1*B2 - A2*B1 == 0 given:

    A1 = y2-y1 B1 = x1-x2

    A2 = y4-y3 B2 = x3-x4

    The basic intuition just being that since you have the equations for point where the segments intersect as:

    x = (C1*B2 - B1*C2)/(A1*B2 - A2*B1)

    y = (A1*C2 - A2*C1)/(A1*B2 - A2*B1)

    You can't divide by 0.

    If the lines that contain the line segments do intersect somewhere not contained in the range of the line segments you check something like

    boolean inRange(double x1,double y1,double x2,double y2,double x3,double y3){
            double dx = Math.abs(x3-x2);
            double dy = Math.abs(y3-y2);
            if (Math.abs(x3-x1)+Math.abs(x2-x1)==dx &&
               Math.abs(y3-y1)+Math.abs(y2-y1)==dy){
                return true;
            }
            return false;
        }
    

    so the condition should look something like

    if (!inRange(...) || (A1*B2 - A2*B1 == 0)) return false; 
    

    If you want a crude way of "deriving" the above equations for x and y you start with the equations of the 2 line segments and solve the system.

    A1*x + B1*y = C1 A2*x + B2*y = C2

    Solve for x on the left

    x = (C1
-B1*y)/A1

    plug back into right and solve for y

    A2*((C1
-B1*y)/A1) + B2*y = C2

    y*(B2-(A2*B1/A1)) = C2 - (A2*C1/A1)

    to make the equation look like

    y = (A1*C2 - A2*C1)/(A1*B2-A2*B1)

    multiply top and bottom of fraction by A1

    Then plug y back into the above equation for x ("x = (C1
-B1*y)/A1")

    x = (C1/A1) - (B1*A1*C2-B1*A2*C1)/A1*(A1*B2 - A2*B1)

    x = ((C1*A1*B2 - B1*A2*C1)-(B1*A1*C2 - B1*A2*C1))/A1*(A1*B2 - A2*B1)

    x = (C1*A1*B2 - B1*A1*C2)/A1*(A1*B2 - A2*B1)

    divide out A1 from top to get equation given in link:

    x = (C1*B2 - B1*C2)/(A1*B2 - A2*B1)