user16484677
user16484677

Reputation: 135

Scale rectangle to fit inside a rotated rectangle

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.

enter image description here

enter image description here

enter image description here

enter image description here

Upvotes: 3

Views: 710

Answers (2)

Mohammed Thalha
Mohammed Thalha

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

MBo
MBo

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)) enter image description here

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:

enter image description here

Upvotes: 2

Related Questions