0

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.

Flexan
  • 33
  • 5
  • Not sure what X and Y you are looking for. If, say, you rotate 20 degrees clockwise, what "point" is the X,Y you want? The way I see it, the X,Y is the point you draw the image at. In your code you are translating to draw at point BaseX,BaseY then undo this translation before drawing image (X-BaseX,Y-BaseY). Don't understand why you do this, but this places your image at X,Y with rotation Rotation. – Adam Sep 18 '22 at 21:10
  • The point I want is the top left of the rotated image. If I mark the top left corner of the image with a blue dot, then rotate it 90 degrees, the blue dot will be at the top right corner. I want the new width and height of the image and the new top left corner of the image. I'm translating it to BaseX and BaseY so it rotates around that point. So I'm removing BaseX and BaseY from X and Y because they are now relative. – Flexan Sep 18 '22 at 21:25
  • https://stackoverflow.com/questions/19830477/find-the-bounding-rectangle-of-rotated-rectangle – dr.null Sep 18 '22 at 22:15
  • @dr.null I have implemented the method, but it seems like it's producing the [same output as above](https://imgur.com/a/BiLIf4J). I'm thinking there is something wrong with the way I'm implementing the Graphics rotation (the BaseX and BaseY is correct). – Flexan Sep 18 '22 at 23:17
  • Yes, you need the radian values for the calcs as Adam says in his answer. obviously you didn't read the [duplicate](https://stackoverflow.com/questions/622140/calculate-bounding-box-coordinates-from-a-rotated-rectangle) answer. _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._ – dr.null Sep 19 '22 at 03:03
  • I used the method from that answer. I had translated the degrees to radians by `Rotation * (Math.PI / 180)`. I noticed that it shouldn't be in brackets but it still produces the same result. – Flexan Sep 19 '22 at 10:21

1 Answers1

2

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));
Adam
  • 562
  • 2
  • 15
  • It works great, but it only works with positive degrees, and numbers like 187 degrees offset the box higher thus breaking it. – Flexan Sep 19 '22 at 10:17
  • Not sure why you are having issues with angles outside [0,180]. I get the expected results for all angles when I draw simple rectangles using `graphics.DrawRectangle` - the rectangle drawn from final (X, Y) and (Width, Height) calculations exactly contains rotated rectangle simulating image. – Adam Sep 19 '22 at 14:35
  • I have experimented a little more but I'm unable to identify the issue. Since it seems to work correctly with 0-180 degrees (which is all I need), I'll accept the answer. – Flexan Sep 20 '22 at 13:03