I have been try to work this problem out for months (If not a year). I can't seem to get the direction of rotation relative the 3D model on screen. I can work out the axis directions (as angles) but can't get the mouse direction to orbit the model(Just like a modelling software like Sketchup).
I have added code which seems to be poor but I have tried so many different ways & it has got so complex. So I have stripped it down to simple bones to start understanding what is really controlling what. I feel like giving up but I think I need to ask for help.
I have tried googling but nothing seems to quite fit or I can apply to the application.
The rotation code was from
Rotating Solid Cube Using VB.NET and GDI+ | codeNtronix[
^]
What I have tried:
Dim Distance_Between As Integer = 0
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
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).
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)
Where I am now after help from Ralf Meier:-
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
With function
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
This works quite well but I seem to a rotation lock when the X & Y are on the same plane. The other problem is some time(not always when upside down) the roll direction is reversed.
Weird & I am not quite sure what is the cause. Any help is welcome as my math are getting very much stretched.
I think I may of found the reason for the errors. the 3D rotation formula works in X rotation but Y & Z are in screen rotation. This is matrices calculation which maybe a bit to far for my math expertices. Maybe need some help finding the reason.
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
Using Solution 2 from Ralf Meier
Here is the problem I am having with changing rotation point by snap point.
Updated after Ralf Meier comments.
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