Search code examples
graphicscore-graphics

Determine if line intersects CGRect


What is the most efficient way to determine if a line intersects a rectangle?

I'm looking for something similar to this:

CGPoint startLine = CGPointMake(5.0f,5.0f);
CGPoint endLine = CGPointMake(25.0f,25.0f);

CGRect intersectingRect = CGRectMake(10.0f,10.0f,50.0f,50.0f);

if (CGRectContainsLine(intersectingRect,startLine,endLine)) { //true
    //line intersects rectangle
}

Solution

  • There's nothing built in, but this ought to do it:

    BOOL RectContainsLine(CGRect r, CGPoint lineStart, CGPoint lineEnd)
    {
        BOOL (^LineIntersectsLine)(CGPoint, CGPoint, CGPoint, CGPoint) = ^BOOL(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End)
        {
            CGFloat q =
                //Distance between the lines' starting rows times line2's horizontal length
                (line1Start.y - line2Start.y) * (line2End.x - line2Start.x)
                //Distance between the lines' starting columns times line2's vertical length
                - (line1Start.x - line2Start.x) * (line2End.y - line2Start.y);
            CGFloat d =
                //Line 1's horizontal length times line 2's vertical length
                (line1End.x - line1Start.x) * (line2End.y - line2Start.y)
                //Line 1's vertical length times line 2's horizontal length
                - (line1End.y - line1Start.y) * (line2End.x - line2Start.x);
    
            if( d == 0 )
                return NO;
    
            CGFloat r = q / d;
    
            q =
                //Distance between the lines' starting rows times line 1's horizontal length
                (line1Start.y - line2Start.y) * (line1End.x - line1Start.x)
                //Distance between the lines' starting columns times line 1's vertical length
                - (line1Start.x - line2Start.x) * (line1End.y - line1Start.y);
    
            CGFloat s = q / d;
            if( r < 0 || r > 1 || s < 0 || s > 1 )
                return NO;
    
            return YES;
        };
    
        /*Test whether the line intersects any of:
         *- the bottom edge of the rectangle
         *- the right edge of the rectangle
         *- the top edge of the rectangle
         *- the left edge of the rectangle
         *- the interior of the rectangle (both points inside)
         */
    
        return (LineIntersectsLine(lineStart, lineEnd, CGPointMake(r.origin.x, r.origin.y), CGPointMake(r.origin.x + r.size.width, r.origin.y)) ||
                LineIntersectsLine(lineStart, lineEnd, CGPointMake(r.origin.x + r.size.width, r.origin.y), CGPointMake(r.origin.x + r.size.width, r.origin.y + r.size.height)) ||
                LineIntersectsLine(lineStart, lineEnd, CGPointMake(r.origin.x + r.size.width, r.origin.y + r.size.height), CGPointMake(r.origin.x, r.origin.y + r.size.height)) ||
                LineIntersectsLine(lineStart, lineEnd, CGPointMake(r.origin.x, r.origin.y + r.size.height), CGPointMake(r.origin.x, r.origin.y)) ||
                (CGRectContainsPoint(r, lineStart) && CGRectContainsPoint(r, lineEnd)));
    }
    

    Trivially ported from this question: How to know if a line intersects a rectangle