Математический вопрос о вращении точки в .Net

У меня есть частичный ответ. Начиная с:

newHeight = Высота * cos (радианы) + Ширина * sin (радианы)
newWidth = Высота * sin (радианы) + Ширина * cos (радианы)

Я могу перевернуть уравнения, чтобы получить:

temp = sqr (cos (радианы)) - sqr (sin (радианы))
Высота = newHeight * cos (радианы) - newWidth * sin (радианы) / temp
Ширина = newWidth * cos (радианы) - newHeight * sin (радианы) / темп

Приведенные выше уравнения действуют только для диапазонов углов 0-28, 62-90, 180-208 и 242-270. За пределами этих диапазонов вычисленные границы слишком велики и приводят к переполнению на 45, 135, 225 и 315.
Я полагаю, мне нужно определить, в каком квадранте я нахожусь, и немного изменить уравнения. Любые идеи?


Я изо всех сил пытался понять, что именно я прошу в этом вопросе, поэтому, надеюсь, следующий пример прояснит ситуацию.
В этом примере берется квадрат 100x100, вращается его на 12 градусов и прибавляется 100 к ширина. Я хочу узнать размеры прямоугольника, который при повороте на 12 градусов приведет к тому же прямоугольнику, без добавления 100 к ширине впоследствии:

Нарисованные прямоугольники - это границы повернутой фигуры, а не сама фигура:

    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))

Посредством следа и ошибки я нашел значения offsetX и offsetY, которые приведут к тому же прямоугольнику для этого конкретного случая: квадрату 100x100, повернутому на 12 градусов с добавлением 100 к ширине. Я уверен, что это связано с грехом, потому что или с тем и другим вместе, но замораживание мозгов не позволяет мне построить формулу.

ETA: В этом случае я увеличиваю ширину, но мне нужно общее решение для изменения ширины, высоты или того и другого.

ETA2: в этом случае размер результирующей рамки прямоугольника составлял 218,6 x 118,6. Чтобы получить эти границы после поворота на 12 градусов, границы начального прямоугольника были примерно 207 x 79.


Оригинал:

Я использую следующие стандартные процедуры, чтобы получить границы изображения после того, как оно было повернуто на заданный угол относительно его центра. Границы смещены так, чтобы центр изображения всегда был в одном и том же месте:

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  

У меня вопрос:

У меня есть несколько BoundsAfterRotation, которые были повернуты на угол вокруг своего центра. Я меняю ширину и / или высоту границ. Как я могу работать в обратном направлении, чтобы найти исходный imageBounds, который создал бы BoundsAfterRotation?


person Jules    schedule 23.06.2010    source источник
comment
Я не понимаю вашего вопроса, это чисто математический вопрос AFAIK. Можете ли вы перефразировать свой вопрос, используя только математическую лексику? Вы также можете посмотреть: en.wikipedia.org/wiki/Rotation_matrix   -  person mathk    schedule 23.06.2010
comment
@ mathk, это чисто математический вопрос. Если у меня есть квадрат, и я поверну его на угол, а затем увеличу ширину на W, я хочу знать, насколько изменились ширина и расположение исходного (до поворота) квадрата. В зависимости от угла он будет отличаться от W.   -  person Jules    schedule 23.06.2010
comment
@Jens, да, угол такой же. Я также всегда вращаюсь вокруг центра.   -  person Jules    schedule 23.06.2010
comment
Я не понимаю вашего вопроса. Игнорируя ошибки округления, ширина квадрата одинакова независимо от того, под каким (2d) углом вы на него смотрите. Вы спрашиваете, как рассчитать эту ширину (т.е. расстояние между двумя точками)?   -  person BlueRaja - Danny Pflughoeft    schedule 23.06.2010


Ответы (3)


Просто поверните его обратно на тот же угол, отрицательный. Обратите внимание, что вы можете рассмотреть возможность использования класса System.Drawing.Matrix. У него есть метод RotateAt (). Используйте метод TransformPoints (), чтобы применить поворот. И метод Invert () для создания матрицы, которая отображает точки обратно.

person Hans Passant    schedule 23.06.2010
comment
Это сработает? Я был бы удивлен. Я попробую позже. Придется поехать смотреть Англию, надеюсь, не вылететь из чемпионата мира! - person Jules; 23.06.2010

Вы можете переходить от изображения к вращению, но не от вращения к изображению. Это связано с тем, что при повороте необходимо округлить положение пикселей, и новое изображение выглядит немного иначе.

Поэтому вы всегда должны придерживаться исходного изображения и делать копии изображения для каждого поворота.

Простое вращение назад приведет к двойному округлению.

person MrFox    schedule 23.06.2010
comment
Я сохраняю исходное изображение. См. Мой комментарий к математике выше, и, надеюсь, это проясняет, что именно я пытаюсь сделать. - person Jules; 23.06.2010

У меня нет времени давать полный ответ, но учтите следующее: если вы начнете с квадрата, повернутого на пи / 2, чтобы он выглядел как <>, и пусть альфа будет углом между линией, соединяющей центр квадрат с его крайним правым краем и осью x, тогда довольно простая тригонометрия говорит вам, что ширина повернутого квадрата равна cos(alpha)*d, где d - диагональ квадрата, если альфа находится между -pi / 4 и pi / 4 .

Теперь вам «просто» нужно вычислить альфу по вашему углу, проверить, какие из ребер важны для определения ширины, и рассчитать «неповоротную» ширину по диагонали.

person Jens    schedule 23.06.2010
comment
Привет, это выглядит многообещающе. Есть ли подобное уравнение, которое связывает новую высоту с альфой и d ?. Если он у меня есть, значит, у меня достаточно информации, чтобы рассчитать требуемую начальную ширину и высоту. Спасибо. - person Jules; 24.06.2010
comment
Я нашел другое уравнение и почти получил ответ. Я изменил свой вопрос, чтобы показать, что у меня есть на данный момент. - person Jules; 25.06.2010