user1078642
user1078642

Reputation: 51

How to find out if a ray intersects a rectangle?

We have a ray that starts at point A(X, Y) and goes on forever through given point B(X, Y) != A. We have a rectangle defined by points K,L,M,N each with its (X, Y).

I wonder how to detect if our ray intersects with any point of our rectangle (get a bool, not precice coordinates)? What is algorithm for calculating such value?

Upvotes: 1

Views: 8028

Answers (4)

comingstorm
comingstorm

Reputation: 26087

You probably want to compute the segment (if any) of the ray AB that intersects the rectangle. If your rectangle is axis-aligned, this will be easier to compute in a numerical sense, but the logic should be similar.


You can represent a directed line L as [a, b, c] such that, if point P is (X, Y):

let L(P) =  a*X + b*Y + c

then, if L(P) == 0, point P is on L
      if L(P) > 0, point P is to the left of L
      if L(P) < 0, point P is to the right of L

Note that this is redundant in the sense that, given any k > 0, [k*a, k*b, k*c] represents the same line (this property makes it a homogeneous coordinate system). We can also represent points with homogeneous coordinates by augmenting them with a third coordinate:

2D point P = (X, Y)
-> homogeneous coordinates [x, y, w] for P are [X, Y, 1]
   L(P)  =  L.a*P.x + L.b*P.y + L.c*P.w  ==  a*X + b*Y + c*1

In any case, given two corners of your rectangle (say, P and Q), you can compute the homogeneous coordinates of the line through P and Q using a 3-D cross-product of their homogeneous coordinates:

homogeneous coordinates for line PQ are:  [P.X, P.Y, 1] cross [Q.X, Q.Y, 1]
  -> PQ.a = P.Y - Q.Y
     PQ.b = Q.X - P.X
     PQ.c = P.X*Q.Y - Q.X*P.Y

You can verify mathematically that points P and Q are both on the above-described line PQ.


To represent the segment of line AB that intersects the rectangle, first compute vector V = B - A, as in @btilly's answer. For homogeneous coordinates, this works as follows:

A  =  [A.X, A.Y, 1]
B  =  [B.X, B.Y, 1]
-> V  =  B - A  =  [B.X-A.X, B.Y-A.Y, 0]

for any point C on AB: homogeneous coordinates for C = u*A + v*V
  (where u and v are not both zero)

Point C will be on the ray part of the line only if u and v are both non-negative. (This representation may seem obscure, compared to the usual formulation of C = A + lambda * V, but doing it this way avoids unnecessary divide-by-zero cases...)


Now, we can compute the ray intersection: we represent a segment of the line AB by the parametric [u,v] coordinates of each endpoint: { start = [start.u, start.v]; end = [end.u, end.v] }.

We compute the edges of the rectangle in the counterclockwise direction, so that points inside the rectangle are on the left/positive side (L(P)>0) of every edge.

Starting segment is entire ray:
  start.u = 1;  start.v = 0
  end.u = 0;  end.v = 1

for each counterclockwise-directed edge L of the rectangle:
  compute:
    L(A) = L.a*A.X + L.b*A.Y + L.c
    L(V) = L.a*V.X + L.b*V.Y
    L(start) = start.u * L(A) + start.v * L(V)
    L(end) = end.u * L(A) + end.v * L(V)
  if L(start) and L(end) are both less than zero:
    exit early: return "no intersection found"
  if L(start) and L(end) are both greater or equal to zero:
    do not update the segment; continue with the next line
  else, if L(start) < 0:
    update start coordinates:
      start.u :=  L(V)
      start.v := -L(A)
  else, if L(end) < 0:
    update end coordinates:
      end.u := -L(V)
      end.v :=  L(A)

on normal loop exit, the ray does intersect the rectangle;
the part of the ray inside the rectangle is the segment between points:
  homog_start = start.u * A + start.v * V
  homog_end = end.u * A + end.v * V
return "intersection found":
  intersection_start.X = homog_start.x/homog_start.w
  intersection_start.Y = homog_start.y/homog_start.w
  intersection_end.X = homog_end.x/homog_end.w
  intersection_end.Y = homog_end.y/homog_end.w

Note that this will work for arbitrary convex polygons, not just rectangles; the above is actually a general ray/convex polygon intersection algorithm. For a rectangle, you can unroll the for-loop; and, if the rectangle is axis-aligned, you can drastically simplify the arithmetic. However, the 4-case decision in the inner loop should remain the same for each edge.

Upvotes: 0

bnaul
bnaul

Reputation: 17636

A less clever, but conceptually simpler approach: the ray intersects the rectangle if and only if it intersects at least one of the sides. So for each side of the rectangle, find the intersection (if any) of the line passing through the endpoints with the ray AB; then it's simply a range check to determine if that intersection lies is part of the line segment on the boundary of the rectangle, or if it is outside.

Upvotes: 0

dan-boa
dan-boa

Reputation: 600

You can use the sweep line algorithm to do so.

http://en.wikipedia.org/wiki/Sweep_line_algorithm

Upvotes: 0

btilly
btilly

Reputation: 46389

Let me get this straight. You have a vector v headed off in direction (b_x - a_x, b_y - a_y) and starting at (a_x, a_y).

Consider the vector w = (b_y - a_y, a_x - b_x). It is at right angles to the first. (Verify with a dot product.) Therefore for any point (p_x, p_y) you can easily tell which side of the vector it is on by taking a dot product of (p_x - a_x, p_y - a_y) with w and looking at the sign.

So take that dot product with all 4 corners of your rectangle. If any give a 0 dot product, they are on the vector, if the signs change there is an intersection, if the sign is always the same there is no intersection.

Upvotes: 3

Related Questions