intersection of ray starting inside square with that square

1.4k Views Asked by At

I have ray's source coordinates (point D) and angle of that ray in radians. How can I find coordinates of intersection of that ray with square perimeter (point F) for arbitrary source and angle?

I've tried to derive equation of line and from angle determine which of two coordinates equal to $0$ or $5$. Another try was to find coordinates on square with inscribed unit circle centered at source of ray and from that coordinates return to source square, but no success.

picture

1

There are 1 best solutions below

2
On BEST ANSWER

Lets label the edges top ($AB$), bottom ($OC$), left ($OA$), and right ($BC$), and the angle of the ray (counterclockwise from the $x$ axis) $\varphi$.

The simple solution is to use the ray angle $\varphi$ (counterclockwise from positive $x$ axis; say, $-180° \le \varphi \le +180°$), and check each possible edge. The ray may hit two edges at most, so we simply calculate the distance from $D$ to the intersection. The correct edge and location corresponds to the smaller (nonnegative) distance.

(You can work this out for all axis-aligned rectangles, by using coordinates $y_{max}$, $y_{min}$, $x_{min}$, and $x_{max}$, so that $O = ( x_{min} , y_{min} )$, $A = ( x_{min} , y_{max} )$, $B = ( x_{max} , y_{max} )$, and $C = ( x_{max} , y_{min} )$. See the example pseudocode at the bottom.)

For the ray, let's use $D = ( x_0, y_0 )$, so that the coordinates for point $t$ on the ray (at distance $t$ from $D$), $0 \le t \in \mathbb{R}$, are $$\begin{cases} x(t) = x_0 + t \cos \varphi \\ y(t) = y_0 + t \sin \varphi \end{cases}$$

However, we can generalize this for any ray, using $$\begin{cases} x(t) = x_0 + t x_\Delta \\ y(t) = y_0 + t y_\Delta \end{cases} \tag{1}\label{NA1}$$ simply by treating OP's particular problem with $$\begin{aligned} x_\Delta &= \cos\varphi \\ y_\Delta &= \sin\varphi \end{aligned}$$

Note that whenever I use the term distance below, I mean Euclidean distance in units of $\sqrt{x_\Delta^2 + y_\Delta^2}$.


The distance $t_x$ from $( x_0 , y_0 )$ to a vertical line at $x = X$ is $$t_x = \frac{X - x_0}{x_\Delta} \tag{2a}\label{NA2a}$$ The distance $t_y$ from $( x_0 , y_0 )$ to a vertical line at $y = Y$ is $$t_y = \frac{Y - y_0}{y_\Delta} \tag{2b}\label{NA2b}$$

If $x_\Delta = 0$, the ray is vertical, and can only hit the top or bottom edge. If $x_\Delta \gt 0$, the ray can hit the right edge. If $x_\Delta \lt 0$, the ray can hit the left edge. Similarly for $y_\Delta$.

If $t_x$ and $t_y$ both exist, then the ray hits the one with the smaller positive value $t$, and the intersection occurs at $( x(t), y(t) )$.


In pseudocode, one might implement this as a function thus:

Function ray_rectangle_intersection(x_0, y_0, x_d, y_d, x_min, y_min, x_max, y_max):

    # Verify rectangle is sane.
    If (x_min >= x_max) Or (y_min >= y_max):
        Return BAD_RECTANGLE
    End If

    # Verify x_0, y_0 is inside the rectangle.
    If (x_0 <= x_min) Or (x_0 >= x_max) Or (y_0 <= y_min) Or (y_0 >= y_max):
        Return OUTSIDE
    End If

    # Verify the direction vector is nonzero.
    If (x_d*x_d + y_d*y_d == 0):
        Return NO_DIRECTION
    End If

    If (x_d > 0):
        x_intersect = RIGHT
        t_x = (x_max - x_0) / x_d
    Else If (x_d < 0):
        x_intersect = LEFT
        t_x = (x_min - x_0) / x_d
    Else:
        x_intersect = NONE
    End If

    If (y_d > 0):
        y_intersect = TOP
        t_y = (y_max - y_0) / y_d
    Else If (y_d < 0):
        y_intersect = BOTTOM
        t_y = (y_min - y_0) / y_d
    Else:
        y_intersect = NONE
    End If

    If (x_intersect == NONE) and (y_intersect == NONE):
        Return NO_DIRECTION
    Else If (x_intersect == NONE):
        Return edge = y_intersect,     # TOP or BOTTOM 
                  x = x_0 + t_y * x_d,
                  y = y_0 + t_y * y_d
    Else If (y_intersect == NONE):
        Return edge = x_intersect,     # LEFT or RIGHT
                  x = x_0 + t_x * x_d,
                  y = y_0 + t_y * y_d
    Else:
         If (t_x < t_y):
             Return edge = x_intersect,  # LEFT or RIGHT
                       x = x_0 + t_x * x_d,
                       y = y_0 + t_x * y_d
         Else If (t_y < t_x):
             Return edge = y_intersect,  # TOP or BOTTOM
                       x = x_0 + t_y * x_d,
                       y = y_0 + t_y * y_d
         Else:
             Return corner = y_intersect + x_intersect
                         x = x_0 + t_x * x_d,
                         y = y_0 + t_x * y_d
         End If
    End If
End Function

Note that it would be easy to generalize the above function to work for any ray and any axis-aligned rectangle, including starting points outside the rectangle; you'd simply expand the OUTSIDE case. You'd probably also want to return some kind of OUTSIDE or INSIDE flag, so the caller can easily determine which edge or vertex/corner and from which side the ray hit the rectangle, if any.