简体   繁体   中英

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 rxs = 10*10 + (-10*10) = 0 which would pass up to the second case where parallelity is assumed, even though the segments are not.

PS Is there a way to make this code more readable?

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)

Additionally, here is a port of Paul Bourke's Intersection point of two line segments in 2 dimensions algorithm using the C# version by Olaf Rabbachin:

Line l1 = new Line(new PVector(10,10),new PVector(390,390));
Line l2 = new Line(new PVector(10,390),new PVector(390,10));

void setup(){
  size(400,400);  
  fill(0);
}
void draw(){
  //draw background and lines
  background(255);
  l1.draw();
  l2.draw();
  text("click and drag",10,15);
  //check intersections (and draw)
  PVector intersection = doLinesIntersect(l1,l2);
  if(intersection != null){
    ellipse(intersection.x,intersection.y,15,15);
  }
}

void mouseDragged(){
  l1.p1.x = mouseX;
  l1.p1.y = mouseY;
}

class Line{
  PVector p1 = new PVector();
  PVector p2 = new PVector();

  Line(PVector start,PVector end){
    p1 = start;
    p2 = end;
  }
  void draw(){
    line(p1.x,p1.y,p2.x,p2.y);
  }
}
//port of http://paulbourke.net/geometry/pointlineplane/Helpers.cs
PVector doLinesIntersect(Line l1, Line l2){
   // Denominator for ua and ub are the same, so store this calculation
   float d =
      (l2.p2.y - l2.p1.y) * (l1.p2.x - l1.p1.x)
      -
      (l2.p2.x - l2.p1.x) * (l1.p2.y - l1.p1.y);

   //n_a and n_b are calculated as seperate values for readability
   float n_a =
      (l2.p2.x - l2.p1.x) * (l1.p1.y - l2.p1.y)
      -
      (l2.p2.y - l2.p1.y) * (l1.p1.x - l2.p1.x);

   float n_b =
      (l1.p2.x - l1.p1.x) * (l1.p1.y - l2.p1.y)
      -
      (l1.p2.y - l1.p1.y) * (l1.p1.x - l2.p1.x);

   // Make sure there is not a division by zero - this also indicates that
   // the lines are parallel.  
   // If n_a and n_b were both equal to zero the lines would be on top of each 
   // other (coincidental).  This check is not done because it is not 
   // necessary for this implementation (the parallel check accounts for this).
   if (d == 0)
      return null;

   // Calculate the intermediate fractional point that the lines potentially intersect.
   float ua = n_a / d;
   float ub = n_b / d;

   // The fractional point will be between 0 and 1 inclusive if the lines
   // intersect.  If the fractional calculation is larger than 1 or smaller
   // than 0 the lines would need to be longer to intersect.
   if (ua >= 0d && ua <= 1d && ub >= 0d && ub <= 1d)
   {
      PVector intersection = new PVector();
      intersection.x = l1.p1.x + (ua * (l1.p2.x - l1.p1.x));
      intersection.y = l1.p1.y + (ua * (l1.p2.y - l1.p1.y));
      return intersection;
   }
   return null;  
}

Also, you might find toxiclibs useful and it's intersectLine Line2D functionality . Be aware that there are some issues with Processing 3

Update

You can run a demo bellow:

 var l1,l2; function setup() { createCanvas(400,400); fill(0); l1 = new Line(); l2 = new Line(); l1.p1.x = l1.p1.y = 10; l1.p2.x = l1.p2.y = 390; l2.p1.x = 10; l2.p1.y = 390; l2.p2.x = 390; l2.p2.y = 10; } function draw() { //draw background and lines background(255); l1.draw(); l2.draw(); text("click and drag",10,15); //check intersections (and draw) var intersection = doLinesIntersect(l1,l2); if(intersection != null){ ellipse(intersection.x,intersection.y,15,15); } } function mouseDragged(){ l1.p1.x = mouseX || touchX; l1.p1.y = mouseY || touchY; } //port of http://paulbourke.net/geometry/pointlineplane/Helpers.cs function doLinesIntersect(l1, l2){ // Denominator for ua and ub are the same, so store this calculation var d = (l2.p2.y - l2.p1.y) * (l1.p2.x - l1.p1.x) - (l2.p2.x - l2.p1.x) * (l1.p2.y - l1.p1.y); //n_a and n_b are calculated as seperate values for readability var n_a = (l2.p2.x - l2.p1.x) * (l1.p1.y - l2.p1.y) - (l2.p2.y - l2.p1.y) * (l1.p1.x - l2.p1.x); var n_b = (l1.p2.x - l1.p1.x) * (l1.p1.y - l2.p1.y) - (l1.p2.y - l1.p1.y) * (l1.p1.x - l2.p1.x); // Make sure there is not a division by zero - this also indicates that // the lines are parallel. // If n_a and n_b were both equal to zero the lines would be on top of each // other (coincidental). This check is not done because it is not // necessary for this implementation (the parallel check accounts for this). if (d == 0) return null; // Calculate the intermediate fractional point that the lines potentially intersect. var ua = n_a / d; var ub = n_b / d; // The fractional point will be between 0 and 1 inclusive if the lines // intersect. If the fractional calculation is larger than 1 or smaller // than 0 the lines would need to be longer to intersect. if (ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0) { var intersection = createVector(); intersection.x = l1.p1.x + (ua * (l1.p2.x - l1.p1.x)); intersection.y = l1.p1.y + (ua * (l1.p2.y - l1.p1.y)); return intersection; } return null; } function Line(){ this.p1 = createVector(); this.p2 = createVector(); this.draw = function(){ line(this.p1.x,this.p1.y,this.p2.x,this.p2.y); } }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.3/p5.min.js"></script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM