8

I can detect the intersection point of two lines, but if my line don't has the length of my screen, it detects the point, where it shouldn't be.

Here a preview: Intersection So, it shouldn't detect this intersection because the horizontal line isn't that long.

Code:

- (NSMutableArray *) intersectWithLines:(CGPoint)startPoint andEnd:(CGPoint)endPoint {
    NSMutableArray *intersects = [[NSMutableArray alloc] init];

    for(GameLine *line in [_lineBackground getLines]) {

        double lineStartX = line.startPos.x;
        double lineStartY = line.startPos.y;
        double tempEndX = line.endPos.x;
        double tempEndY = line.endPos.y;

        double d = ((startPoint.x - endPoint.x)*(lineStartY - tempEndY)) - ((startPoint.y - endPoint.y) * (lineStartX - tempEndX));

        if(d != 0) {            
            double sX = ((lineStartX - tempEndX) * (startPoint.x * endPoint.y - startPoint.y * endPoint.x) - (startPoint.x - endPoint.x) * (lineStartX * tempEndY - lineStartY * tempEndX)) / d;
            double sY = ((lineStartY - tempEndY) * (startPoint.x * endPoint.y - startPoint.y * endPoint.x) - (startPoint.y - endPoint.y) * (lineStartX * tempEndY - lineStartY * tempEndX)) / d;


            if([self isValidCGPoint:CGPointMake(sX, sY)]) {
                [intersects addObject:[NSValue valueWithCGPoint:CGPointMake(sX, sY)]];
            }            
        }
    }

    return intersects;
}
Christian 'fuzi' Orgler
  • 1,682
  • 8
  • 27
  • 51
  • I don't know what you mean, that horizontal line clearly intersects one of the vertical ones. – Tony Mar 28 '13 at 19:41
  • yes of course, but not graphically - I think I have to determine if the intersection point is on one of these lines, if not - i can draw it to the top. u know what I mean? – Christian 'fuzi' Orgler Mar 28 '13 at 19:43

7 Answers7

32

If I understand your question correctly, you need to determine the intersection point of two line segments. This should work with the following method:

- (NSValue *)intersectionOfLineFrom:(CGPoint)p1 to:(CGPoint)p2 withLineFrom:(CGPoint)p3 to:(CGPoint)p4
{
    CGFloat d = (p2.x - p1.x)*(p4.y - p3.y) - (p2.y - p1.y)*(p4.x - p3.x);
    if (d == 0)
        return nil; // parallel lines
    CGFloat u = ((p3.x - p1.x)*(p4.y - p3.y) - (p3.y - p1.y)*(p4.x - p3.x))/d;
    CGFloat v = ((p3.x - p1.x)*(p2.y - p1.y) - (p3.y - p1.y)*(p2.x - p1.x))/d;
    if (u < 0.0 || u > 1.0)
        return nil; // intersection point not between p1 and p2
    if (v < 0.0 || v > 1.0)
        return nil; // intersection point not between p3 and p4
    CGPoint intersection;
    intersection.x = p1.x + u * (p2.x - p1.x);
    intersection.y = p1.y + u * (p2.y - p1.y);

    return [NSValue valueWithCGPoint:intersection];
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • NOTE: Attempting to use this, I found that `intersection.y = p1.y + v * (p2.y - p1.y);` needs to be `intersection.y = p1.y + u * (p2.y - p1.y);` as it originally was. I don't know who edited the `u` to be a `v`, but it needs to be edited back -- `u` and `v` are interpolation values along each line segment -- using two different interpolation values for each axis of the same interpolation is absurd. – Georges Oates Larsen May 23 '15 at 23:44
  • 1
    @GeorgesOatesLarsen: You are right of course. It is my fault that I did not check the "suggested edit" carefully, I have reverted the answer to the previous version. Thanks! – Martin R May 24 '15 at 07:10
  • @MartinR No worries! Thank you for reverting it! +1 – Georges Oates Larsen May 25 '15 at 09:35
  • 1
    can you define the variable name properly to understand code better, like what d defines for, what u defines for etc ... – Bhargav Kukadiya Jun 02 '16 at 12:39
  • @MartinR. I want to check point on CGContextStrokePath to delete that line any help on this? – Avijit Nagare Jan 11 '17 at 13:26
  • If anybody wants to know where this solution comes from, here is a derivation http://www.cs.swan.ac.uk/~cssimon/line_intersection.html – JeremyP Mar 23 '19 at 12:56
8

This is a slightly modified version of Hayden Holligan's answer to work with Swift 3:

func getIntersectionOfLines(line1: (a: CGPoint, b: CGPoint), line2: (a: CGPoint, b: CGPoint)) -> CGPoint {

    let distance = (line1.b.x - line1.a.x) * (line2.b.y - line2.a.y) - (line1.b.y - line1.a.y) * (line2.b.x - line2.a.x)
    if distance == 0 {
        print("error, parallel lines")
        return CGPoint.zero
    }

    let u = ((line2.a.x - line1.a.x) * (line2.b.y - line2.a.y) - (line2.a.y - line1.a.y) * (line2.b.x - line2.a.x)) / distance
    let v = ((line2.a.x - line1.a.x) * (line1.b.y - line1.a.y) - (line2.a.y - line1.a.y) * (line1.b.x - line1.a.x)) / distance

    if (u < 0.0 || u > 1.0) {
        print("error, intersection not inside line1")
        return CGPoint.zero
    }
    if (v < 0.0 || v > 1.0) {
        print("error, intersection not inside line2")
        return CGPoint.zero
    }

    return CGPoint(x: line1.a.x + u * (line1.b.x - line1.a.x), y: line1.a.y + u * (line1.b.y - line1.a.y))
}
sutherlandahoy
  • 1,395
  • 1
  • 12
  • 24
5

Swift version

func getIntersectionOfLines(line1: (a: CGPoint, b: CGPoint), line2: (a: CGPoint, b: CGPoint)) -> CGPoint {
        let distance = (line1.b.x - line1.a.x) * (line2.b.y - line2.a.y) - (line1.b.y - line1.a.y) * (line2.b.x - line2.a.x)
        if distance == 0 {
            print("error, parallel lines")
            return CGPointZero
        }

        let u = ((line2.a.x - line1.a.x) * (line2.b.y - line2.a.y) - (line2.a.y - line1.a.y) * (line2.b.x - line2.a.x)) / distance
        let v = ((line2.a.x - line1.a.x) * (line1.b.y - line1.a.y) - (line2.a.y - line1.a.y) * (line1.b.x - line1.a.x)) / distance

        if (u < 0.0 || u > 1.0) {
            print("error, intersection not inside line1")
            return CGPointZero
        }
        if (v < 0.0 || v > 1.0) {
            print("error, intersection not inside line2")
            return CGPointZero
        }

        return CGPointMake(line1.a.x + u * (line1.b.x - line1.a.x), line1.a.y + u * (line1.b.y - line1.a.y))
    }
Hayden Holligan
  • 1,822
  • 2
  • 19
  • 26
2

That's the correct equation:

+(CGPoint) intersection2:(CGPoint)u1 u2:(CGPoint)u2 v1:(CGPoint)v1 v2:(CGPoint)v2 {  
    CGPoint ret=u1;  
    double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))  
    /((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));  
    ret.x+=(u2.x-u1.x)*t;  
    ret.y+=(u2.y-u1.y)*t;  
    return ret;  
}  
TomH
  • 2,950
  • 2
  • 26
  • 49
  • This solution is not working. Just tried with this values: `Line A: (0,0) and (0, 20)` `Line B: (3, 2) and (-5, 2) ` The given result is 0, 320 that is clearly wrong. – MatterGoal Feb 09 '17 at 17:52
2

Here is another solution in Swift 4.2. This is functionally identical to MartinR's solution but uses simd vectors and matrices to clean it up.

/// Protocol adoped by any type that models a line segment.
protocol LineSegment
{
    /// Point defining an end of a line segment.
    var p1: simd_double2 { get }
    /// Point defining an end of a line segment.
    var p2: simd_double2 { get }
}

extension LineSegment
{
    /// Calcualte the intersection between this line segment and another line
    /// segment.
    ///
    /// Algorithm from here:
    /// http://www.cs.swan.ac.uk/~cssimon/line_intersection.html
    ///
    /// - Parameter other: The other line segment.
    /// - Returns: The intersection point, or `nil` if the two line segments are
    ///            parallel or the intersection point would be off the end of
    ///            one of the line segments.
    func intersection(lineSegment other: LineSegment) -> simd_double2?
    {
        let p3 = other.p1 // Name the points so they are consistent with the explanation below
        let p4 = other.p2
        let matrix = simd_double2x2(p4 - p3, p1 - p2)
        guard matrix.determinant != 0 else { return nil } // Determinent == 0 => parallel lines
        let multipliers = matrix.inverse * (p1 - p3)
        // If either of the multipliers is outside the range 0 ... 1, then the
        // intersection would be off the end of one of the line segments.
        guard (0.0 ... 1.0).contains(multipliers.x) && (0.0 ... 1.0).contains(multipliers.y)
            else { return nil }
        return p1 + multipliers.y * (p2 - p1)
    }
}

The algorithm works because, if you have line segment a defined by two points p1 and p2 and line segment b defined by p3 and p4 the points on a and b are respectively defined by

  • p1 + ta(p2 - p1)
  • p3 + tb(p4 - p3)

so the point of intersection would be where

p1 + ta(p2 - p1) = p3 + tb(p4 - p3)

This can be rearranged as

p1 - p3 = tb(p4 - p3) + ta(p1 - p2)

and with a bit of jiggery pokery you can get to the following equivalent

p1 - p3 = A.t

where t is the vector (tb, ta) and A is the matrix whose columns are p4 - p3 and p1 - p2

The equation can be rearranged as

A-1(p1 - p3) = t

Everything on the left hand side is already known or can be calculated to get us t. Either of the components of t can be plugged into the respective original equation to get the intersection point (NB floating point rounding errors will mean that the two answers probably aren't exactly the same but are very close).

Note that, if the lines are parallel, the determinant of A will be zero. Also, if either component is outside the range 0 ... 1, then one or both line segments needs to be extended to get to the point of intersection.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • The documentation of `.inverse` says *“Inverse of the matrix if it exists, otherwise the contents of the resulting matrix are undefined.”* As I understand it, that does not necessarily imply that the result components are `NaN`. – Martin R Mar 23 '19 at 16:30
  • @MartinR Where did you find that? I looked all over for any docs as to what happens, couldn't find any and arrived at my answer by trial and error. – JeremyP Mar 23 '19 at 16:34
  • From the “quick help inspector” or “Jump to Definition” in Xcode. – Martin R Mar 23 '19 at 17:24
  • @MartinR Interesting, it doesn't show up in quick help for me, but it is in the interface doc. – JeremyP Mar 23 '19 at 17:45
2

This answer is available in several programming languages

https://rosettacode.org/wiki/Find_the_intersection_of_two_lines

struct Point {
  var x: Double
  var y: Double
}
 
struct Line {
  var p1: Point
  var p2: Point
 
  var slope: Double {
    guard p1.x - p2.x != 0.0 else { return .nan }
 
    return (p1.y-p2.y) / (p1.x-p2.x)
  }
 
  func intersection(of other: Line) -> Point? {
    let ourSlope = slope
    let theirSlope = other.slope
 
    guard ourSlope != theirSlope else { return nil }
 
    if ourSlope.isNaN && !theirSlope.isNaN {
      return Point(x: p1.x, y: (p1.x - other.p1.x) * theirSlope + other.p1.y)
    } else if theirSlope.isNaN && !ourSlope.isNaN {
      return Point(x: other.p1.x, y: (other.p1.x - p1.x) * ourSlope + p1.y)
    } else {
      let x = (ourSlope*p1.x - theirSlope*other.p1.x + other.p1.y - p1.y) / (ourSlope - theirSlope)
      return Point(x: x, y: theirSlope*(x - other.p1.x) + other.p1.y)
    }
  }
}
 
let l1 = Line(p1: Point(x: 4.0, y: 0.0), p2: Point(x: 6.0, y: 10.0))
let l2 = Line(p1: Point(x: 0.0, y: 3.0), p2: Point(x: 10.0, y: 7.0))
 
print("Intersection at : \(l1.intersection(of: l2)!)")
Carlos Chaguendo
  • 2,895
  • 19
  • 24
0

I know the answer is given and all of them are correct one still, I feel to give my answer to this question. So here it is.

func linesCross(start1: CGPoint, end1: CGPoint, start2: CGPoint, end2: CGPoint) -> (x: CGFloat, y: CGFloat)? {
// calculate the differences between the start and end X/Y positions for each of our points
let delta1x = end1.x - start1.x
let delta1y = end1.y - start1.y
let delta2x = end2.x - start2.x
let delta2y = end2.y - start2.y

// create a 2D matrix from our vectors and calculate the determinant
let determinant = delta1x * delta2y - delta2x * delta1y

if abs(determinant) < 0.0001 {
    // if the determinant is effectively zero then the lines are parallel/colinear
    return nil
}

// if the coefficients both lie between 0 and 1 then we have an intersection
let ab = ((start1.y - start2.y) * delta2x - (start1.x - start2.x) * delta2y) / determinant

if ab > 0 && ab < 1 {
    let cd = ((start1.y - start2.y) * delta1x - (start1.x - start2.x) * delta1y) / determinant

    if cd > 0 && cd < 1 {
        // lines cross – figure out exactly where and return it
        let intersectX = start1.x + ab * delta1x
        let intersectY = start1.y + ab * delta1y
        return (intersectX, intersectY)
    }
}

// lines don't cross
return nil
}

I get this from this site.

This one is very simple and easy, too.

Happy Coding :)

The iOSDev
  • 5,237
  • 7
  • 41
  • 78