Reputation: 135
I have 2 rectangles: red and black with their dimensions. Black rectangle is fitted inside red rectangle (first image). I'm trying to find the way to calculate black rectangle dimension after rotating red rectangle any degrees (45deg here for simplicity) so that it is still fitted inside the red rectangle (third image). Second image shows the black rectangle before resize to fit inside the red rectangle. Center of both rectangles are at (0,0)
I've been looking at this Calculate largest inscribed rectangle in a rotated rectangle but this solution only works if the black rectangle is a square.
Can someone point me to the right direction? Thank you.
Upvotes: 3
Views: 710
Reputation: 1
This worked for me
import kotlin.math.*
data class CGSize(val width: Double, val height: Double)
data class CGRect(val x: Double, val y: Double, val width: Double, val height: Double)
fun cropRectForRectSize(rectSize: CGSize, rotationRadians: Double): CGRect {
val quadrant = (floor(rotationRadians / PI / 2.0) % 4).toInt()
val signAlpha = if ((quadrant and 1) == 0) rotationRadians else PI - rotationRadians
val alpha = (signAlpha.rem(PI) + PI).rem(PI)
val s = CGSize(
width = rectSize.width * cos(alpha) + rectSize.height * sin(alpha),
height = rectSize.width * sin(alpha) + rectSize.height * cos(alpha)
)
val gamma = if (rectSize.width < rectSize.height) atan2(s.width, s.height)
else atan2(s.height, s.width)
val delta = PI - alpha - gamma
val length = if (rectSize.width < rectSize.height) rectSize.height
else rectSize.width
val d = length * cos(alpha)
val a = d * sin(alpha) / sin(delta)
val y = a * cos(gamma)
val x = y * tan(gamma)
return CGRect(x = x, y = y, width = s.width - 2 * x, height = s.height - 2 * y)
}
fun main() {
val rectSize = CGSize(width = 100.0, height = 200.0)
val rotationRadians = 45.0 * (PI / 180) // 45 degrees in radians
val cropRect = cropRectForRectSize(rectSize, rotationRadians)
println("Crop Rectangle: $cropRect")
}
Upvotes: 0
Reputation: 80285
Let rectangles center is coordinate origin (and center of rotation).
Large rect dimensions are 2W
, 2H
, small rect 2w
, 2h
.
First vertex (A
) of small rect has initial coordinates (w,h)
, they become (k*w,k*h)
after fitting, second one (B
) : (-k*w,k*h)
, k
is unknown yet coefficient (we need only two vertices for calculations))
For rotation angle f
in range 0..Pi/2
(and Pi..3*Pi/2
) side of large rect which can contain A
, has starting point (F
)
px = W*cos(f)-H*sin(f)
py = W*sin(f)+H*cos(f)
and direction vector
dx = sin(f)
dy = -cos(f)
and this side has parametric equation
x = px + t * dx
y = py + t * dy
We can write equation that side contains A
k*w = px + t * dx
k*h = py + t * dy
and solve it for unknowns k, t
k1 = (px * dy - py * dx) / (w * dy - h * dx)
Similarly side that might contain B
px = W*cos(f)-H*sin(f) //same point
py = W*sin(f)+H*cos(f)
and direction vector
d2x = -cos(f)
d2y = -sin(f)
-k*w = px + t * d2x
k*h = py + t * d2y
k2 = (px * d2y - py * d2x) / (-w * d2y - h * d2x)
We need to choose smaller valid value from k1
and k2
(perhaps some results are just invalid - negative etc)
Similar calculations for angle f
in range Pi/2..Pi
(and 3*Pi/2..2*Pi
) with corresponding sides.
Implemented in Delphi to visually control correctness. Formulas depend on rotation angle quadrant: quad = 0,1,2,3 for rotation angle f in range 0..Pi/2, Pi/2..Pi and so on. (Really we can diminish ranges to 0..Pi due to the symmetry)
procedure TForm1.RectInRotRect(hw, hh, hWW, hHH: Integer; f: Double);
var
px, py, dx, dy, k, kmin: Double;
pts: array [0 .. 3] of TPoint;
quad: Integer;
begin
kmin := 999999;
quad := Floor(2 * f / Pi);
if Odd(quad) then
begin
px := hWW * cos(f) + hHH * sin(f);
py := hWW * sin(f) - hHH * cos(f);
dx := -cos(f);
dy := -sin(f);
if (hw * dy - hh * dx) <> 0 then
begin
k := (px * dy - py * dx) / (hw * dy - hh * dx);
if quad >= 2 then
k := -k;
if k > 0 then
kmin := Min(kmin, k);
end;
if (-hw * dx + hh * dy) <> 0 then
begin
k := (px * dx + py * dy) / (-hw * dx + hh * dy);
if quad >= 2 then
k := -k;
if k > 0 then
kmin := Min(kmin, k);
end
end
else
begin
px := hWW * cos(f) - hHH * sin(f);
py := hWW * sin(f) + hHH * cos(f);
dx := sin(f);
dy := -cos(f);
if (hw * dy - hh * dx) <> 0 then
begin
k := (px * dy - py * dx) / (hw * dy - hh * dx);
if quad >= 2 then
k := -k;
if k > 0 then
kmin := Min(kmin, k);
end;
if (-hw * dx + hh * dy) <> 0 then
begin
k := (px * dx + py * dy) / (-hw * dx + hh * dy);
if quad >= 2 then
k := -k;
if k > 0 then
kmin := Min(kmin, k);
end;
end;
kmin
is used to scale inner rectangle
Result of work:
Upvotes: 2