views:

322

answers:

7

How can I check if 2 segments intersect ?

I've the following data:

Segment1 [ {x1,y1}, {x2,y2} ]
Segment2 [ {x1,y1}, {x2,y2} ] 

I need to write a small algorithm in python to detect if the 2 lines are intersecting.

thanks

Update: alt text

+1  A: 

The formula for a line is:

f(x) = A*x + b = y

For a segment, it is exactly the same, except that x is included into an interval I.

If you have two segments, defined as follow:

Segment1 = {(X1, Y1), (X2, Y2)}
Segment2 = {(X3, Y3), (X4, Y4)}

The abcisse Xa of the potential point of intersection (Xa,Ya) must be contained in both interval I1 and I2, defined as follow :

I1 = [MIN(X1,X2), MAX(X1,X2)]
I2 = [MIN(X3,X4), MAX(X3,X4)]

And we could say that Xa is included into :

Ia = [MAX( MIN(X1,X2), MIN(X3,X4) ), MIN( MAX(X1,X2), MAX(X3,X4)] )]

Now, you need to check that this interval Ia exists :

if (MAX(X1,X2) < MIN(X3,X4))
    return false; // There is no mutual abcisses

So, you got two line formula, and a mutual interval. Your line formulas are:

f1(x) = A1*x + b1 = y
f2(x) = A2*x + b2 = y

As we got two points by segment, we are able to determine A1, A2, b1 and b2:

A1 = (Y1-Y2)/(X1-X2) // Pay attention to not dividing by zero
A2 = (Y3-Y4)/(X3-X4) // Pay attention to not dividing by zero
b1 = Y1-A1*X1 = Y2-A1*X2
b2 = Y3-A2*X3 = Y4-A2*X4

If the segments are parallel, then A1 == A2 :

if (A1 == A2)
    return false; // Parallel segments

A point (Xa,Ya) standing on both line must verify both formulas f1 and f2:

Ya = A1 * Xa + b1
Ya = A2 * Xa + b2
A1 * Xa + b1 = A2 * Xa + b2
Xa = (b2 - b1) / (A1 - A2) // Once again, pay attention to not dividing by zero

The last thing to do is check that Xa is included into Ia:

if ( (Xa < MAX( MIN(X1,X2), MIN(X3,X4) )) ||
  (Xa > MIN( MAX(X1,X2), MAX(X3,X4) )) )
    return false; // intersection is out of bound
else
    return true;

In addition to this, you may check at startup that two of the four provided points are not equals to avoid all that testing.

OMG_peanuts
@OMG_peanuts Segments, they are segments, sorry. Could you update your answer given segments ?
Patrick
@OMG_peanuts ok thanks I will go through the code. Wow isn't complicated ? I thought it was not that long solution. But if it's the only one.. well let's do it.
Patrick
This is not so complicated, i wrote a lots of (unessential ?) intermediate steps in a comprehension purpose. The main points to implements are just : Check mutual interval existence, calculate A1, A2, b1, b2, and Xa, and then check that Xa is included in the mutual interval. That's all :)
OMG_peanuts
@OMG_peanuts: you are right, sorry it's many years I don't handle math :)
Patrick
A: 

if your data define line you just have to prove that they are not parallel. To do this you can compute

alpha = float(y2 - y1) / (x2 - x1).

If this coefficient is equal for both Line1 and Line2, it means the line are parallel. If not, it means they will intersect.

If they are parallel you then have to prove that they are not the same. For that, you compute

beta = y1 - alpha*x1

If beta is the same for Line1 and Line2,it means you line intersect as they are equal

If they are segment, you still have to compute alpha and beta as described above for each Line. Then you have to check that (beta1 - beta2) / (alpha1 - alpha2) is greater than Min(x1_line1, x2_line1) and less than Max(x1_line1, x2_line1)

PierrOz
@PierrOz Segments, they are segments, sorry. Could you update your answer given segments ?
Patrick
A: 

Calculate the intersection point of the lines laying on your segments (it means basically to solve a linear equation system), then check whether is it between the starting and ending points of your segments.

kosii
A: 

You have two line segments. Define one segment by endpoints A & B and the second segment by endpoints C & D. There is a nice trick to show that they must intersect, WITHIN the bounds of the segments. (Note that the lines themselves may intersect beyond the bounds of the segments, so you must be careful. Good code will also watch for parallel lines.)

The trick is to test that points A and B must line on opposite sides of line CD, AND that points C and D must lie on opposite sides of line AB.

Since this is homework, I won't give you an explicit solution. But a simple test to see which side of a line a point falls on, is to use a dot product. Thus, for a given line CD, compute the normal vector to that line (I'll call it N_C.) Now, simply test the signs of these two results:

dot(A-C,N_C)

and

dot(B-C,N_C)

If those results have opposite signs, then A and B are opposite sides of line CD. Now do the same test for the other line, AB. It has normal vector N_A. Compare the signs of

dot(C-A,N_A)

and

dot(D-A,N_A)

I'll leave it to you to figure out how to compute a normal vector. (In 2-d, that is trivial, but will your code worry about whether A and B are distinct points? Likewise, are C and D distinct?)

You still need to worry about line segments that lie along the same infinite line, or if one point actually falls on the other line segment itself. Good code will cater to every possible problem.

woodchips
+2  A: 

Suppose the two segments have endpoints A,B and C,D. The numerically robust way to determine intersection is to check the sign of the four determinants:

| Ax-Cx  Bx-Cx |    | Ax-Dx  Bx-Dx |
| Ay-Cy  By-Cy |    | Ay-Dy  By-Dy |

| Cx-Ax  Dx-Ax |    | Cx-Bx  Dx-Bx |
| Cy-Ay  Dy-Ay |    | Cy-By  Dy-By |

For intersection, each determinant on the left must have the opposite sign of the one to the right, but there need not be any relationship between the two lines. You are basically checking each point of a segment against the other segment to make sure they lie on opposite sides of the line defined by the other segment.

See here: http://www.cs.cmu.edu/~quake/robust.html

Victor Liu
+2  A: 

You don't have to compute exactly where does the segments intersect, but only understand whether they intersect at all. This will simplify the solution.

The idea is to treat one segment as the "anchor" and separate the second segment into 2 points.
Now, you will have to find the relative position of each point to the "anchored" segment (OnLeft, OnRight or Collinear).
After doing so for both points, check that one of the points is OnLeft and the other is OnRight (or perhaps include Collinear position, if you wish to include improper intersections as well).

Implementing such method will be much easier than actually implementing a method that finds the intersection point (given the many corner cases which you will have to handle as well).

Update

The following functions should illustrate the idea (source: Computational Geometry in C).
Remark: This sample assumes the usage of integers. If you're using some floating-point representation instead (which could obviously complicate things), then you should determine some epsilon value to indicate "equality" (mostly for the IsCollinear evaluation).

// points "a" and "b" forms the anchored segment.
// point "c" is the evaluated point
bool IsOnLeft(Point a, Point b, Point c)
{
     return Area2(a, b, c) > 0;
}

bool IsOnRight(Point a, Point b, Point c)
{
     return Area2(a, b, c) < 0;
}

bool IsCollinear(Point a, Point b, Point c)
{
     return Area2(a, b, c) == 0;
}

// calculates the triangle's size (formed by the "anchor" segment and additional point)
int Area2(Point a, Point b, Point c)
{
     return (b.X - a.X) * (c.Y - a.Y) -
            (c.X - a.X) * (b.Y - a.Y);
}

Of course, when using these function, one must remember to check that each segment lays "between" the other segment (since these are finite segments, and not infinite lines).

Also, using these function you can understand whether you've got a proper or improper intersection.

  • Proper: There are no collinear points. The segments crosses each other "from side to side".
  • Improper: One segment only "touches" the other (at least one of the points is collinear to the anchored segment).
Liran
+1 Pretty much my idea too. If you just think about where the points are in relation to each other, you can decide if their segments must to intersect or not, without calculating anything.
THC4k
@Liran and @THC4k Uhm, it is actually not clear. FOr example check the image I've added to the question: the 2 points are "OnLeft" and "OnRight" but the 2 segments are not intersecting.
Patrick
@Patrick, actually no. Depending on which of the segments is the "anchor", then both points are either OnLeft or OnRight in this case. (See my updated answer).
Liran
A: 

This is what I've got for AS3, don't know much about python but the concept is there

    public function getIntersectingPointF($A:Point, $B:Point, $C:Point, $D:Point):Number {
        var A:Point = $A.clone();
        var B:Point = $B.clone();
        var C:Point = $C.clone();
        var D:Point = $D.clone();
        var f_ab:Number = (D.x - C.x) * (A.y - C.y) - (D.y - C.y) * (A.x - C.x);

        // are lines parallel
        if (f_ab == 0) { return Infinity };

        var f_cd:Number = (B.x - A.x) * (A.y - C.y) - (B.y - A.y) * (A.x - C.x);
        var f_d:Number = (D.y - C.y) * (B.x - A.x) - (D.x - C.x) * (B.y - A.y);
        var f1:Number = f_ab/f_d
        var f2:Number = f_cd / f_d
        if (f1 == Infinity || f1 <= 0 || f1 >= 1) { return Infinity };
        if (f2 == Infinity || f2 <= 0 || f2 >= 1) { return Infinity };
        return f1;
    }

    public function getIntersectingPoint($A:Point, $B:Point, $C:Point, $D:Point):Point
    {
        var f:Number = getIntersectingPointF($A, $B, $C, $D);
        if (f == Infinity || f <= 0 || f >= 1) { return null };

        var retPoint:Point = Point.interpolate($A, $B, 1 - f);
        return retPoint.clone();
    }
Daniel