Calculate Bounding box coordinates from a rotated rectangle

Viewed 85803

I have the coordinates of the top left point of a rectangle as well as its width, height and rotation from 0 to 180 and -0 to -180.

I am trying to get the bounding coordinates of the actual box around the rectangle.

What is a simple way of calculating the coordinates of the bounding box

  • Min y, max y, min x, max x?

The A point is not always on the min y bound, it can be anywhere.

I can use matrix the transform toolkit in as3 if needed.

12 Answers
  • Transform the coordinates of all four corners
  • Find the smallest of all four x's as min_x
  • Find the largest of all four x's and call it max_x
  • Ditto with the y's
  • Your bounding box is (min_x,min_y), (min_x,max_y), (max_x,max_y), (max_x,min_y)

AFAIK, there isn't any royal road that will get you there much faster.

If you are wondering how to transform the coordinates, try:

x2 = x0+(x-x0)*cos(theta)+(y-y0)*sin(theta)
y2 = y0-(x-x0)*sin(theta)+(y-y0)*cos(theta)

where (x0,y0) is the center around which you are rotating. You may need to tinker with this depending on your trig functions (do they expect degrees or radians) the sense / sign of your coordinate system vs. how you are specifying angles, etc.

The method outlined by MarkusQ works perfectly but bear in mind that you don't need to transform the other three corners if you have point A already.

An alternative method, which is more efficient, is to test which quadrant your rotation angle is in and then simply compute the answer directly. This is more efficient as you only have a worst case of two if statements (checking the angle) whereas the other approach has a worst case of twelve (6 for each component when checking the other three corners to see if they are greater than the current max or less than the current min) I think.

The basic algorithm, which uses nothing more than a series of applications of Pythagoras' theorem, is shown below. I have denoted the rotation angle by theta and expressed the check there in degrees as it's pseudo-code.

ct = cos( theta );
st = sin( theta );

hct = h * ct;
wct = w * ct;
hst = h * st;
wst = w * st;

if ( theta > 0 )
{
    if ( theta < 90 degrees )
    {
        // 0 < theta < 90
        y_min = A_y;
        y_max = A_y + hct + wst;
        x_min = A_x - hst;
        x_max = A_x + wct;
    }
    else
    {
        // 90 <= theta <= 180
        y_min = A_y + hct;
        y_max = A_y + wst;
        x_min = A_x - hst + wct;
        x_max = A_x;
    }
}
else
{
    if ( theta > -90 )
    {
        // -90 < theta <= 0
        y_min = A_y + wst;
        y_max = A_y + hct;
        x_min = A_x;
        x_max = A_x + wct - hst;
    }
    else
    {
        // -180 <= theta <= -90
        y_min = A_y + wst + hct;
        y_max = A_y;
        x_min = A_x + wct;
        x_max = A_x - hst;
    }
}

This approach assumes that you have what you say you have i.e. point A and a value for theta that lies in the range [-180, 180]. I've also assumed that theta increases in the clockwise direction as that's what the rectangle that has been rotated by 30 degrees in your diagram seems to indicate you are using, I wasn't sure what the part on the right was trying to denote. If this is the wrong way around then just swap the symmetric clauses and also the sign of the st terms.

if you are using GDI+ , you can create a new GrpaphicsPath -> Add any points or shapes to it -> Apply rotate transformation -> use GraphicsPath.GetBounds() and it will return a rectangle that bounds your rotated shape.

(edit) VB.Net Sample

Public Shared Sub RotateImage(ByRef img As Bitmap, degrees As Integer)
' http://stackoverflow.com/questions/622140/calculate-bounding-box-coordinates-from-a-rotated-rectangle-picture-inside#680877
'
Using gp As New GraphicsPath
  gp.AddRectangle(New Rectangle(0, 0, img.Width, img.Height))

  Dim translateMatrix As New Matrix
  translateMatrix.RotateAt(degrees, New PointF(img.Width \ 2, img.Height \ 2))
  gp.Transform(translateMatrix)

  Dim gpb = gp.GetBounds

  Dim newwidth = CInt(gpb.Width)
  Dim newheight = CInt(gpb.Height)

  ' http://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations
  '
  Dim rotatedBmp As New Bitmap(newwidth, newheight)

  rotatedBmp.SetResolution(img.HorizontalResolution, img.VerticalResolution)

  Using g As Graphics = Graphics.FromImage(rotatedBmp)
    g.Clear(Color.White)
    translateMatrix = New Matrix
    translateMatrix.Translate(newwidth \ 2, newheight \ 2)
    translateMatrix.Rotate(degrees)
    translateMatrix.Translate(-img.Width \ 2, -img.Height \ 2)
    g.Transform = translateMatrix
    g.DrawImage(img, New PointF(0, 0))
  End Using
  img.Dispose()
  img = rotatedBmp
End Using

End Sub

Although Code Guru stated the GetBounds() method, I've noticed the question is tagged as3, flex, so here is an as3 snippet that illustrates the idea.

var box:Shape = new Shape();
box.graphics.beginFill(0,.5);
box.graphics.drawRect(0,0,100,50);
box.graphics.endFill();
box.rotation = 20;
box.x = box.y = 100;
addChild(box);

var bounds:Rectangle = box.getBounds(this);

var boundingBox:Shape = new Shape();
boundingBox.graphics.lineStyle(1);
boundingBox.graphics.drawRect(bounds.x,bounds.y,bounds.width,bounds.height);
addChild(boundingBox);

I noticed that there two methods that seem to do the same thing: getBounds() and getRect()

Apply the rotation matrix to your corner points. Then use the minimum/maximum respectively of the obtained x,y coordinates to define your new bounding box.

I am not sure I understand, but a compound transformation matrix will give you the new co-ordinates for all points concerned. If you think the rectangle may spill over the imagable area post transformation apply a clipping path.

In case you are unfamiliar with the exact definition of the matrices take a look here.

I used Region for First rotating the rectangle and then use that rotated region to detect that rectangle

        r = new Rectangle(new Point(100, 200), new Size(200, 200));         
        Color BorderColor = Color.WhiteSmoke;
        Color FillColor = Color.FromArgb(66, 85, 67);
        int angle = 13;
        Point pt = new Point(r.X, r.Y);
        PointF rectPt = new PointF(r.Left + (r.Width / 2),
                               r.Top + (r.Height / 2));
       //declare myRegion globally 
        myRegion = new Region(r);

        // Create a transform matrix and set it to have a 13 degree

        // rotation.
        Matrix transformMatrix = new Matrix();
        transformMatrix.RotateAt(angle, pt);

        // Apply the transform to the region.
        myRegion.Transform(transformMatrix);
        g.FillRegion(Brushes.Green, myRegion);
        g.ResetTransform();

now to detecting that rectangle

        private void panel_MouseMove(object sender, MouseEventArgs e)
    {


        Point point = e.Location;
        if (myRegion.IsVisible(point, _graphics))
        {
            // The point is in the region. Use an opaque brush.
            this.Cursor = Cursors.Hand;
        }
        else {
            this.Cursor = Cursors.Cross;
        }

    }

When the rectangle is rotated around its center, the calculation is trivial:

function getBoundingBox(rX, rY, rW, rH, rA) {
  const absCosRA = Math.abs(Math.cos(rA));
  const absSinRA = Math.abs(Math.sin(rA));

  const bbW = rW * absCosRA + rH * absSinRA;
  const bbH = rW * absSinRA + rH * absCosRA;
  
  const bbX = rX - (bbW - rW) / 2;
  const bbY = rY - (bbH - rH) / 2;

  return { x: bbX, y: bbY, w: bbW, h: bbH };
}
Related