Knight school Ответов: 2

Как сделать 3D вращение с помощью мыши переместить


Я пытался решить эту проблему в течение нескольких месяцев (если не года). Кажется, я не могу определить направление вращения относительно 3D-модели на экране. Я могу определить направление оси (как углы), но не могу заставить направление мыши вращаться вокруг модели(точно так же, как программное обеспечение для моделирования, такое как Sketchup).

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

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

Код ротации был от Вращающийся твердый куб с помощью VB.NET и GDI+ | codeNtronix[^]

Что я уже пробовал:

Dim Distance_Between As Integer = 0 'How much rotation
  
Distance_Between = Math.Sqrt(((e.X - m_PanStartPoint.X) ^ 2) + ((e.Y - m_PanStartPoint.Y) ^ 2))

Dim X_Angle_Diff As Integer = Angle_Axis_X - Angle_Mouse
Dim Y_Angle_Diff As Integer = (Angle_Axis_Y) - Angle_Mouse

If X_Angle_Diff <> 0 Then
   X_Rotate = Orig_X_Rot - (X_Angle_Diff * (Distance_Between / 2000))
End If

If Y_Angle_Diff <> 0 Then
   Y_Rotate = Orig_Y_Rot + (Y_Angle_Diff * (Distance_Between / 2000))
End If


Извините, что не совсем ясно выразился.
Вы правы, точка вращения не указана, но она будет (до тех пор, пока я не смогу тренировать сдвиг относительно другой точки вращения, т. е. расстояние от нарисованной нулевой точки и вращение мыши) нулевой точкой рисования. Нулевая точка чертежа позиционируется матричным переводом (масштабирование также выполняется в это время).

Dim translateMatrix1 As New Matrix
                translateMatrix1.Scale(Scale_Path, Scale_Path, MatrixOrder.Append)
                translateMatrix1.Translate(Offset_X_Path, Offset_Y_Path, MatrixOrder.Append)
                Temp_Path.Transform(translateMatrix1)



Где я сейчас нахожусь после помощи Ральфа Мейера:-
Dim Correct_Speed_Factor As Decimal = Scale_Path * 0.5 ' set speed of rotation relative to scale

Dim Angle_Mouse As Decimal = Math.Atan2(Orbit_Start.Y - e.Y, Orbit_Start.X - e.X) * 180 / Math.PI

If Angle_Mouse < 0 Then
    Angle_Mouse = 360 + Angle_Mouse
End If


Dim Distance_Between As Decimal = 0 ' To account how much rotation


'Get distance of coord from mousedown to mouse postion
Distance_Between = Math.Sqrt(((e.X - Orbit_Start.X) ^ 2) + ((e.Y - Orbit_Start.Y) ^ 2))


'Get Positive differance between mouse & axis direction
Dim X_Angle_Diff As Decimal = Calc_Angle_dif(Y_Direction_Angle, Angle_Mouse) 'Y_Direction_Angle - Angle_Mouse


Dim Y_Angle_Diff As Decimal = Calc_Angle_dif(X_Direction_Angle, Angle_Mouse)
Dim Z_Angle_Diff As Decimal = (Calc_Angle_dif(X_Direction_Angle, Angle_Mouse) + Calc_Angle_dif(Y_Direction_Angle, Angle_Mouse)) / 2

  X_Rotate = Orig_X_Rot - ((Distance_Between * ((X_Angle_Diff - 90) / 90)) / Correct_Speed_Factor)

    Y_Rotate = Orig_Y_Rot - ((Distance_Between * ((Y_Angle_Diff - 90) / 90)) / Correct_Speed_Factor)
        Z_Rotate = Orig_Z_Rot - ((Distance_Between * ((Z_Angle_Diff - 90) / 90)) / Correct_Speed_Factor)

        'Then redraw


С функциями
Private Function Calc_Angle_dif(ByVal firstAngle As Single, ByVal secondAngle As Single) As Decimal
     Dim difference As Decimal = secondAngle - firstAngle
     Select Case difference
         Case Is < -180
             difference += 360
         Case Is > 180
             difference -= 360
     End Select
     If secondAngle = firstAngle Then
         Return 0
     Else
         Return (Math.Abs(difference))
     End If
     End
 End Function


Это работает довольно хорошо, но я, кажется, блокирую вращение, когда X &Y находятся в одной плоскости. Другая проблема заключается в том, что через некоторое время(не всегда в перевернутом состоянии) направление крена меняется на противоположное.

Странно, и я не совсем уверен, в чем причина. Любая помощь приветствуется, так как моя математика становится очень растянутой.

Я думаю, что, возможно, нашел причину этих ошибок. формула 3D-вращения работает при вращении X, но Y и Z находятся во вращении экрана. Это вычисление матриц, которое, возможно, немного далеко для моих математических экспертов. Может быть, нужна помощь, чтобы найти причину.

Public Function RotateX(ByVal angle As Integer) As Point3D
        Dim rad As Double, cosa As Double, sina As Double, yn As Double, zn As Double
 
        rad = angle * Math.PI / 180
        cosa = Math.Cos(rad)
        sina = Math.Sin(rad)
        yn = Me.Y * cosa - Me.Z * sina
        zn = Me.Y * sina + Me.Z * cosa
        Return New Point3D(Me.X, yn, zn)
    End Function
 
    Public Function RotateY(ByVal angle As Integer) As Point3D
        Dim rad As Double, cosa As Double, sina As Double, Xn As Double, Zn As Double
 
        rad = angle * Math.PI / 180
        cosa = Math.Cos(rad)
        sina = Math.Sin(rad)
        Zn = Me.Z * cosa - Me.X * sina
        Xn = Me.Z * sina + Me.X * cosa
 
        Return New Point3D(Xn, Me.Y, Zn)
    End Function
 
    Public Function RotateZ(ByVal angle As Integer) As Point3D
        Dim rad As Double, cosa As Double, sina As Double, Xn As Double, Yn As Double
 
        rad = angle * Math.PI / 180
        cosa = Math.Cos(rad)
        sina = Math.Sin(rad)
        Xn = Me.X * cosa - Me.Y * sina
        Yn = Me.X * sina + Me.Y * cosa
        Return New Point3D(Xn, Yn, Me.Z)
    End Function



Использование решения 2 от Ральфа Мейера

Вот проблема, с которой я сталкиваюсь, меняя точку вращения на точку привязки.
Обновлено после комментариев Ральфа Мейера.
Imports System.Drawing.Graphics

Public Class Form2
    'Inherits Control

    Protected m_vertices(8) As Point3D
    Protected m_faces(6, 4) As Integer
    Protected m_colors(6) As Color
    Protected m_brushes(6) As Brush


    Dim Offset_X As Integer
    Dim Offset_Y As Integer
    Dim Scale As Integer = 100

    Sub New()
        ' Enable double-buffering to eliminate flickering.
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)

        ' Create the cube vertices.

        m_vertices = New Point3D() {
                     New Point3D(-1, 1, -1),
                     New Point3D(1, 1, -1),
                     New Point3D(1, -1, -1),
                     New Point3D(-1, -1, -1),
                     New Point3D(-1, 1, 1),
                     New Point3D(1, 1, 1),
                     New Point3D(1, -1, 1),
                     New Point3D(-1, -1, 1)}

        ' Create an array representing the 6 faces of a cube. Each face is composed by indices to the vertex array
        ' above.
        m_faces = New Integer(,) {{0, 1, 2, 3}, {1, 5, 6, 2}, {5, 4, 7, 6}, {4, 0, 3, 7}, {0, 4, 5, 1}, {3, 2, 6, 7}}

        ' Define the colors of each face.
        m_colors = New Color() {Color.BlueViolet, Color.Cyan, Color.Green, Color.Yellow, Color.Violet, Color.LightSkyBlue}

        ' Create the brushes to draw each face. Brushes are used to draw filled polygons.
        For i = 0 To 5
            m_brushes(i) = New SolidBrush(m_colors(i))
        Next




    End Sub

    Property xAngle As Integer
        Get
            Return my_xAngle
        End Get
        Set(value As Integer)
            my_xAngle = (value + 360) Mod 360
            Me.Invalidate()
        End Set
    End Property

    Private my_xAngle As Integer

    Property yAngle As Integer
        Get
            Return my_yAngle
        End Get
        Set(value As Integer)
            my_yAngle = (value + 360) Mod 360
            Me.Invalidate()
        End Set
    End Property
    Private my_yAngle As Integer

    Property zAngle As Integer
        Get
            Return my_zAngle
        End Get
        Set(value As Integer)
            my_zAngle = (value + 360) Mod 360
            Me.Invalidate()
        End Set
    End Property

    Private my_zAngle As Integer

    Dim Shift_X As Integer 'Shift in X Pivot point
    Dim Shift_Y As Integer 'Shift in Y Pivot point
    Dim Shift_Z As Integer 'Shift in Z Pivot point

    ' Used for Panning
    Dim Orig_X_Shift As Integer
    Dim Orig_Y_Shift As Integer


    Dim X_Zero_Track As Integer 'Track Origin point in X
    Dim Y_Zero_Track As Integer 'Track Origin point in Y

    Protected Overrides Sub OnMouseDown(e As System.Windows.Forms.MouseEventArgs)

        If e.Button = Windows.Forms.MouseButtons.Left Then
            MousePress = True
            MousePosLast = e.Location
        End If


        'Pan
        If e.Button = Windows.Forms.MouseButtons.Middle Then
            MousePosLast = e.Location
            Orig_X_Shift = Offset_X
            Orig_Y_Shift = Offset_Y
        End If



        MyBase.OnMouseDown(e)
    End Sub
    Private MousePress As Boolean = False
    Protected Overrides Sub OnMouseUp(e As System.Windows.Forms.MouseEventArgs)
        MousePress = False

        'Reset it back to position of Origin on No Snap
        snap = False
        Offset_X = X_Zero_Track
        Offset_Y = Y_Zero_Track
        Shift_X = 0 ' Zero Pivot point X
        Shift_Y = 0 ' Zero Pivot point Y
        Shift_Z = 0  'Zero Pivot point Z

        MyBase.OnMouseUp(e)
    End Sub



    Private snap As Boolean = False


    Protected Overrides Sub OnMouseMove(e As System.Windows.Forms.MouseEventArgs)

        'Orbit
        If MousePress Then

            MouseMoveHorizontal = MousePosLast.X - e.X
            MouseMoveVertical = MousePosLast.Y - e.Y
            MousePosLast = e.Location

            Dim Speed_Reducer As Integer = 5

            Dim xxAngle As Integer = (xAngle + 360) Mod 180
            Dim yyAngle As Integer = (yAngle + 360) Mod 180

            If (yyAngle > 135) Or (yyAngle <= 45) Then
                yAngle += MouseMoveHorizontal / Speed_Reducer
                xAngle -= MouseMoveVertical / Speed_Reducer
            Else
                xxAngle = (xAngle + 360) Mod 180
                If (xxAngle > 135) Or (xxAngle <= 45) Then
                    yAngle += MouseMoveHorizontal / Speed_Reducer
                    xAngle -= MouseMoveVertical / Speed_Reducer
                Else
                    xAngle += MouseMoveHorizontal / Speed_Reducer
                    yAngle -= MouseMoveVertical / Speed_Reducer
                End If
            End If

            Me.Invalidate()
        End If




        'Pan
        If e.Button = Windows.Forms.MouseButtons.Middle Then
            Offset_X = Orig_X_Shift + (e.X - MousePosLast.X)
            Offset_Y = Orig_Y_Shift + (e.Y - MousePosLast.Y)
            Me.Invalidate()
        End If

        MyBase.OnMouseMove(e)
    End Sub
    Private MouseMoveVertical As Integer = 0
    Private MouseMoveHorizontal As Integer = 0
    Private MousePosLast As Point




    Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
        Dim t(8) As Point3D
        Dim f(4) As Integer
        Dim v As Point3D
        Dim avgZ(6) As Double
        Dim order(6) As Integer
        Dim tmp As Double
        Dim iMax As Integer

        ' Clear the window
        e.Graphics.Clear(Color.LightBlue)

        'Save to the original offset to set it back later
        Dim Zero_Offset_X As Integer = Offset_X
        Dim Zero_Offset_y As Integer = Offset_Y



        'Reset offset (point of orgin) realitive to pivot point
        Dim Pivot_Point As Point3D
        Pivot_Point = New Point3D(Shift_X, Shift_Y, Shift_Z)
        Pivot_Point = Pivot_Point.RotateX(xAngle).RotateY(yAngle).RotateZ(zAngle)
        Offset_X -= ((Pivot_Point.X) * Scale)
        Offset_Y -= ((Pivot_Point.Y) * Scale)


        Dim Stringf As String = "SNAP ---- X" & Shift_X & " ------ Y" & Shift_X & " ------ Z" & Shift_Z
        e.Graphics.DrawString(Stringf, New Font("Ariel", 12), New SolidBrush(Color.Black), 10, 10)





        ' Transform all the points and store them on the "t" array.
        For i = 0 To 7
            Dim b As Brush = New SolidBrush(Color.White)
            v = m_vertices(i)
            t(i) = v.RotateX(xAngle).RotateY(yAngle).RotateZ(zAngle)
            't(i) = t(i).Project(Me.ClientSize.Width, Me.ClientSize.Height, 256, 10) 'Not Used



            'Would do this as Transform Matrix to Graphicspath
            'Scale
            t(i).X *= Scale
            t(i).Y *= Scale

            'Shift Pivot Point for view
            t(i).X += Offset_X
            t(i).Y += Offset_Y
        Next

        ' Compute the average Z value of each face.
        For i = 0 To 5
            avgZ(i) = (t(m_faces(i, 0)).Z + t(m_faces(i, 1)).Z + t(m_faces(i, 2)).Z + t(m_faces(i, 3)).Z) / 4.0
            order(i) = i
        Next

        ' Next we sort the faces in descending order based on the Z value.
        ' The objective is to draw distant faces first. This is called 
        ' the PAINTERS ALGORITHM. So, the visible faces will hide the invisible ones.
        ' The sorting algorithm used is the SELECTION SORT.
        For i = 0 To 4
            iMax = i
            For j = i + 1 To 5
                If avgZ(j) > avgZ(iMax) Then
                    iMax = j
                End If
            Next
            If iMax <> i Then
                tmp = avgZ(i)
                avgZ(i) = avgZ(iMax)
                avgZ(iMax) = tmp

                tmp = order(i)
                order(i) = order(iMax)
                order(iMax) = tmp
            End If
        Next

        ' Draw the faces using the PAINTERS ALGORITHM (distant faces first, closer faces last).
        For i = 0 To 5
            Dim points() As Point
            Dim index As Integer = order(i)
            points = New Point() {
                New Point(CInt(t(m_faces(index, 0)).X), CInt(t(m_faces(index, 0)).Y)),
                New Point(CInt(t(m_faces(index, 1)).X), CInt(t(m_faces(index, 1)).Y)),
                New Point(CInt(t(m_faces(index, 2)).X), CInt(t(m_faces(index, 2)).Y)),
                New Point(CInt(t(m_faces(index, 3)).X), CInt(t(m_faces(index, 3)).Y))
            }
            e.Graphics.FillPolygon(m_brushes(index), points)
        Next


        'Draw active pivot point
        Pivot_Point = New Point3D(Shift_X, Shift_Y, Shift_Z)
        Pivot_Point = Pivot_Point.RotateX(xAngle).RotateY(yAngle).RotateZ(zAngle)
        e.Graphics.DrawString("X", New Font("Ariel", 12, FontStyle.Bold), New SolidBrush(Color.Red), ((Pivot_Point.X * Scale) + Offset_X) - 10, ((Pivot_Point.Y * Scale) + Offset_Y) - 10)



        'Draw Orgin point
        e.Graphics.DrawString("O", New Font("Ariel", 10), New SolidBrush(Color.Black), Offset_X - 8, Offset_Y - 8)

        'Track Last place of Origin
        X_Zero_Track = Offset_X ' (Zero.X * Scale) + Offset_X
        Y_Zero_Track = Offset_Y ' (Zero.X * Scale) + Offset_X


        'Reset Offset adjustments
        Offset_X = Zero_Offset_X
        Offset_Y = Zero_Offset_y




        MyBase.OnPaint(e)
    End Sub

    Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Me.WindowState = FormWindowState.Maximized

        'Set Offsets
        Offset_X = Me.Width / 2
        Offset_Y = Me.Height / 2

        'Set Scale/Size
        Scale = 100


    End Sub

    Private Sub Form2_MouseWheel(sender As Object, e As MouseEventArgs) Handles Me.MouseWheel


        Dim scale_Factor As Decimal = 1.1


        'Zoom
        If e.Delta > 0 Then 'Zoom In
            Scale *= scale_Factor

        Else 'Zoom Out
            If Scale / scale_Factor <= 0 Then 'Zoom to Small
                Exit Sub
            Else
                Scale /= scale_Factor
            End If

        End If

        Me.Invalidate()
    End Sub



    Private Sub Form2_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp

        isKeyDown = False

        'Reset it back to position of Origin on No Snap
        snap = False
        Offset_X = X_Zero_Track
        Offset_Y = Y_Zero_Track
        Shift_X = 0 ' Zero Pivot point X
        Shift_Y = 0 ' Zero Pivot point Y
        Shift_Z = 0  'Zero Pivot point Z


        Me.Invalidate()
    End Sub



    Dim isKeyDown As Boolean = False

    Private Sub Form2_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown

        'Makesure there is only fire
        If isKeyDown = True Then
            Exit Sub
        End If

        Select Case e.KeyCode
            Case Keys.NumPad1
                snap = True
                'Shift pivot to 1st point
                Shift_X = m_vertices(0).X 'Shift in X Pivot point
                Shift_Y = m_vertices(0).Y  'Shift in Y Pivot point
                Shift_Z = m_vertices(0).Z  'Shift in Z Pivot point.

            Case Keys.NumPad2
                snap = True
                'Shift pivot to 2nd point
                Shift_X = m_vertices(1).X 'Shift in X Pivot point
                Shift_Y = m_vertices(1).Y  'Shift in Y Pivot point
                Shift_Z = m_vertices(1).Z  'Shift in Z Pivot point

            Case Keys.NumPad3
                snap = True
                'Shift pivot to one 3rd point
                Shift_X = m_vertices(2).X 'Shift in X Pivot point
                Shift_Y = m_vertices(2).Y  'Shift in Y Pivot point
                Shift_Z = m_vertices(2).Z  'Shift in Z Pivot point

            Case Keys.NumPad4
                snap = True
                'Shift pivot to 4th point
                Shift_X = m_vertices(3).X 'Shift in X Pivot point
                Shift_Y = m_vertices(3).Y  'Shift in Y Pivot point
                Shift_Z = m_vertices(3).Z  'Shift in Z Pivot point

            Case Keys.NumPad5
                snap = True
                'Shift pivot to 5th point
                Shift_X = m_vertices(4).X 'Shift in X Pivot point
                Shift_Y = m_vertices(4).Y  'Shift in Y Pivot point
                Shift_Z = m_vertices(4).Z  'Shift in Z Pivot point

            Case Keys.NumPad6
                snap = True
                'Shift pivot to  6th point
                Shift_X = m_vertices(5).X 'Shift in X Pivot point
                Shift_Y = m_vertices(5).Y  'Shift in Y Pivot point
                Shift_Z = m_vertices(5).Z  'Shift in Z Pivot point

            Case Keys.NumPad7
                snap = True
                'Shift pivot to 7th point
                Shift_X = m_vertices(6).X 'Shift in X Pivot point
                Shift_Y = m_vertices(6).Y  'Shift in Y Pivot point
                Shift_Z = m_vertices(6).Z  'Shift in Z Pivot point

            Case Keys.NumPad8
                snap = True
                'Shift pivot to 8th point
                Shift_X = m_vertices(7).X 'Shift in X Pivot point
                Shift_Y = m_vertices(7).Y  'Shift in Y Pivot point
                Shift_Z = m_vertices(7).Z  'Shift in Z Pivot point

            Case Else
                snap = False
                'Shift point back to Zero/Origin
                Shift_X = 0 ' Zero Pivot point X
                Shift_Y = 0 ' Zero Pivot point Y
                Shift_Z = 0  'Zero Pivot point Z

        End Select


        'Make adjustments offsets to compensate for new pivot point. Only once fire
        Dim Pivot_Point As Point3D = New Point3D(Shift_X, Shift_Y, Shift_Z)
        Pivot_Point = Pivot_Point.RotateX(xAngle).RotateY(yAngle).RotateZ(zAngle)
        Offset_X += ((Pivot_Point.X) * Scale)
        Offset_Y += ((Pivot_Point.Y) * Scale)

        isKeyDown = True

        Me.Invalidate()
    End Sub
End Class

RedDk

Большой вопрос. Безответный и не очень "хороший" пост-корм для QA ... Но, поскольку программное обеспечение идет, по крайней мере, такое программное обеспечение, которое я покупаю, лучшей реализацией движения мыши должно быть ... Любой и все они!

Удачи тебе, мой друг!

Knight school

Спасибо, что прочитали и ответили. По крайней мере, я не схожу с ума.

Ralf Meier

Ваш вопрос мне непонятен.
Вы хотите создать 3D-вектор, соответствующий центральной точке на экране, с помощью мыши ?
В какой момент вы застряли ?
Ваш фрагмент кода ничего не говорит мне о вашей проблеме ...

Ralf Meier

Я улучшил ваш вопрос с помощью вашего опубликованного "решения" ...
Вы должны видеть это по-другому :
- у вас есть одна центральная точка и один базовый вектор для Вашего расчета
- ваше движение мыши только позволяет вам обнаружить его x - и y-движение. Если вы хотите повернуть свой вычисленный вектор результатов на этом движении. При горизонтальном перемещении вектор вращается вокруг оси Y. При вертикальном перемещении вы вращаетесь вокруг оси X. В зависимости от каждого движения вы получите новый вектор, который является основой для вашего следующего движения. Только с этим вектором вы можете сделать расчет вращения/крена и, наконец, вы можете нарисовать его.

Я не верю, что Scale-Translate-Transform очень полезен для этого ...

Knight school

Спасибо. Что произойдет, если ось уже повернута (то есть X= 45° Y =30° & Z=5°). Будет ли крен/вращение не относительно истинной модели, потому что ось не выровнена по горизонтали/вертикали?

Ralf Meier

Нет (на мой взгляд), потому что вы можете изменить только 2 оси (со смыслом), и когда вы смотрите и программное обеспечение, которое работает таким образом, вы можете увидеть то же самое. Но если ваш вектор (я думаю, что только это может быть правильным подходом) является некоторой диагональю, вы также можете свернуть/повернуть его как сложный вектор.
Я думаю, что только относительная трансформация не может привести к правильному результату ...
В своем вопросе вы ссылались на ссылку - я думаю (но сам не пробовал), что это работает точно так же ...

Knight school

Спасибо за ваши комментарии. Это проясняет в уме проблемы, которые я, возможно, создаю.
Могу ли я определить относительную ось, по которой ось указывает самое высокое и нижнее направление? Таким образом, я буду изменять только 2 оси, которые могут управляться движением мыши.

пример:- если ось Z больше всего направлена вверх, то вертикальный рулон изменится, а ось X больше всего направлена вниз, то следует использовать горизонтальный рулон. Также средняя ось не затронута.

Будет ли это работать и даст ли мне желаемый эффект??

Ralf Meier

Это зависит от вашего мнения, как это должно работать.
Вы также можете сказать (и я бы попробовал, какое поведение лучше всего), если Z указывает вверх-вниз, а X-влево-вправо, движение вверх-вниз от мыши вращает ось Z/Y вокруг оси X, а движение влево-вправо от мыши вращает ось X/Y вокруг оси Z.
"Немного" сложнее может быть, если одна (или несколько) осей уже диагональны - в этом случае вы должны повернуть оставшийся вектор к движению вокруг другого оставшегося вектора. Было бы лучше всего, если бы вы нарисовали вектор для себя на листе бумаги (например) - я думаю, что это могло бы стать яснее.

Но для вашего сведения : я никогда не делал этого раньше, но если бы это было необходимо, я бы начал вот так. Ваш проект кажется мне очень интересным ...

Knight school

Я попробовал X move = X Rotation & Y move = Y rotation, но он падает, когда модель находится в XY-представлении.

У меня есть еще одна мысль, но мне может понадобиться помощь с математикой.
Если вы возьмете начальную точку, а также точку перемещения мыши и обратно, вычислите 3D-точку обратно к оси. Это даст вам видимость перемещения начальной точки на экране в виде рулона. Это может привести к более чем одному ответу, но мы могли бы отфильтровать его по ближайшему углу предыдущего.

Как вы думаете, это путь, который возможен или длинный выстрел?

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

Ralf Meier

Я не уверен, что понимаю, что вы имеете в виду - извините.
Поскольку мне это тоже интересно, я постараюсь провести собственные эксперименты.
Сначала я бы сделал следующее : вращался в зависимости от движения мыши вокруг вертикальной/горизонтальной осей.
Я дам вам ответ, когда у меня будет результат ...

Ralf Meier

У меня нет готового кода для вас, но на самом деле есть идея, которой нужно следовать ...
Когда у вас есть базовая точка (x=0,y=0,z=0), вы можете иметь углы для вектора : угол от x0 в направлении y и угол от x0 в направлении z. Оба угла могут быть изменены вашей мышью : x_to_y с вертикальным движением и x_to_z с горизонтальным движением. Вы должны определить инкремент/декремент для каждого из этих углов, соответствующих реальному движению мыши.
Это был бы мой первый шаг, чтобы начать. Я бы понял, что внутри есть специальный контроль (который происходит от контроля), чтобы сохранить код "чистым".

Knight school

Мне кажется, я понимаю, о чем вы говорите.

Я могу получить 2D-углы (как видно на экране после преобразования) и направление от точек преобразования, скажем, 50 от исходной точки. Затем вычисляем угол с помощью Math.Atan2, но именно здесь я немного теряюсь & как преобразовать в X-Z & X-Y. Извиняюсь

Ralf Meier

Я имею в виду это :
Зет|
| /Год
|/
-----Икс
Итак, если у вас есть это так, то X2Y и X2Z оба равны 0°.
Если вы повернете X против часовой стрелки, то X станет Z после 90° или -Z после 90° по часовой стрелке.
То же самое с X и Y.
Но на самом деле у меня нет расчета на это (не хватает времени - извините).

Knight school

Надеюсь, что обновление находится где-то рядом с объяснением, которое вы предоставили, но, как вы увидите, есть еще какой-то странный последействие. Я все еще пытаюсь определить причину блокировки и выяснить, почему вращение происходит наоборот, но, возможно, вплоть до того момента, когда вы сделали X превращается в Z при повороте на 90 градусов.
Добираясь туда, я, возможно, должен был попросить о помощи некоторое время назад, но я просто собирался попытаться понять это сам. может быть, мне не стоит пытаться больше года.
Если у вас есть еще какие-то идеи, чтобы решить эту проблему, просто кричите.
Большое спасибо

Ralf Meier

смотрите мое решение ...

2 Ответов

Рейтинг:
16

Ralf Meier

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

Imports System.Drawing.Graphics

Public Class RotatingCube
    Inherits Control

    Protected m_vertices(8) As Point3D
    Protected m_faces(6, 4) As Integer
    Protected m_colors(6) As Color
    Protected m_brushes(6) As Brush

    Sub New()
        ' Enable double-buffering to eliminate flickering.
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)

        ' Create the cube vertices.
        m_vertices = New Point3D() {
                     New Point3D(-1, 1, -1),
                     New Point3D(1, 1, -1),
                     New Point3D(1, -1, -1),
                     New Point3D(-1, -1, -1),
                     New Point3D(-1, 1, 1),
                     New Point3D(1, 1, 1),
                     New Point3D(1, -1, 1),
                     New Point3D(-1, -1, 1)}

        ' Create an array representing the 6 faces of a cube. Each face is composed by indices to the vertex array
        ' above.
        m_faces = New Integer(,) {{0, 1, 2, 3}, {1, 5, 6, 2}, {5, 4, 7, 6}, {4, 0, 3, 7}, {0, 4, 5, 1}, {3, 2, 6, 7}}

        ' Define the colors of each face.
        m_colors = New Color() {Color.BlueViolet, Color.Cyan, Color.Green, Color.Yellow, Color.Violet, Color.LightSkyBlue}

        ' Create the brushes to draw each face. Brushes are used to draw filled polygons.
        For i = 0 To 5
            m_brushes(i) = New SolidBrush(m_colors(i))
        Next
    End Sub

    Property xAngle As Integer
        Get
            Return my_xAngle
        End Get
        Set(value As Integer)
            my_xAngle = (value + 360) Mod 360
            Me.Invalidate()
        End Set
    End Property
    Private my_xAngle As Integer

    Property yAngle As Integer
        Get
            Return my_yAngle
        End Get
        Set(value As Integer)
            my_yAngle = (value + 360) Mod 360
            Me.Invalidate()
        End Set
    End Property
    Private my_yAngle As Integer

    Property zAngle As Integer
        Get
            Return my_zAngle
        End Get
        Set(value As Integer)
            my_zAngle = (value + 360) Mod 360
            Me.Invalidate()
        End Set
    End Property
    Private my_zAngle As Integer



    Protected Overrides Sub OnMouseDown(e As System.Windows.Forms.MouseEventArgs)
        If e.Button = Windows.Forms.MouseButtons.Left Then
            MousePress = True
            MousePosLast = e.Location
        End If
        MyBase.OnMouseDown(e)
    End Sub
    Private MousePress As Boolean = False
    Protected Overrides Sub OnMouseUp(e As System.Windows.Forms.MouseEventArgs)
        MousePress = False
        MyBase.OnMouseUp(e)
    End Sub

    Protected Overrides Sub OnMouseMove(e As System.Windows.Forms.MouseEventArgs)
        If MousePress Then
            MouseMoveHorizontal = MousePosLast.X - e.X
            MouseMoveVertical = MousePosLast.Y - e.Y
            MousePosLast = e.Location

            Dim xxAngle As Integer = (xAngle + 360) Mod 180
            Dim yyAngle As Integer = (yAngle + 360) Mod 180

            If (yyAngle > 135) Or (yyAngle <= 45) Then
                yAngle += MouseMoveHorizontal
                xAngle -= MouseMoveVertical
            Else
                xxAngle = (xAngle + 360) Mod 180
                If (xxAngle > 135) Or (xxAngle <= 45) Then
                    yAngle += MouseMoveHorizontal
                    xAngle -= MouseMoveVertical
                Else
                    xAngle += MouseMoveHorizontal
                    yAngle -= MouseMoveVertical
                End If
            End If

            Me.Invalidate()
        End If

        MyBase.OnMouseMove(e)
    End Sub
    Private MouseMoveVertical As Integer = 0
    Private MouseMoveHorizontal As Integer = 0
    Private MousePosLast As Point

    Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
        Dim t(8) As Point3D
        Dim f(4) As Integer
        Dim v As Point3D
        Dim avgZ(6) As Double
        Dim order(6) As Integer
        Dim tmp As Double
        Dim iMax As Integer

        ' Clear the window
        e.Graphics.Clear(Color.LightBlue)

        ' Transform all the points and store them on the "t" array.
        For i = 0 To 7
            Dim b As Brush = New SolidBrush(Color.White)
            v = m_vertices(i)
            t(i) = v.RotateX(xAngle).RotateY(yAngle).RotateZ(zAngle)
            t(i) = t(i).Project(Me.ClientSize.Width, Me.ClientSize.Height, 256, 4)
        Next

        ' Compute the average Z value of each face.
        For i = 0 To 5
            avgZ(i) = (t(m_faces(i, 0)).Z + t(m_faces(i, 1)).Z + t(m_faces(i, 2)).Z + t(m_faces(i, 3)).Z) / 4.0
            order(i) = i
        Next

        ' Next we sort the faces in descending order based on the Z value.
        ' The objective is to draw distant faces first. This is called 
        ' the PAINTERS ALGORITHM. So, the visible faces will hide the invisible ones.
        ' The sorting algorithm used is the SELECTION SORT.
        For i = 0 To 4
            iMax = i
            For j = i + 1 To 5
                If avgZ(j) > avgZ(iMax) Then
                    iMax = j
                End If
            Next
            If iMax <> i Then
                tmp = avgZ(i)
                avgZ(i) = avgZ(iMax)
                avgZ(iMax) = tmp

                tmp = order(i)
                order(i) = order(iMax)
                order(iMax) = tmp
            End If
        Next

        ' Draw the faces using the PAINTERS ALGORITHM (distant faces first, closer faces last).
        For i = 0 To 5
            Dim points() As Point
            Dim index As Integer = order(i)
            points = New Point() {
                New Point(CInt(t(m_faces(index, 0)).X), CInt(t(m_faces(index, 0)).Y)),
                New Point(CInt(t(m_faces(index, 1)).X), CInt(t(m_faces(index, 1)).Y)),
                New Point(CInt(t(m_faces(index, 2)).X), CInt(t(m_faces(index, 2)).Y)),
                New Point(CInt(t(m_faces(index, 3)).X), CInt(t(m_faces(index, 3)).Y))
            }
            e.Graphics.FillPolygon(m_brushes(index), points)
        Next

        MyBase.OnPaint(e)
    End Sub

End Class


Public Class Point3D
    Protected m_x As Double, m_y As Double, m_z As Double

    Public Sub New(ByVal x As Double, ByVal y As Double, ByVal z As Double)
        Me.X = x
        Me.Y = y
        Me.Z = z
    End Sub

    Public Property X() As Double
        Get
            Return m_x
        End Get
        Set(ByVal value As Double)
            m_x = value
        End Set
    End Property

    Public Property Y() As Double
        Get
            Return m_y
        End Get
        Set(ByVal value As Double)
            m_y = value
        End Set
    End Property

    Public Property Z() As Double
        Get
            Return m_z
        End Get
        Set(ByVal value As Double)
            m_z = value
        End Set
    End Property

    Public Function RotateX(ByVal angle As Integer) As Point3D
        Dim rad As Double, cosa As Double, sina As Double, yn As Double, zn As Double

        rad = angle * Math.PI / 180
        cosa = Math.Cos(rad)
        sina = Math.Sin(rad)
        yn = Me.Y * cosa - Me.Z * sina
        zn = Me.Y * sina + Me.Z * cosa
        Return New Point3D(Me.X, yn, zn)
    End Function

    Public Function RotateY(ByVal angle As Integer) As Point3D
        Dim rad As Double, cosa As Double, sina As Double, Xn As Double, Zn As Double

        rad = angle * Math.PI / 180
        cosa = Math.Cos(rad)
        sina = Math.Sin(rad)
        Zn = Me.Z * cosa - Me.X * sina
        Xn = Me.Z * sina + Me.X * cosa

        Return New Point3D(Xn, Me.Y, Zn)
    End Function

    Public Function RotateZ(ByVal angle As Integer) As Point3D
        Dim rad As Double, cosa As Double, sina As Double, Xn As Double, Yn As Double

        rad = angle * Math.PI / 180
        cosa = Math.Cos(rad)
        sina = Math.Sin(rad)
        Xn = Me.X * cosa - Me.Y * sina
        Yn = Me.X * sina + Me.Y * cosa
        Return New Point3D(Xn, Yn, Me.Z)
    End Function

    Public Function Project(ByVal viewWidth, ByVal viewHeight, ByVal fov, ByVal viewDistance)
        Dim factor As Double, Xn As Double, Yn As Double
        factor = fov / (viewDistance + Me.Z)
        Xn = Me.X * factor + viewWidth / 2
        Yn = Me.Y * factor + viewHeight / 2
        Return New Point3D(Xn, Yn, Me.Z)
    End Function
End Class


Knight school

Извините за задержку и большое спасибо за решение. Я был в отпуске, и у меня был строгий порядок без ноутбука, и вдобавок ко всему мой компьютер решил иметь windows meltdown. Я скоро буду работать и буду тестировать обновление.
Большое спасибо (это сводило меня с ума).

Ralf Meier

Я рад снова получить от вас весточку и жду вашего ответа.

Knight school

Большое спасибо.
Один вопрос все еще не дает мне покоя-как изменить точку вращения.
Я действительно думал, что должен просто переместить точки, чтобы сделать точку вращения в начале координат, но это вызывает ошибки (т. е. сдвиги куба). Затем я попытался исправить это, вращая виртуальную точку (тот же сдвиг XYZ, что и перемещение), но это, похоже, идет совершенно неправильно.

Есть идеи?

Извините за вопрос за вопросом.

Ralf Meier

Я не совсем понимаю, что ты имеешь в виду ... Я полагаю ты имеешь в виду это :
В данный момент "точка вращения" находится в центре куба. Ты хочешь вынести его наружу? .. Если я прав, то думаю, что вы должны добавить смещение к точкам, определенным в m_vertices. Смещение должно работать с point3d и выполняет обратное преобразование.Проект-Способ. Точки из m_vertices являются факторами для ширины отображения.
Но... как вы хотите переместить эту точку смещения ? Правая кнопка мыши и MouseMove ...?

Knight school

Извините, что еще раз не объяснил проблему.
Вы правы, я хочу вращаться вокруг точки, которая не является исходной/центральной точкой (то есть одним из углов - у меня уже есть способ привязки к точке, из которой я буду вращать ее). Я также заставил его вращаться вокруг этой точки (как вы указали, смещение точки/вершин, но когда я делаю это, он заставляет куб прыгать с защелкнутым углом, где раньше был источник/центр. То, что я стремлюсь сделать, - это вращаться/вращаться относительно точки привязки, но не заставлять модель прыгать, а просто вращаться.
Мое мышление (также попытка) состоит в том, чтобы иметь плавное вращение относительно точки привязки. Я пытался изменить translateMatrix1.Translate, включая новый сдвиг, но именно здесь все идет не так.Кажется, я никак не могу смириться с тем, что это просто сдвиг-поворот - сдвиг назад.

еще раз извините, что добавляю к первоначальному вопросу.

Ralf Meier

Я не понимаю, в чем разница между решением, которое я вам дал ...
Как, по-вашему, меняется поведение ...?

Knight school

Ваше решение было идеальным, и я сразу же принял его, но, конечно, имеет некоторый эффект (по дизайну, а не по коду), который я действительно обдумал.

Я использую код не для того,чтобы просто вращать куб, а для тысяч точек на графическом пути, которые могут быть большими, сложными и смещенными в нулевую точку. Поэтому иногда полезно повернуть траекторию относительно точки, которую вы можете видеть при увеличении масштаба. Чем точка за пределами экрана, которая дает вам странный эффект, когда путь откатывается от поля зрения.

Я бы понял, если бы вы сказали, что это не то, о чем я просил, но вы, похоже, тот, кто интересуется этим видом работы и может указать мне правильное направление. Я все еще буду пытаться решить сам, но если у вас есть какие-то идеи, я буду вечно благодарен.

Ralf Meier

Нет - нет ... вы меня неправильно поняли ... Я только пытаюсь понять, что вы делаете ...
Возможно, было бы полезно, если бы у меня был ваш код для проведения собственных экспериментов.

Knight school

Извините за задержку с ответом и еще раз не объяснился должным образом.

Мой код находится так далеко от исходного поста, но то, что я пытаюсь сделать, - это повернуть/повернуть в другом положении (т. е. ближайший из углов куба к положению мыши вниз). Это должно дать эффект быстрого вращения и более естественного вращения с более локализованной точкой вращения.

Мой код находит точку поворота, которая находится ближе всего (это было легко), но когда точка поворота перемещается из одного угла в другой, куб смещается из-за изменения точки поворота. Я пытаюсь посмотреть, смогу ли я получить у вас поставляемое решение, чтобы дать этот эффект.

Я надеюсь, что это лучшее объяснение.

Knight school

Мне удалось сделать так, чтобы моя проблема проявилась на вашем решении. Мне пришлось имитировать привязку к точке с помощью клавиши Alt & у меня есть добавить масштабирование плюс панорамирование, чтобы лучше контролировать пространство куба.

См. приложенный код.

я надеюсь, что это лучше понять (Извините, что мне пришлось использовать WinForm).

Ralf Meier

Извиняюсь... какой прилагаемый код ...?

Knight school

Смотрите нижнюю часть вопроса.

Ralf Meier

Ладно ... нашел ...
Первое, что я вижу, это то, что ваш код создает спираль - я думаю, что это ваша проблема.
It comes from the changes you made inside the OnMouseDown-Method - this Method only should detect the state to make everything further in OnMouseMove possible. If you want to use it like me you should do the same : detect the Mouse-MiddleButton, create an own Boolean for it and work with this boolean inside the OnMouseMove. I don't have corrected your code in the moment but I'm sure that this will be the right approach. Also you should think about at which moment which action is executed. All the On-Methods corresponds directly to the different Events - so if you have MouseDown you should not ask for a Keystroke - for this you have an extra Event.
Когда у меня будет решение для тебя, я вернусь ...

Ralf Meier

Следующий шаг после того, как сделано 1-е) :
Если ваше вращение должно работать вокруг специальной точки, оно должно соответствовать заданным вершинам (с его масштабом), а также должно быть рассчитано с помощью угла поворота-но в настоящее время это не решается мной ...

Knight school

Что вы подразумеваете под спиралью?

Что касается методов (клавиша Alt, масштабирование и панорамирование) Я только добавил их к вашему коду, чтобы дать вам рабочую репрезентативную модель того, какая проблема у меня была.
Клавиша Alt предназначена только для создания точки привязки, а Pan + Zoom-для повышения удобства использования приложения (остановите перемещение куба за пределы экрана. Это было только для того, чтобы помочь объяснить вещи (которые трудно выразить словами).

Ralf Meier

При тестировании вашего кода я увидел, что когда я генерирую точку панорамирования вне центра куба, с каждой петлей Куба вокруг этой центральной точки радиус орбиты увеличивается (вы получаете спираль). Я полагал, что это одна из твоих проблем. Решение должно быть таким : не вращать куб - вращать (новую) центральную точку и зависимость от куба. Но (в данный момент) У меня нет никакого кода для такого поведения ...

Knight school

Извините за задержку с возвращением. Я пытался изложить то, что, как мне кажется, вы указали. Теперь это перемещает точку рисования (смещение) относительно точки разворота. Я также изменил клавишу Alt на выбор числовой клавиши (от 1 до 8), чтобы изменить точку поворота. Я надеюсь, что это то, что остановит спираль.

См. раздел обновленный код, о котором идет речь


п. С.
Меня очень интересует алгоритм живописцев и то, как удалить цилиндр из куба. Может быть, новый вопрос.

Еще раз большое спасибо

Knight school

Привет,
Ваше решение работает хорошо, но я, должно быть, что-то упускаю, например, когда вращение вращается вокруг (прибл. когда ось Z направлена на вас), ось перевернута, то есть задом наперед. Будет ли это 3Dpoint class calc? Я слышал, что есть ошибка из-за некоторых случаев (извините, я не могу найти деталь или имя снова). Вы слышали об этом, а еще лучше-есть ли у вас какие-нибудь обходные пути?

Ralf Meier

Привет,
приятно снова тебя слышать ... :)
В данный момент у меня нет ни решения, ни обходного пути, но в основном :
Назначение движения мыши (я надеюсь, вы понимаете эту работу-создание) зависит от различных углов от различных осей.
Например :
Если ось Z указывает на то, что у вас есть либо ось X в вертикальном направлении и ось Y в горизонтальном направлении, либо ось Y в вертикальном направлении и ось X в горизонтальном направлении. Независимо от этого лево-право будет вращаться вокруг оси Z, а вверх-вниз-вокруг горизонтальной оси. Таким образом, расчет 3D-точки должен учитывать это-я полагаю, что это в данный момент не работает хорошо, и необходим совершенно другой подход ...

Knight school

Спасибо,
Если я правильно вас понял, вычисления класса 3Dpoint вызывают эти ошибки, а не орбитальное движение мыши. Если это правильно, то как бы лучше всего начать смотреть на Формулу?

Спасибо за вашу помощь.

Ralf Meier

Это будет совершенно новый/другой расчет для 3D-точки.
Я посмотрю, что могу для вас сделать ...

Ralf Meier

До сих пор у меня не было кода ... но есть предложение :
Вместо использования RotateX, RotateY и RotateZ из point3d и выполняет обратное преобразование-класса вы/мы должны создать другой поворот-методы - RotateVertical и RotateHorizontal.
Каждый из этих методов будет/должен работать с фактическим созвездием внутри Point3D - поэтому они должны изменять углы в зависимости от текущей ситуации ...
Я думаю, что это был бы лучший подход ...

Рейтинг:
0

Knight school

Sorry for not being clear.
You are correct the rotation point is not stated but it will be (until I can workout the shift relative to the different rotation point i.e. distance from drawn zero point & mouse rotation) the drawing zero point. The drawing zero point is positioned by Matrix Translate (& Scale is done also at this time).

<pre lang="vb">Dim translateMatrix1 As New Matrix
                translateMatrix1.Scale(Scale_Path, Scale_Path, MatrixOrder.Append)
                translateMatrix1.Translate(Offset_X_Path, Offset_Y_Path, MatrixOrder.Append)
                Temp_Path.Transform(translateMatrix1)


Мне нужно иметь орбитальный крен относительно движения/жеста мыши.


Richard Deeming

Если вы хотите ответить на комментарий, используйте кнопку "ответить" рядом с этим комментарием.

НЕ опубликуйте свой ответ как "решение"!