40

I have 2 lines. Both lines containing their 2 points of X and Y. This means they both have length.

I see 2 formulas, one using determinants and one using normal algebra. Which would be the most efficient to calculate and what does the formula looks like?

I'm having a hard time using matrices in code.

This is what I have so far, can it be more efficient?

public static Vector3 Intersect(Vector3 line1V1, Vector3 line1V2, Vector3 line2V1, Vector3 line2V2)
{
    //Line1
    float A1 = line1V2.Y - line1V1.Y;
    float B1 = line1V1.X - line1V2.X;
    float C1 = A1*line1V1.X + B1*line1V1.Y;

    //Line2
    float A2 = line2V2.Y - line2V1.Y;
    float B2 = line2V1.X - line2V2.X;
    float C2 = A2 * line2V1.X + B2 * line2V1.Y;

    float det = A1*B2 - A2*B1;
    if (det == 0)
    {
        return null;//parallel lines
    }
    else
    {
        float x = (B2*C1 - B1*C2)/det;
        float y = (A1 * C2 - A2 * C1) / det;
        return new Vector3(x,y,0);
    }
}
andrewchan2022
  • 4,953
  • 45
  • 48
Shawn Mclean
  • 56,733
  • 95
  • 279
  • 406
  • 8
    How's about you write the formulas, just math, no code, and then you show us the code you have, and then you tell us where you're having trouble? – atk Dec 28 '10 at 03:44
  • 1
    You hage an O(1) algorithm, so I'm not sure you're really looking for efficiency. If you really are, have you profiled your code to figure out what bits are less efficient than others? have you checked against other parts of your program to see what's inefficient and how do you define efficiency, he (size in memory, speed, etc)? Or, since you talk about matricies, are you really asking for a generic solution, with a line in arbitrary number of dimensions? – atk Dec 28 '10 at 13:21
  • You say "lines" but you say they have length. Do you mean lines or line segments? The line case is a lot easier because any two non-parallel lines in an x,y plane will intersect _somewhere_, not so with segments – user316117 Dec 28 '15 at 18:31
  • 4
    You have an error in that code. Calculations of B1 and B2 should be as follows: `float B1 = line1V1.X - line1V2.X` and `float B2 = line2V1.X - line2V2.X` – mcs_dodo Feb 01 '17 at 15:55

3 Answers3

63

Assuming you have two lines of the form Ax + By = C, you can find it pretty easily:

float delta = A1 * B2 - A2 * B1;

if (delta == 0) 
    throw new ArgumentException("Lines are parallel");

float x = (B2 * C1 - B1 * C2) / delta;
float y = (A1 * C2 - A2 * C1) / delta;

Pulled from here

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Brian Genisio
  • 47,787
  • 16
  • 124
  • 167
8

I recently went back on paper to find a solution to this problem using basic algebra. We just need to solve the equations formed by the two lines and if a valid solution exist then there is an intersection.

You can check my Github repository for extended implementation handling potential precision issue with double and tests.

public struct Line
{
    public double x1 { get; set; }
    public double y1 { get; set; }

    public double x2 { get; set; }
    public double y2 { get; set; }
}

public struct Point
{
    public double x { get; set; }
    public double y { get; set; }
}

public class LineIntersection
{
    //  Returns Point of intersection if do intersect otherwise default Point (null)
    public static Point FindIntersection(Line lineA, Line lineB, double tolerance = 0.001)
    {
        double x1 = lineA.x1, y1 = lineA.y1;
        double x2 = lineA.x2, y2 = lineA.y2;

        double x3 = lineB.x1, y3 = lineB.y1;
        double x4 = lineB.x2, y4 = lineB.y2;

        // equations of the form x=c (two vertical lines) with overlapping
        if (Math.Abs(x1 - x2) < tolerance && Math.Abs(x3 - x4) < tolerance && Math.Abs(x1 - x3) < tolerance)
        {
            throw new Exception("Both lines overlap vertically, ambiguous intersection points.");
        }

        //equations of the form y=c (two horizontal lines) with overlapping
        if (Math.Abs(y1 - y2) < tolerance && Math.Abs(y3 - y4) < tolerance && Math.Abs(y1 - y3) < tolerance)
        {
            throw new Exception("Both lines overlap horizontally, ambiguous intersection points.");
        }

        //equations of the form x=c (two vertical parallel lines)
        if (Math.Abs(x1 - x2) < tolerance && Math.Abs(x3 - x4) < tolerance)
        {   
            //return default (no intersection)
            return default(Point);
        }

        //equations of the form y=c (two horizontal parallel lines)
        if (Math.Abs(y1 - y2) < tolerance && Math.Abs(y3 - y4) < tolerance)
        {
            //return default (no intersection)
            return default(Point);
        }

        //general equation of line is y = mx + c where m is the slope
        //assume equation of line 1 as y1 = m1x1 + c1 
        //=> -m1x1 + y1 = c1 ----(1)
        //assume equation of line 2 as y2 = m2x2 + c2
        //=> -m2x2 + y2 = c2 -----(2)
        //if line 1 and 2 intersect then x1=x2=x & y1=y2=y where (x,y) is the intersection point
        //so we will get below two equations 
        //-m1x + y = c1 --------(3)
        //-m2x + y = c2 --------(4)

        double x, y;

        //lineA is vertical x1 = x2
        //slope will be infinity
        //so lets derive another solution
        if (Math.Abs(x1 - x2) < tolerance)
        {
            //compute slope of line 2 (m2) and c2
            double m2 = (y4 - y3) / (x4 - x3);
            double c2 = -m2 * x3 + y3;

            //equation of vertical line is x = c
            //if line 1 and 2 intersect then x1=c1=x
            //subsitute x=x1 in (4) => -m2x1 + y = c2
            // => y = c2 + m2x1 
            x = x1;
            y = c2 + m2 * x1;
        }
        //lineB is vertical x3 = x4
        //slope will be infinity
        //so lets derive another solution
        else if (Math.Abs(x3 - x4) < tolerance)
        {
            //compute slope of line 1 (m1) and c2
            double m1 = (y2 - y1) / (x2 - x1);
            double c1 = -m1 * x1 + y1;

            //equation of vertical line is x = c
            //if line 1 and 2 intersect then x3=c3=x
            //subsitute x=x3 in (3) => -m1x3 + y = c1
            // => y = c1 + m1x3 
            x = x3;
            y = c1 + m1 * x3;
        }
        //lineA & lineB are not vertical 
        //(could be horizontal we can handle it with slope = 0)
        else
        {
            //compute slope of line 1 (m1) and c2
            double m1 = (y2 - y1) / (x2 - x1);
            double c1 = -m1 * x1 + y1;

            //compute slope of line 2 (m2) and c2
            double m2 = (y4 - y3) / (x4 - x3);
            double c2 = -m2 * x3 + y3;

            //solving equations (3) & (4) => x = (c1-c2)/(m2-m1)
            //plugging x value in equation (4) => y = c2 + m2 * x
            x = (c1 - c2) / (m2 - m1);
            y = c2 + m2 * x;

            //verify by plugging intersection point (x, y)
            //in orginal equations (1) & (2) to see if they intersect
            //otherwise x,y values will not be finite and will fail this check
            if (!(Math.Abs(-m1 * x + y - c1) < tolerance
                && Math.Abs(-m2 * x + y - c2) < tolerance))
            {
                //return default (no intersection)
                return default(Point);
            }
        }

        //x,y can intersect outside the line segment since line is infinitely long
        //so finally check if x, y is within both the line segments
        if (IsInsideLine(lineA, x, y) &&
            IsInsideLine(lineB, x, y))
        {
            return new Point { x = x, y = y };
        }

        //return default (no intersection)
        return default(Point);

    }

    // Returns true if given point(x,y) is inside the given line segment
    private static bool IsInsideLine(Line line, double x, double y)
    {
        return (x >= line.x1 && x <= line.x2
                    || x >= line.x2 && x <= line.x1)
               && (y >= line.y1 && y <= line.y2
                    || y >= line.y2 && y <= line.y1);
    }
}
Jehonathan Thomas
  • 2,110
  • 1
  • 28
  • 34
2

How to find intersection of two lines/segments/ray with rectangle

public class LineEquation{
    public LineEquation(Point start, Point end){
        Start = start;
        End = end;

        IsVertical = Math.Abs(End.X - start.X) < 0.00001f;
        M = (End.Y - Start.Y)/(End.X - Start.X);
        A = -M;
        B = 1;
        C = Start.Y - M*Start.X;
    }

    public bool IsVertical { get; private set; }

    public double M { get; private set; }

    public Point Start { get; private set; }
    public Point End { get; private set; }

    public double A { get; private set; }
    public double B { get; private set; }
    public double C { get; private set; }

    public bool IntersectsWithLine(LineEquation otherLine, out Point intersectionPoint){
        intersectionPoint = new Point(0, 0);
        if (IsVertical && otherLine.IsVertical)
            return false;
        if (IsVertical || otherLine.IsVertical){
            intersectionPoint = GetIntersectionPointIfOneIsVertical(otherLine, this);
            return true;
        }
        double delta = A*otherLine.B - otherLine.A*B;
        bool hasIntersection = Math.Abs(delta - 0) > 0.0001f;
        if (hasIntersection){
            double x = (otherLine.B*C - B*otherLine.C)/delta;
            double y = (A*otherLine.C - otherLine.A*C)/delta;
            intersectionPoint = new Point(x, y);
        }
        return hasIntersection;
    }

    private static Point GetIntersectionPointIfOneIsVertical(LineEquation line1, LineEquation line2){
        LineEquation verticalLine = line2.IsVertical ? line2 : line1;
        LineEquation nonVerticalLine = line2.IsVertical ? line1 : line2;

        double y = (verticalLine.Start.X - nonVerticalLine.Start.X)*
                   (nonVerticalLine.End.Y - nonVerticalLine.Start.Y)/
                   ((nonVerticalLine.End.X - nonVerticalLine.Start.X)) +
                   nonVerticalLine.Start.Y;
        double x = line1.IsVertical ? line1.Start.X : line2.Start.X;
        return new Point(x, y);
    }

    public bool IntersectWithSegementOfLine(LineEquation otherLine, out Point intersectionPoint){
        bool hasIntersection = IntersectsWithLine(otherLine, out intersectionPoint);
        if (hasIntersection)
            return intersectionPoint.IsBetweenTwoPoints(otherLine.Start, otherLine.End);
        return false;
    }

    public bool GetIntersectionLineForRay(Rect rectangle, out LineEquation intersectionLine){
        if (Start == End){
            intersectionLine = null;
            return false;
        }
        IEnumerable<LineEquation> lines = rectangle.GetLinesForRectangle();
        intersectionLine = new LineEquation(new Point(0, 0), new Point(0, 0));
        var intersections = new Dictionary<LineEquation, Point>();
        foreach (LineEquation equation in lines){
            Point point;
            if (IntersectWithSegementOfLine(equation, out point))
                intersections[equation] = point;
        }
        if (!intersections.Any())
            return false;

        var intersectionPoints = new SortedDictionary<double, Point>();
        foreach (var intersection in intersections){
            if (End.IsBetweenTwoPoints(Start, intersection.Value) ||
                intersection.Value.IsBetweenTwoPoints(Start, End)){
                double distanceToPoint = Start.DistanceToPoint(intersection.Value);
                intersectionPoints[distanceToPoint] = intersection.Value;
            }
        }
        if (intersectionPoints.Count == 1){
            Point endPoint = intersectionPoints.First().Value;
            intersectionLine = new LineEquation(Start, endPoint);
            return true;
        }

        if (intersectionPoints.Count == 2){
            Point start = intersectionPoints.First().Value;
            Point end = intersectionPoints.Last().Value;
            intersectionLine = new LineEquation(start, end);
            return true;
        }

        return false;
    }

    public override string ToString(){
        return "[" + Start + "], [" + End + "]";
    }
}

full sample is described [here][1]

Artiom
  • 7,694
  • 3
  • 38
  • 45
  • 1
    Your link was helpful, and the code does work, but i'm not sure why they didn't do the intersection code more simply like this: http://pastebin.com/iQDhQTFN – FocusedWolf Jun 24 '15 at 22:03