Ruotare un quadrato ed un rettangolo in una DrawingArea

Da Gambas-it.org - Wikipedia.
Vai alla navigazione Vai alla ricerca

Rotazione in 2D

Mostriamo due analoghi possibili codici [nota 1] per far ruotare su se stessi, ossia intorno al proprio asse centrale, un Quadrato ed un Rettangolo disegnati su una DrawingArea. [nota 2]
In entrambi gli esempi la rotazione del Quadrato e del Rettangolo viene effettuata premendo i tasti freccia in su e freccia in giù della tastiera oppure ruotando la rotellina del mouse.

Ruotare un Quadrato sul proprio asse centrale

Per ottenere la rotazione sul proprio asse centrale di un quadrato disegnato in una DrawingArea è possibile adottare il seguente codice:

' Il fulcro del Metodo ".Translate()" agisce da vortice, da centro di rotazione intorno a una ipotetica circonferenza

Private da As DrawingArea
Private Const LATO As Short = 200   ' Memorizza il lato del quadrato.
Private fAngolo As Float            ' Memorizza i gradi dell'angolo.
Private c As Integer[] = [Color.Blue, Color.Green, Color.Yellow, Color.Red]
Private f As Float[] = [0, 0.34, 0.67, 1]


Public Sub Form_Open()  
 
 With Me
   .W = LATO * 3
   .H = .W * 0.6
   .Center
   .Arrangement = Arrange.Fill
 End With  

 With da  = New DrawingArea(Me) As "DrawingArea1"
   .Background = Color.White
 End With
 
End
 

Public Sub DrawingArea1_Draw()
 
 With Paint
   .Brush = .LinearGradient(0, 20, 15, 0, c, f)
' Imposta il fulcro/punto della rotazione - ad esempio - al centro dell'area di disegno:
   .Translate(da.W / 2, da.H / 2)
   .Rotate(Rad(fAngolo))   
   .LineWidth = 2.0
' Disegna il quadrato.
' Poiché l'angolo ruota su se stesso, si dà alle coordinate del quadrato la metà della dimensione del suo lato.
' Non si devono modificare i valori né i segni dei quattro argomenti qui riportati.
   .Rectangle(LATO / 2, LATO / 2, -LATO, -LATO)
' oppure così:
'  .Rectangle(-LATO / 2, -LATO / 2, LATO, LATO)
   .Stroke 
 End With     
    
End
 

Public Sub Form_KeyPress()
  
 Select Case Key.Code
   Case Key.Up
     fAngolo += 1
     If fAngolo = 361 Then fAngolo = 1
   Case Key.Down
     fAngolo -= 1
     If fAngolo = -361 Then fAngolo = -1
 End Select
 
 da.Refresh
  
End


Public Sub Form_MouseWheel()

 fAngolo += Mouse.Delta
 Select Case fAngolo
   Case 361
     fAngolo = 1
   Case -361
     fAngolo = -1
 End Select

 da.Refresh

End


Public Sub Form_Resize()   ' Con questo evento se cambia la dimensione del Form, il disegno non va perso.
  
 da.Refresh
  
End

oppure quest'altro codice:

Private DrawingArea1 As DrawingArea
Private Timer1 As Timer
Private angleZ As Float    ' Angolo di rotazione intorno all’asse Z


Public Sub Form_Open()

 With Me
   .Title = "Quadrato che ruota intorno all’asse Z"
   .Move(0, 0, Screen.AvailableWidth, Screen.AvailableHeight)
   .Arrangement = Arrange.Fill
 End With

 With DrawingArea1 = New DrawingArea(Me) As "DrawingArea1"
   .Background = Color.White
 End With

 With Timer1 = New Timer As "Timer1"
   .Delay = 30
   .Start
 End With

End


Public Sub Timer1_Timer()

 angleZ += 2                                 ' Incrementa l’angolo ogni frame
 If angleZ >= 360 Then angleZ = angleZ - 360
 DrawingArea1.Refresh

End


Public Sub DrawingArea1_Draw()

' Centra il disegno del quadrato nella "DrawingArea":
 Dim cx As Float = DrawingArea1.Width / 2
 Dim cy As Float = DrawingArea1.Height / 2

 Dim size As Float = 1.0              ' Metà lato del quadrato
 Dim d As Float = 5.0                 ' Distanza "camera" (l'osservatore)
 Dim az As Float = Rad(angleZ)        ' Angolo in radianti

' === Vertici 3D del quadrato ===
' Ordinati in senso orario:
 Dim square As New Float[][4]
 square[0] = [-size, size, 0.0]   ' In alto a sinistra
 square[1] = [size, size, 0.0]   ' In alto a destra
 square[2] = [size, -size, 0.0]   ' In basso a destra
 square[3] = [-size, -size, 0.0]   ' In basso a sinistra

' === Ruota e proietta i 4 vertici ===
 Dim coords As Float[] = []
 For i As Integer = 0 To 3
   Dim p As Float[] = RotateZ(square[i], az)
   Dim pt As PointF = Project3D(p, d, cx, cy)
   coords.Add(pt.X)
   coords.Add(pt.Y)
 Next

' === Disegna il quadrato ruotato ===
 Paint.Brush = Paint.Color(Color.RGB(100, 200, 255))
 Paint.Polygon(coords)
 Paint.Fill()

' Bordo del quadrato:
 Paint.LineWidth = 1
 Paint.Brush = Paint.Color(Color.Black)
 Paint.Polygon(coords)
 Paint.Stroke()

End


' === Rotazione attorno all'asse Z ===
Private Function RotateZ(v As Float[], angle As Float) As Float[]

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Rotazione nel piano XY:
 Dim xr As Float = x * Cos(angle) - y * Sin(angle)
 Dim yr As Float = x * Sin(angle) + y * Cos(angle)

 Return [xr, yr, z]

End


' === Proiezione 3D → 2D con prospettiva ===
Private Function Project3D(v As Float[], d As Float, cx As Float, cy As Float) As PointF

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Scala in base alla profondità:
 Dim factor As Float = d / (z + d + 0.001)
 Dim x2D As Float = x * factor
 Dim y2D As Float = y * factor

 Return New PointF(cx + x2D * 100, cy - y2D * 100)

End


Ruotare un Rettangolo sul proprio asse centrale

Per ottenere la rotazione sul proprio asse centrale di un Rettangolo disegnato in una DrawingArea è possibile adottare il seguente codice identico al precedente:

' Il fulcro del Metodo ".Translate()" agisce da vortice, da centro di rotazione intorno a una ipotetica circonferenza

Private da As DrawingArea
Private Const ORIZZONTALE As Short = 200   ' Memorizza la dimensione di ciascun lato orizzontale.
Private Const VERTICALE As Short = 100     ' Memorizza la dimensione di ciascun lato verticale.
Private fAngolo As Float            ' Memorizza i gradi dell'angolo.
Private c As Integer[] = [Color.Blue, Color.Green, Color.Yellow, Color.Red]
Private f As Float[] = [0, 0.34, 0.67, 1]


Public Sub Form_Open()  
 
 With Me
   .W = Max(ORIZZONTALE, VERTICALE) * 4
   .H = .W / 2
   .Center
   .Arrangement = Arrange.Fill
 End With  

 With da  = New DrawingArea(Me) As "DrawingArea1"
   .Background = Color.White
 End With
 
End
 

Public Sub DrawingArea1_Draw()

 With Paint
   .Brush = .LinearGradient(0, 20, 15, 0, c, f)
' Imposta il fulcro/punto della rotazione - ad esempio -al centro dell'area di disegno:
   .Translate(da.W / 2, da.H / 2)
   .Rotate(Rad(fAngolo))   
   .LineWidth = 2.0
' Disegna il rettangolo.
' Siccome l'angolo ruota su se stesso, si dà alle coordinate del rettangolo la metà della dimensione di ciascun suo lato:
' Non si devono modificare i valori né i segni dei quattro argomenti qui riportati.
   .Rectangle(ORIZZONTALE / 2, VERTICALE / 2, -ORIZZONTALE, -VERTICALE)
' oppure così:
'  .Rectangle(-ORIZZONTALE / 2, -VERTICALE / 2, ORIZZONTALE, VERTICALE)
   .Stroke 
 End With     
    
End
 

Public Sub Form_KeyPress()
  
 Select Case Key.Code
   Case Key.Up
     fAngolo += 1
     If fAngolo = 361 Then fAngolo = 1
   Case Key.Down
     fAngolo -= 1
     If fAngolo = -361 Then fAngolo = -1
 End Select
 
 da.Refresh
  
End


Public Sub Form_MouseWheel()

 fAngolo += Mouse.Delta
 Select Case fAngolo
   Case 361
     fAngolo = 1
   Case -361
     fAngolo = -1
 End Select

 da.Refresh

End


Public Sub Form_Resize()   ' Con questo evento se cambia la dimensione del Form, il disegno non va perso.
  
 da.Refresh
  
End


Rotazione in 3D

Rotazione di un quadrato intorno al suo asse Y

Private DrawingArea1 As DrawingArea
Private Timer1 As Timer
Private angleY As Float   ' Angolo di rotazione intorno all’asse Y


Public Sub Form_Open()
 
 With Me
   .Title = "Quadrato che ruota intorno all’asse Y"
   .Move(0, 0, Screen.AvailableWidth, Screen.AvailableHeight)
   .Arrangement = Arrange.Fill
 End With
 
 With DrawingArea1 = New DrawingArea(Me) As "DrawingArea1"
   .Background = Color.White
 End With

 With Timer1 = New Timer As "Timer1"
   .Delay = 30
   .Start
 End With

End


Public Sub Timer1_Timer()

 angleY += 2             ' Incrementa l’angolo ogni frame
 If angleY >= 360 Then angleY = angleY - 360
 DrawingArea1.Refresh    ' Richiede il ridisegno

End


Public Sub DrawingArea1_Draw()

' Centra il disegno del quadrato nella "DrawingArea":
 Dim cx As Float = DrawingArea1.Width / 2
 Dim cy As Float = DrawingArea1.Height / 2

 Dim size As Float = 1.0              ' Metà lato del quadrato
 Dim d As Float = 5.0                 ' Distanza "camera" (l'osservatore)
 Dim ay As Float = Rad(angleY)        ' Angolo in radianti

' === Vertici 3D del quadrato ===
' Ordinati in senso orario:
 Dim square As New Float[][4]
 square[0] = [-size,  size, 0.0]   ' In alto a sinistra
 square[1] = [ size,  size, 0.0]   ' In alto a destra
 square[2] = [ size, -size, 0.0]   ' In basso a destra
 square[3] = [-size, -size, 0.0]   ' In basso a sinistra

' === Ruota e proietta i 4 vertici ===
 Dim coords As Float[] = []
 For i As Integer = 0 To 3
   Dim p As Float[] = RotateY(square[i], ay)
   Dim pt As PointF = Project3D(p, d, cx, cy)
   coords.Add(pt.X)
   coords.Add(pt.Y)
 Next

' === Disegna il quadrato ruotato ===
 Paint.Brush = Paint.Color(Color.RGB(100, 200, 255))
 Paint.Polygon(coords)
 Paint.Fill()

' Bordo del quadrato:
 Paint.LineWidth = 1
 Paint.Brush = Paint.Color(Color.Black)
 Paint.Polygon(coords)
 Paint.Stroke()

End


' === Rotazione attorno all'asse Y ===
Private Function RotateY(v As Float[], angle As Float) As Float[]

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Rotazione nel piano XZ:
 Dim xr As Float = x * Cos(angle) + z * Sin(angle)
 Dim zr As Float = -x * Sin(angle) + z * Cos(angle)

 Return [xr, y, zr]

End


' === Proiezione 3D → 2D con prospettiva ===
Private Function Project3D(v As Float[], d As Float, cx As Float, cy As Float) As PointF

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Scala in base alla profondità:
 Dim factor As Float = d / (z + d + 0.001)
 Dim x2D As Float = x * factor
 Dim y2D As Float = y * factor

 Return New PointF(cx + x2D * 100, cy - y2D * 100)

End


Quadrato che ruota intorno al suo asse X

Private DrawingArea1 As DrawingArea
Private Timer1 As Timer
Private angleX As Float   ' Angolo di rotazione intorno all’asse X


Public Sub Form_Open()

 With Me
   .Title = "Quadrato che ruota intorno all’asse X"
   .Move(0, 0, Screen.AvailableWidth, Screen.AvailableHeight)
   .Arrangement = Arrange.Fill
 End With

 With DrawingArea1 = New DrawingArea(Me) As "DrawingArea1"
   .Background = Color.White
 End With

 With Timer1 = New Timer As "Timer1"
   .Delay = 30
   .Start
 End With

End


Public Sub Timer1_Timer()

 angleX += 2                                  ' Incrementa l’angolo ogni frame
 If angleX >= 360 Then angleX = angleX - 360
 DrawingArea1.Refresh

End


Public Sub DrawingArea1_Draw()

' Centra il disegno del quadrato nella "DrawingArea":
 Dim cx As Float = DrawingArea1.Width / 2
 Dim cy As Float = DrawingArea1.Height / 2

 Dim size As Float = 1.0              ' Metà lato del quadrato
 Dim d As Float = 5.0                 ' Distanza "camera" (l'osservatore)
 Dim ax As Float = Rad(angleX)        ' Angolo in radianti

' === Vertici 3D del quadrato ===
' Ordinati in senso orario:
 Dim square As New Float[][4]
 square[0] = [-size,  size, 0.0]      ' In alto a sinistra
 square[1] = [ size,  size, 0.0]      ' In alto a destra
 square[2] = [ size, -size, 0.0]      ' In basso a destra
 square[3] = [-size, -size, 0.0]      ' In basso a sinistra

'  === Ruota e proietta i 4 vertici ===
 Dim coords As Float[] = []
 For i As Integer = 0 To 3
   Dim p As Float[] = RotateX(square[i], ax)
   Dim pt As PointF = Project3D(p, d, cx, cy)
   coords.Add(pt.X)
   coords.Add(pt.Y)
 Next

'  === Disegna il quadrato ruotato ===
 Paint.Brush = Paint.Color(Color.RGB(100, 200, 255))
 Paint.Polygon(coords)
 Paint.Fill()

' Bordo del quadrato:
 Paint.LineWidth = 1
 Paint.Brush = Paint.Color(Color.Black)
 Paint.Polygon(coords)
 Paint.Stroke()

End


' === Rotazione attorno all'asse X ===
Private Function RotateX(v As Float[], angle As Float) As Float[]

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Rotazione nel piano YZ:
 Dim yr As Float = y * Cos(angle) - z * Sin(angle)
 Dim zr As Float = y * Sin(angle) + z * Cos(angle)

 Return [x, yr, zr]

End


' === Proiezione 3D → 2D con prospettiva ===
Private Function Project3D(v As Float[], d As Float, cx As Float, cy As Float) As PointF

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Scala in base alla profondità:
 Dim factor As Float = d / (z + d + 0.001)
 Dim x2D As Float = x * factor
 Dim y2D As Float = y * factor

 Return New PointF(cx + x2D * 100, cy - y2D * 100)

End


Quadrato che ruota intorno ai suoi assi X, Y e Z

' === Angoli di rotazione per X, Y, Z ===
Private angleX As Float
Private angleY As Float
Private angleZ As Float


Public Sub Form_Open()

 With Me
   .Title = "Quadrato che ruota intorno ai suoi assi X, Y e Z"
   .Move(0, 0, Screen.AvailableWidth, Screen.AvailableHeight)
   .Arrangement = Arrange.Fill
 End With
 
 Timer1.Delay = 16   ' ~60 FPS
 Timer1.Start

End


Public Sub Timer1_Timer()

 angleX += 1.2      ' Incrementa angolo di rotazione attorno all’asse X
 angleY += 2.0      ' Incrementa angolo di rotazione attorno all’asse Y
 angleZ += 0.8      ' Incrementa angolo di rotazione attorno all’asse Z
 DrawingArea1.Refresh

End


Public Sub DrawingArea1_Draw()

' Centro della DrawingArea:
 Dim cx As Float = DrawingArea1.Width / 2
 Dim cy As Float = DrawingArea1.Height / 2

' Parametri del quadrato:
 Dim size As Float = 1.0
 Dim d As Float = 5.0

' Angoli in radianti:
 Dim ax As Float = Rad(angleX)
 Dim ay As Float = Rad(angleY)
 Dim az As Float = Rad(angleZ)

' === Vertici 3D del quadrato inizialmente nel piano XY ===
' Ordinati in senso orario:
 Dim square As New Float[][4]
 square[0] = [-size,  size, 0.0]
 square[1] = [ size,  size, 0.0]
 square[2] = [ size, -size, 0.0]
 square[3] = [-size, -size, 0.0]

' === Array dei punti proiettati 2D ===
 Dim coords As Float[] = []

' === Rotazione 3D e proiezione per ciascun vertice ===
 For i As Integer = 0 To 3
   Dim p As Float[] = square[i]

' Applica rotazioni in ordine: X → Y → Z:
   p = RotateX(p, ax)
   p = RotateY(p, ay)
   p = RotateZ(p, az)

' Proietta 3D → 2D:
   Dim pt As PointF = Project3D(p, d, cx, cy)

' Aggiunge le coordinate 2D all’array da disegnare:
   coords.Add(pt.X)
   coords.Add(pt.Y)
 Next

' === Disegna il quadrato ruotato ===
 Paint.Brush = Paint.Color(Color.RGB(100, 200, 255))
 Paint.Polygon(coords)
 Paint.Fill()

' Bordo nero:
 Paint.LineWidth = 1
 Paint.Brush = Paint.Color(Color.Black)
 Paint.Polygon(coords)
 Paint.Stroke()

End


' === ROTAZIONE ATTORNO ALL’ASSE X ===
Private Function RotateX(v As Float[], angle As Float) As Float[]

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Ruota nel piano YZ:
 Dim yr As Float = y * Cos(angle) - z * Sin(angle)
 Dim zr As Float = y * Sin(angle) + z * Cos(angle)

 Return [x, yr, zr]

End


' === ROTAZIONE ATTORNO ALL’ASSE Y ===
Private Function RotateY(v As Float[], angle As Float) As Float[]

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Ruota nel piano XZ:
 Dim xr As Float = x * Cos(angle) + z * Sin(angle)
 Dim zr As Float = -x * Sin(angle) + z * Cos(angle)

 Return [xr, y, zr]

End


' === ROTAZIONE ATTORNO ALL’ASSE Z ===
Private Function RotateZ(v As Float[], angle As Float) As Float[]

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Ruota nel piano XY:
 Dim xr As Float = x * Cos(angle) - y * Sin(angle)
 Dim yr As Float = x * Sin(angle) + y * Cos(angle)

 Return [xr, yr, z]

End


' === PROIEZIONE 3D → 2D con prospettiva ===
Private Function Project3D(v As Float[], d As Float, cx As Float, cy As Float) As PointF

 Dim x As Float = v[0]
 Dim y As Float = v[1]
 Dim z As Float = v[2]

' Fattore prospettico (più z è vicino alla "camera" (l'osservatore), più grande):
 Dim factor As Float = d / (z + d + 0.001)
 Dim x2D As Float = x * factor
 Dim y2D As Float = y * factor

' Converte in coordinate della DrawingArea:
 Return New PointF(cx + x2D * 100, cy - y2D * 100)

End



Note

[1] I codici, presenti in questa pagina, sono tratti - con modifiche ed integrazioni - da suggerimenti del membro Gianluigi del forum di gambas-it.org e del membro Shell del forum www.gambas-es.org .

[2] Vedere anche queste pagine: