views:

2766

answers:

9

For a game I am developing I need an algorithm that can calculate intersections. I have solved the problem, but the way I have done it is really nasty and I am hoping someone here might have a more elegant solution.

A pair of points represent the end points of a line drawn between them. Given two pairs of points, do the drawn lines intersect, and if so, at what point?

So for example call the lines (A.x, A.y)-(B.x, B.y) and (C.x, C.y)-(D.x, D.y)

Can anyone think of a solution? A solution in any language will do.

Edit: A point I should have made clearer, the algorithm must return false if the point of intersection is beyond the lengths of the line segments. I.E. this isn't an intersecition: lines.gif

A: 

Here are some nice stuff to read:

http://mathworld.wolfram.com/Intersection.html

http://www.idevgames.com/forum/showthread.php?t=8784

I think you will find what you need here.

Filip Ekberg
+2  A: 

You need to solve the simultaneous equations for the two lines.

See here for the algorithm with some code.

frankodwyer
Finding the point of intersection of two line equations is simple, what I have found hard is the lines only extend across a limited domain.
+1  A: 
public struct PointD
{
   pubic double X
{get;set;}
public double Y
{get;set;}
}


 /// <summary>
 /// Find the intersection point between two lines.
 /// </summary>
 /// <param name="IntersectPoint">The intersection point. A <see cref="Esteem.Geometry.PointD">PointD</see> structure.</param>
 /// <param name="L1StartPoint">The starting point of first line. A PointD structure.</param>
 /// <param name="L1EndPoint">The end point of first line. A PointD structure.</param>
 /// <param name="L2StartPoint">The starting point of second line. A PointD structure.</param>
 /// <param name="L2EndPoint">The end point of second line. A PointD structure.</param>
 /// <param name="L1IntersectPos">The intersection position at first line.</param>
 /// <param name="L2IntersectPos">The intersection position at second line.</param>
 /// <returns>Returns a boolean. True if there is intersection, false otherwise.</returns>
 /// <remarks>The formula is taken from comp.graphics.algorithms Frequently Asked Questions.</remarks>
 public static bool LineIntersect(out PointD IntersectPoint, PointD L1StartPoint, PointD L1EndPoint, PointD L2StartPoint, PointD L2EndPoint, out double L1IntersectPos, out double L2IntersectPos) 
 {
  IntersectPoint = new PointD();
  double ay_cy, ax_cx, px, py;
  double dx_cx = L2EndPoint.X - L2StartPoint.X,
   dy_cy = L2EndPoint.Y - L2StartPoint.Y,
   bx_ax = L1EndPoint.X - L1StartPoint.X,
   by_ay = L1EndPoint.Y - L1StartPoint.Y;

  double de = (bx_ax) * (dy_cy) - (by_ay) * (dx_cx);
  //double tor = 1.0E-10;  //tolerance


  L1IntersectPos = 0;   L2IntersectPos = 0;
  if (Math.Abs(de)<0.01)
   return false;
  //if (de > -tor && de < tor) return false; //line is in parallel

  ax_cx = L1StartPoint.X - L2StartPoint.X;
  ay_cy = L1StartPoint.Y - L2StartPoint.Y;
  double r = ((ay_cy) * (dx_cx) - (ax_cx) * (dy_cy)) / de;
  double s = ((ay_cy) * (bx_ax) - (ax_cx) * (by_ay)) / de;
  px = L1StartPoint.X + r * (bx_ax);
  py = L1StartPoint.Y + r * (by_ay);

  IntersectPoint.X = px;  //return the intersection point
  IntersectPoint.Y = py; //return the intersection position
  L1IntersectPos = r;   L2IntersectPos = s;

  return true; //indicate there is intersection
 }

To check whether the intersection point is not beyond the original length of the line, just make sure that 0<r<1 and 0<s<1

Ngu Soon Hui
+2  A: 

A simple optimization that may save you alot of time is to check the axis-aligned bounding boxes of the lines prior to getting into the intersection calculation.
If the bounding boxes are completely disjoint then you can be certain there is no intersection.
Of course this is dependent of the data you have. if an intersection is very likely in every check you make then you might find yourself wasting time on a check that is always true.

shoosh
No, intersections are not common. This is as very good idea, thankyou.
Ah, bounding boxes. Whenever I hear those words I have an image of computerised boxes jumping around in a field, happy as lambs in spring. Thanks :-)
endian
+4  A: 
float x12 = x1 - x2;
float x34 = x3 - x4;
float y12 = y1 - y2;
float y34 = y3 - y4;

float c = x12 * y34 - y12 * x34;

if (fabs(c) < 0.01)
{
  // No intersection
  return false;
}
else
{
  // Intersection
  float a = x1 * y2 - y1 * x2;
  float b = x3 * y4 - y3 * x4;

  float x = (a * x34 - b * x12) / c;
  float y = (a * y34 - b * y12) / c;

  return true;
}

Formulas taken from:
Line-line intersection - Wikipedia

TomWij
From the article, "can produce an intersection point beyond the lengths of the line segments." This has been my issue. A solution could be to then check if the point of intersection is within the bounding box of the lines.
At the place where "return true;" is you can do a check if x is between x1 and x2, x3 and x4, and y is between y1 and y2, y3 and y4.
TomWij
This has numerical problems if (x2-x1) is much smaller in magnitude than (x2+x1), or similar issues with x3,x4 and y1,y2 and y3,y4 (the math you're doing in the else clause).
Jason S
+3  A: 
Simucal
A: 

Well, mathematics and scalar products can help there.
- Say you want to intersect segments [AB] and [CD]
- Suppose the line intersection of the lines is M

M is inside segment [ÅB] if and only if
Vector(AB) . Vector(AM) >= 0
and
Vector(AB) . Vector(MB) >= 0

Where the dot "." denotes a scalar product : u . v = ux * vx + uy * vy

So, your algo is :
- find M
- M is inside both segment if and only if the 4 eq below are true
Vector(AB) . Vector(AM) >= 0
Vector(AB) . Vector(MB) >= 0
Vector(CD) . Vector(CM) >= 0
Vector(CD) . Vector(MD) >= 0

Hope this helps

Pascal T.
A: 

Below is my line-line intersection as described in MathWorld. For general collision detection/intersection you may want to look at the Separating Axis Theorem. I found this tutorial on SAT very informative.

    /// <summary>
    /// Returns the intersection point of the given lines. 
    /// Returns Empty if the lines do not intersect.
    /// Source: http://mathworld.wolfram.com/Line-LineIntersection.html
    /// </summary>
    public static PointF LineIntersection(PointF v1, PointF v2, PointF v3, PointF v4)
    {
        float tolerance = 0.000001f;

        float a = Det2(v1.X - v2.X, v1.Y - v2.Y, v3.X - v4.X, v3.Y - v4.Y);
        if (Math.Abs(a) < float.Epsilon) return PointF.Empty; // Lines are parallel

        float d1 = Det2(v1.X, v1.Y, v2.X, v2.Y);
        float d2 = Det2(v3.X, v3.Y, v4.X, v4.Y);
        float x = Det2(d1, v1.X - v2.X, d2, v3.X - v4.X) / a;
        float y = Det2(d1, v1.Y - v2.Y, d2, v3.Y - v4.Y) / a;

        if (x < Math.Min(v1.X, v2.X) - tolerance || x > Math.Max(v1.X, v2.X) + tolerance) return PointF.Empty;
        if (y < Math.Min(v1.Y, v2.Y) - tolerance || y > Math.Max(v1.Y, v2.Y) + tolerance) return PointF.Empty;
        if (x < Math.Min(v3.X, v4.X) - tolerance || x > Math.Max(v3.X, v4.X) + tolerance) return PointF.Empty;
        if (y < Math.Min(v3.Y, v4.Y) - tolerance || y > Math.Max(v3.Y, v4.Y) + tolerance) return PointF.Empty;

        return new PointF(x, y);
    }

    /// <summary>
    /// Returns the determinant of the 2x2 matrix defined as
    /// <list>
    /// <item>| x1 x2 |</item>
    /// <item>| y1 y2 |</item>
    /// </list>
    /// </summary>
    public static float Det2(float x1, float x2, float y1, float y2)
    {
        return (x1 * y2 - y1 * x2);
    }
Ozgur Ozcitak
+7  A: 

Most of the answers already here seem to follow the general idea that:

  1. find the intersection of two straight lines passing the given points.
  2. determine if the intersection belong to both line segments.

But when intersection does not occur often, a better way probably is to reverse these steps:

  1. express the straight lines in the form of y = ax + b (line passing A,B) and y = cx + d (line passing C,D)
  2. see if C and D are on the same side of y = ax+b
  3. see if A and B are on the same side of y = cx+d
  4. if the answer to the above are both no, then there is an intersection. otherwise there is no intersection.
  5. find the intersection if there is one.

Note: to do step 2, just check if (C.y - a(C.x) - b) and (D.y - a(D.x) - b) have the same sign. Step 3 is similar. Step 5 is just standard math from the two equations.

Furthermore, if you need to compare each line segment with (n-1) other line segments, precomputing step 1 for all lines saves you time.

PolyThinker
Jason S
One comment: don't use the y=mx+b form. It fails for vertical lines, also there are numerical stability issues. Instead use a(x-xm)+b(y-ym)=c. (cont'd)
Jason S
For lines through points (x0,y0) and (x1,y1), let xm = (x0+x1)/2, ym = (y0+y1)/2 (median of line segment). Then a = (y1-y0) and b = (x0-x1). If you evaluate c = a(x-xm)+b(y-ym), c=0 for (x,y) on the line, and the sign(c) tells you which side a point is on.
Jason S
(you can also replace xm,ym with x0,y0 or x1,y1)
Jason S
Thanks for your suggestions. I didn't consider the special cases. Your method is better :)
PolyThinker