(click to interact)
(mouseover/click to play GIF)
(click and drag to adjust rectangle size/position/rotation; distance to a circle is shown)
Suppose you have a rotated rectangle and a basic shape that rotation doesn't matter for (such as a point, circle, or a line segment), and you want to check whether the two are intersecting - be that for collision handling, hit testing, or whatever else.
On a glance this might seem like a bother because things are rarely too simple with rotated rectangles, but in this case it isn't - because you can "unrotate" the rectangle.
This small post is about that.
The idea
Let's suppose you have a rotated rectangle defined by a few properties,
- Size (width and height)
- Offset to rotation center
(that is, if this is set to 0,h/2, the rectangle would spin around the center of it's left edge) - Position (at which the rotation center would be at any point of time)
- Rotation angle
And you have your basic shape defined by one or two (if it's a line segment) positions and perhaps a radius (if it's a circle).
If you are to subtract rectangle's position from each of the shape positions and rotate them for the opposite of rectangle's rotation angle, you would have the local coordinates for a system where the rectangle sits unrotated at 0,0 and the shape floats relative to it. At this point you can use the generally trivial intersection checks for axis-aligned rectangles/bounding boxes.
The code
The above description translates into code in a fairly straightforward way,
rel_x = shape_x - rect_x; rel_y = shape_y - rect_y; angle = -rect_angle; local_x = cos(angle) * rel_x + cos(angle - pi / 2) * rel_y; local_y = sin(angle) * rel_x + sin(angle - pi / 2) * rel_y;
of course, as per earlier post, you can also cut the second sine/cosine call if you want,
rel_x = shape_x - rect_x; rel_y = shape_y - rect_y; angle = -rect_angle; angle_cos = cos(angle); angle_sin = sin(angle); local_x = angle_cos * rel_x - angle_sin * rel_y; local_y = angle_sin * rel_x + angle_cos * rel_y;
once you have local_x and local_y, you could check for intersection with a point like:
return local_x >= -rect_offset_x && local_x <= -rect_offset_x + rect_width && local_y >= -rect_offset_y && local_y <= -rect_offset_y + rect_height;
or for intersection with circle (related post):
delta_x = max(-rect_offset_x, min(local_x, rect_width - rect_offset_x)); delta_y = max(-rect_offset_y, min(local_y, rect_height - rect_offset_y)); return delta_x * delta_x + delta_y * delta_y < circle_radius * circle_radius;
or any other algorithm involving an axis-aligned rectangle that spans from (-rect_offset_x, -rect_offset_y) to (rect_width-rect_offset_x, rect_height-rect_offset_y).
So a complete function might look as following (in JS and common camel-case),
function pointInRotatedRectangle(pointX, pointY, rectX, rectY, rectOffsetX, rectOffsetY, rectWidth, rectHeight, rectAngle ) { var relX = pointX - rectX; var relY = pointY - rectY; var angle = -rectAngle; var angleCos = Math.cos(angle); var angleSin = Math.sin(angle); var localX = angleCos * relX - angleSin * relY; var localY = angleSin * relX + angleCos * relY; return localX >= -rectOffsetX && localX <= rectWidth - rectOffsetX && localY >= -rectOffsetY && localY <= rectHeight - rectOffsetY; }
And that's about it.
Hi. Thanks for the post. I can’t seem to understand how to get the rectOffsetX and rectOffsetY. Please shed more light on it.
It’s the “origin point” (for rotation) of the rectangle. In the interactive demo it starts off at x ~ 50%, y ~ 25%
Why does it not handle the case when circle is totally inside the rectangle?
In your other post where its unrortated it does handle that and shows it red when circle in inside rectangle.
Although in this one it just again goes black if circle is inside rectangle.
It does handle that, it is just that the demo is showing whether the circle is touching a specific rectangle side as a line. Might revise it later to fill the rectangle with color on any hit instead.
This is somethink like SAT (separating axis theorem)?
I’m not sure if it classifies directly as SAT, but many algorithms rely on projecting into local coordinates for ease of calculation.