Search code examples
c#winformssystem.drawing

Rectangle bounds of rotated image


I am drawing an image rotated with Graphics transform, but I am unable to get the position and size of the rotated image.

Paint event:

graphics.TranslateTransform(BaseX, BaseY);
graphics.RotateTransform((float)Rotation);
graphics.DrawImage(Image, X - BaseX, Y - BaseY, Image.Width, Image.Height);

ToRectangle method:

public Rectangle ToRotatedRectangle() {
    // Code here to rotate X, Y, Image.Width and Image.Height values
    return new Rectangle(rotatedX, rotatedY, rotatedWidth, rotatedHeight);
}

I have seen several other posts that can get the rotated size, but none of them include the X and Y values. I have attempted rotating them individually but the location is not correct.


Solution

  • Be careful: 'graphics.RotateTransform(X)' rotates image clockwise X degrees whereas 'Math.Cos(X)' and 'Math.Sin(X)' calculate based on X radians.

    Based on your translation/rotation, we can calculate the sin and cos values using trig.

    graphics.TranslateTransform(BaseX, BaseY);
    graphics.RotateTransform((float)Rotation);
    
    ...
    
    var sin = Math.Sin(Rotation * Math.PI / 180.0);
    var cos = Math.Cos(Rotation * Math.PI / 180.0);
    

    the resultant graphics matrix is multiplied into any given (X,Y) using linear algebra as follows

    // (X, Y) =>
    // | cos  -sin  BaseX | | X |   | X*cos-Y*sin+BaseX |
    // | sin   cos  BaseY | | Y | = | X*sin+Y*cos+BaseY |
    // |  0     0     1   | | 1 |   |        1          |
    // => (X*cos-Y*sin+BaseX, X*sin+Y*cos+BaseY)
    

    the 4 corner points as drawn on graphics are:

    var (x1, y1) = (X-BaseX, Y-BaseY);
    var (x2, y2) = (X-BaseX+Image.Width, Y-BaseY);
    var (x3, y3) = (X-BaseX, Y-BaseY+Image.Height);
    var (x4, y4) = (X-BaseX+Image.Width, Y-BaseY+Image.Height);
    

    thus, after translation/rotation, they become

    var (X1, Y1) = (cos*(X-BaseX)-sin*(Y-BaseY)+BaseX, sin*(X-BaseX)+cos*(Y-BaseY)+BaseY);
    var (X2, Y2) = (cos*(X-BaseX+Image.Width)-sin*(Y-BaseY)+BaseX, sin*(X-BaseX+Image.Width)+cos*(Y-BaseY)+BaseY);
    var (X3, Y3) = (cos*(X-BaseX)-sin*(Y-BaseY+Image.Height)+BaseX, sin*(X-BaseX)+cos*(Y-BaseY+Image.Height)+BaseY);
    var (X4, Y4) = (cos*(X-BaseX+Image.Width)-sin*(Y-BaseY+Image.Height)+BaseX, sin*(X-BaseX+Image.Width)+cos*(Y-BaseY+Image.Height)+BaseY);
    

    To get the top, left corner, you would need the smallest from each X and Y value. The width and height would be the difference between the largest and smallest X's and Y's, which can be simplified as shown below.

    var (X, Y) = (Math.Min(Math.Min(X1, X2), Math.Min(X3, X4)), Math.Min(Math.Min(Y1, Y2), Math.Min(Y3, Y4)));
    var (Width, Height) = (Math.Abs(cos*Image.Width)+Math.Abs(sin*Image.Height), Math.Abs(sin*Image.Width)+Math.Abs(cos*Image.Height));