views:

116

answers:

3

I have a partial answer. Starting with:

newHeight = Height * cos(radians) + Width * sin(radians)
newWidth = Height * sin(radians) + Width * cos(radians)

I can reverse the equations to get:

temp = sqr(cos(radians)) - sqr(sin(radians))
Height = newHeight * cos(radians) - newWidth * sin(radians) / temp
Width = newWidth * cos(radians) - newHeight * sin(radians) / temp

The above equations only behave for the angle ranges 0-28, 62-90, 180-208 and 242-270. Out of these ranges, the calculated bounds are too large and result in an overflow at 45, 135, 225 and 315.
I presume I need to detect which quadrant I'm in and modify the equations slightly. Any ideas?


I've struggled to be clear on what exactly I'm asking for in this question so hopefully the following example will clear things up.
What the example does is take a 100x100 square, rotate it by 12 degrees and add 100 to the width. What I want to do is find out the the dimensions of a rectangle that when rotated by 12 degrees will result in the same rectangle, without adding the 100 to the width afterwards:

The rectangles drawn are the bounds of the rotated shape, not the shape itself:

    Dim radAngle = Math.PI * 12 / 180.0R
    Dim widthChange = 100

    Dim b2 = New RectangleF(200, 200, 100, 100)
    b2 = GetBoundsAfterRotation(b2, radAngle)
    b2.Width += widthChange
    g.DrawRectangle(Pens.Red, ToRectangle(b2))

    Dim offsetY = 21
    Dim offsetX = -7
    b2 = New RectangleF(200, 200, 100 + widthChange - offsetX, 100 - offsetY)
    b2 = GetBoundsAfterRotation(b2, radAngle)
    b2.X += CInt(offsetX / 2)
    b2.Y += CInt(offsetY / 2)
    g.DrawRectangle(Pens.Green, ToRectangle(b2))

Through trail and error I found the values of offsetX and offsetY that will result in the same rectangle for this particular case : a 100x100 square rotated by 12 degrees with 100 added to the width. I'm sure it involves sin, cos or both somewhere but brain freeze prevents me from building the formula.

ETA: In this case I increase the width, but I need a general solution for the width, height or both changing.

ETA2: For this case, the resultant rectangle bound's size was 218.6 x 118.6. To get these bounds after rotating through 12 degrees, the start rectangle bounds were around 207 x 79.


Original:

I use the following, standard, routines to get the bounds of an image after it has been rotated by a specified angle, about its centre. The bounds are offset so that the centre of the image is always in the same place:

Public Function GetBoundsAfterRotation(ByVal imageBounds As RectangleF, ByVal radAngle As Double) As RectangleF

    Dim w = imageBounds.Width
    Dim h = imageBounds.Height
    Dim rotationPoints As PointF() = {New PointF(0, 0), New PointF(w, 0), New PointF(0, h), New PointF(w, h)}
    RotatePoints(rotationPoints, New PointF(w / 2.0F, h / 2.0F), radAngle)
    Dim newBounds = GetBoundsF(rotationPoints)
    Dim x = imageBounds.X + newBounds.X  //Offset the location to ensure the centre point remains the same
    Dim y = imageBounds.Y + newBounds.Y
    Return New RectangleF(New PointF(x, y), newBounds.Size)

End Function

//

Public Shared Sub RotatePoints(ByVal pnts As PointF(), ByVal origin As PointF, ByVal radAngle As Double)
    For i As Integer = 0 To pnts.Length - 1
        pnts(i) = RotatePoint(pnts(i), origin, radAngle)
    Next
End Sub  

//

Public Shared Function RotatePoint(ByVal pnt As PointF, ByVal origin As PointF, ByVal radAngle As Double) As PointF
    Dim newPoint As New PointF()
    Dim deltaX As Double = pnt.X - origin.X
    Dim deltaY As Double = pnt.Y - origin.Y
    newPoint.X = CSng((origin.X + (Math.Cos(radAngle) * deltaX - Math.Sin(radAngle) * deltaY)))
    newPoint.Y = CSng((origin.Y + (Math.Sin(radAngle) * deltaX + Math.Cos(radAngle) * deltaY)))
    Return newPoint
End Function  

//

Public Shared Function GetBoundsF(ByVal pnts As PointF()) As RectangleF
    Dim left As Single = pnts(0).X
    Dim right As Single = pnts(0).X
    Dim top As Single = pnts(0).Y
    Dim bottom As Single = pnts(0).Y

    For i As Integer = 1 To pnts.Length - 1
        If pnts(i).X < left Then
            left = pnts(i).X
        ElseIf pnts(i).X > right Then
            right = pnts(i).X
        End If

        If pnts(i).Y < top Then
            top = pnts(i).Y
        ElseIf pnts(i).Y > bottom Then
            bottom = pnts(i).Y
        End If
    Next

    Return New RectangleF(left, top, CSng(Math.Abs(right - left)), CSng(Math.Abs(bottom - top)))
End Function  

My question is:

I have some BoundsAfterRotation that were rotated by an angle about its centre. I change the width and/or height of the bounds. How can I work backwards to find the original imageBounds that would have created the BoundsAfterRotation?

+1  A: 

Just rotate it back by the same angle, negative. Note that you could consider using the System.Drawing.Matrix class. It has a RotateAt() method. Use the TransformPoints() method to apply the rotation. And the Invert() method to create a matrix that maps points back.

Hans Passant
Will that work? I'd be surprised. I'll give it a go later. Have to go and watch England, hopefully, not get knocked out of the world cup!
Jules
A: 

You can go from image to rotation, but not from rotation to image. That is because when you rotate, you have to round the pixel positions and the new image looks a little bit different.

Therefore you must always hold on to the original image and make copies of the image for every roation.

Simply rotating back will double rounding.

MrFox
I keep hold of the original image. See my comment to mathk above, and hopefully that clarifies what exactly I'm trying to do.
Jules
A: 

I don't have the time to give a complete answer, but consider the following: If you start with the square rotated by pi/2, so that it looks like <>, and let alpha be the angle between the line connecting the center of the square with its rightmost edge and the x-axis, then pretty basic trigonometry tells you, that the width of the rotated square is cos(alpha)*d, with d being the diagonal of the square, if alpha is between -pi/4 and pi/4.

Now, you "just" have to calculate alpha from your angle, check wich ones of the edges are the ones important to know the width, and calculate the "unrotated" width from the diagonal.

Jens
Hi, this looks promising. Is there a similar equation that relates the new height to alpha and d?. If I have this, then I have enough information to calculate the required starting width and height. Thanks.
Jules
I found another equation, and nearly have an answer. I've modified my question to show what I have so far.
Jules