# Rotating Wireframe Cube using VB.NET and GDI+

Today I will show you how to simulate the rotation of a wireframe cube using VB.NET and GDI+. I assume the reader has at least a basic understanding of Visual Basic. See below a video of the wireframe cube in action.

So, what is GDI+? GDI+ is an API that consists of a set of classes that enable applications to draw graphics on video displays and printers. GDI+ is defined in the System.Drawing namespace.

I have created this simulation using Visual Studio Express 2010 which is freely downloadable from Microsoft. However, you can use any IDE for .NET of your choice.

THE FULL SOURCE CODE IS HERE.

## The Code

First of all, create the Point3D class. This class will represent points in 3D space. Below is the code of  for the class. For better organization, save it in a file named Point3D.vb.

```'
' Defines the Point3D class that represents points in 3D space.
' Developed by leonelmachava <[email protected]>
' http://codentronix.com
'
' Copyright (c) 2011 Leonel Machava
'
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
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
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
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 As Integer, ByVal viewHeight As Integer, ByVal fov As Integer, ByVal viewDistance As Integer)
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
```

The methods RotateX(), RotateY(), and RotateZ(), rotate a point, respectively, around X, Y and Z axis. The method Project(), transforms a point from 3D space to 2D space. This is fundamental in order to draw a 3D point in our 2D screens.

You should not bother with the details of the implementation of these methods because they are just based on formulas that can be easily found on the internet.

Next, create a Windows Form, and name it Main. After that, copy the code below to the form.

```'
' Simulation of a Wireframe Cube using GDI+
' Developed by leonelmachava <[email protected]>
' http://codentronix.com
'
' Copyright (c) 2011 Leonel Machava
'
Imports System.Drawing.Graphics
Imports System.Drawing.Pen
Imports System.Drawing.Color
Imports System.Drawing.Brush
Imports System.Drawing.Point

Public Class Main
Protected m_pen As Pen
Protected m_timer As Timer
Protected m_vertices(8) As Point3D
Protected m_faces(6, 4) As Integer
Protected m_angle As Integer

Private Sub Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' Create a GDI+ Pen. This will be used to draw lines.
m_pen = New Pen(Color.Red)

InitCube()

' Create the timer.
m_timer = New Timer()

' Set the timer interval to 33 milliseconds. This will give us 1000/34 ~ 30 frames per second.
m_timer.Interval = 33

' Set the callback for the timer.

' Start the timer.
m_timer.Start()
End Sub

Private Sub InitCube()
' Create an array with 8 points.
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}}
End Sub

Private Sub AnimationLoop()
' Forces the Paint event to be called.
Me.Invalidate()

' Update the variable after each frame.
m_angle += 1
End Sub

Private Sub Main_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim t(8) As Point3D
Dim f(4) As Integer
Dim v As Point3D

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

' Transform all the points and store them on the "t"- array.
For i = 0 To 7
v = m_vertices(i)
t(i) = v.RotateX(m_angle).RotateY(m_angle).RotateZ(Me.m_angle)
t(i) = t(i).Project(Me.ClientSize.Width, Me.ClientSize.Height, 256, 4)
Next

' Draw the wireframe cube. Uses the "m_faces" array to find the vertices that compose each face.
For i = 0 To 5
e.Graphics.DrawLine(m_pen, CInt(t(m_faces(i, 0)).X), CInt(t(m_faces(i, 0)).Y), CInt(t(m_faces(i, 1)).X), CInt(t(m_faces(i, 1)).Y))
e.Graphics.DrawLine(m_pen, CInt(t(m_faces(i, 1)).X), CInt(t(m_faces(i, 1)).Y), CInt(t(m_faces(i, 2)).X), CInt(t(m_faces(i, 2)).Y))
e.Graphics.DrawLine(m_pen, CInt(t(m_faces(i, 2)).X), CInt(t(m_faces(i, 2)).Y), CInt(t(m_faces(i, 3)).X), CInt(t(m_faces(i, 3)).Y))
e.Graphics.DrawLine(m_pen, CInt(t(m_faces(i, 3)).X), CInt(t(m_faces(i, 3)).Y), CInt(t(m_faces(i, 0)).X), CInt(t(m_faces(i, 0)).Y))
Next
End Sub
End Class
```

When the form is loaded, we first create a Pen that is later used to draw the lines of the cube. Next, we call InitCube() to initialize data about the cube. Finally, we create and setup a timer to drive the animation at 30 frames per second.

The AnimationLoop() method handles the timer event, triggering a Paint event each time it is invoked. Main_Paint() rotates and draws the wireframe cube while handling the Paint event.

THE FULL SOURCE CODE IS HERE.

## Conclusion

Today, we have seen how to simulate a rotating wireframe cube. In the next tutorial, I will show you how to adapt this code to simulate a rotating solid cube.

If you liked this article, please consider leaving a comment, or sharing this post using one of the buttons below, or even subscribing to the blog.

1. ‘See Comments at end of code
Option Strict On
Public Class Form1
#Region “Declarations”
Public cube1 As New Cube
Public cube2 As New Cube
Public SelectedCube As Integer = 2
Public Controller1 As New CubeController
Dim CubePoller As New Timer
#End Region
#Region “Form Events”
cube1.ParentForm = Me : cube2.ParentForm = Me
‘Specify Initial Location of each cube
cube1.Location.X = Me.Width / 2 : cube1.Location.Y = Me.Height / 2 : cube1.Location.Z = 2.5
cube2.Location.X = Me.Width : cube2.Location.Y = Me.Height : cube2.Location.Z = 2.1
‘Setup Each Cube’s Wireframe Color
cube1.WireColor = Color.Red : cube2.WireColor = Color.Blue
‘Select The Startup Cube in the combobox
Select Case SelectedCube
Case 1
ComboBox1.SelectedIndex = 0
Case 2
ComboBox1.SelectedIndex = 1
End Select
‘Progam Cube1′s Input Controller
cube1.Controls.Up = Keys.Up : cube1.Controls.Down = Keys.Down : cube1.Controls.Left = Keys.Left
cube1.Controls.Right = Keys.Right : cube1.Controls.SlideTowards = Keys.Oemplus
cube1.Controls.SlideAway = Keys.OemMinus : cube1.Controls.AngleLeft = Keys.A
cube1.Controls.AngleRight = Keys.D : cube1.Controls.AngleUp = Keys.W
cube1.Controls.AngleDown = Keys.S : cube1.Controls.AngleBack = Keys.Q
cube1.Controls.AngleForward = Keys.E
‘Program cube2′s Input Controller
cube2.Controls.Up = Keys.Up : cube2.Controls.Down = Keys.Down : cube2.Controls.Left = Keys.Left
cube2.Controls.Right = Keys.Right : cube2.Controls.SlideTowards = Keys.Oemplus
cube2.Controls.SlideAway = Keys.OemMinus : cube2.Controls.AngleLeft = Keys.A
cube2.Controls.AngleRight = Keys.D : cube2.Controls.AngleUp = Keys.W
cube2.Controls.AngleDown = Keys.S : cube2.Controls.AngleBack = Keys.Q
cube2.Controls.AngleForward = Keys.E
‘Create a timer that polls for input and changes to cube properties
CubePoller.Interval = 1 : CubePoller.Enabled = True
‘Add a handler for the timer
End Sub
Private Sub Form1_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
‘Clear the form
e.Graphics.Clear(Color.White)
‘Update The Form’s Graphics
cube1.Graphics = e.Graphics
cube2.Graphics = e.Graphics
End Sub
Private Sub CubePoller_Tick(sender As System.Object, e As System.EventArgs)
‘Select a cube based on the selected item in the combobox
Select Case ComboBox1.SelectedIndex
Case 0
‘Syncronize the values in the textboxes to the
‘cube properties
‘Update cube
cube1.Update()
TextBox1.Text = CStr(cube1.Location.X)
TextBox2.Text = CStr(cube1.Location.Y)
TextBox3.Text = CStr(cube1.Location.Z)
TextBox4.Text = CStr(cube1.Xangle)
TextBox5.Text = CStr(CDbl(cube1.Yangle))
TextBox6.Text = CStr(CDbl(cube1.Zangle))
Case 1
‘Syncronize the values in the textboxes to the
‘cube properties
‘Update cube
cube2.Update()
TextBox1.Text = CStr(cube2.Location.X)
TextBox2.Text = CStr(cube2.Location.Y)
TextBox3.Text = CStr(cube2.Location.Z)
TextBox4.Text = CStr(cube2.Xangle)
TextBox5.Text = CStr(CDbl(cube2.Yangle))
TextBox6.Text = CStr(CDbl(cube2.Zangle))
End Select

End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
‘set focus to textbox1
TextBox1.Focus()
End Sub
#End Region
#Region “Structures”
Public Structure Cube
#Region “Cube Properties”
Public Location As Cooridinate3D
Public Dimensions As Sizes
Private t() As Point3D
Private V As Point3D
Public _Graphics As Graphics
Private m_vertices() As Point3D
Private _PenColor As Color
Private _m_pen As Pen
Private _ParentForm As Form
Private IsInitialized As Boolean
Private m_x As Double, m_y As Double, m_z As Double
Public Xangle As Double
Public Yangle As Double
Public Zangle As Double
Public Controls As CubeController
Private m_faces(,) As Integer
Private f() As Integer
Public Property ParentForm As Form
Get
Return _ParentForm
End Get
Set(value As Form)
_ParentForm = value
Location.MainForm = value
Initialize()
End Set
End Property

Public Property Graphics As Graphics
Get
Return _Graphics
End Get
Set(value As Graphics)
Transform()
_Graphics = value
DrawCube()
End Set
End Property
Public ReadOnly Property m_pen As Pen
Get
Return _m_pen
End Get
End Property
Public Property WireColor As Color
Get
Return _PenColor
End Get
Set(value As Color)
_PenColor = value
_m_pen = New Pen(value)
Initialize()
End Set
End Property
#End Region
#Region “Cube Functions”
Public Sub Transform()
Dim tt(8) As Point3D
Dim ff(4) As Integer
Dim vv As Point3D = Nothing
For i = 0 To 7
vv = m_vertices(i)
tt(i) = vv.RotateX(CInt(Xangle)).RotateY(CInt(Yangle)).RotateZ(CInt(Zangle))
tt(i) = CType(tt(i).Project(CDbl(Me.Location.X), CDbl(Me.Location.Y), 100, CDbl(Me.Location.Z)), Point3D)
Next
Me.t = tt
Me.f = ff
Me.V = vv
End Sub
Public Sub RotateX(ByVal Angle As Double)
Xangle = Angle
If IsInitialized = True Then
Animate()
End If
End Sub
Public Sub RotateY(ByVal Angle As Double)
Yangle = Angle
If IsInitialized = True Then
Animate()
End If
End Sub
Public Sub RotateZ(ByVal Angle As Double)
Zangle = Angle
If IsInitialized = True Then
Animate()
End If
End Sub
Public Sub DrawCube()
For i = 0 To 5
Me.Graphics.DrawLine(Me.m_pen, CInt(Me.t(Me.m_faces(i, 0)).X), CInt(Me.t(Me.m_faces(i, 0)).Y), CInt(Me.t(Me.m_faces(i, 1)).X), CInt(Me.t(Me.m_faces(i, 1)).Y))
Me.Graphics.DrawLine(Me.m_pen, CInt(Me.t(Me.m_faces(i, 1)).X), CInt(Me.t(Me.m_faces(i, 1)).Y), CInt(Me.t(Me.m_faces(i, 2)).X), CInt(Me.t(Me.m_faces(i, 2)).Y))
Me.Graphics.DrawLine(Me.m_pen, CInt(Me.t(Me.m_faces(i, 2)).X), CInt(Me.t(Me.m_faces(i, 2)).Y), CInt(Me.t(Me.m_faces(i, 3)).X), CInt(Me.t(Me.m_faces(i, 3)).Y))
Me.Graphics.DrawLine(Me.m_pen, CInt(Me.t(Me.m_faces(i, 3)).X), CInt(Me.t(Me.m_faces(i, 3)).Y), CInt(Me.t(Me.m_faces(i, 0)).X), CInt(Me.t(Me.m_faces(i, 0)).Y))
Next
End Sub
Public Sub Update()
Controls.Initialize()
If Controls.KeyIsDown(CType(Controls.Up, Keys)) Then
Me.Location.Y = CDbl(Me.Location.Y) – Controls.XYMove
End If
If Controls.KeyIsDown(CType(Controls.Down, Keys)) Then
Me.Location.Y = CDbl(Me.Location.Y) + Controls.XYMove
End If
If Controls.KeyIsDown(CType(Controls.Left, Keys)) Then
Me.Location.X = CDbl(Me.Location.X) – Controls.XYMove
End If
If Controls.KeyIsDown(CType(Controls.Right, Keys)) Then
Me.Location.X = CDbl(Me.Location.X) + Controls.XYMove
End If
If Controls.KeyIsDown(CType(Controls.SlideAway, Keys)) Then
Me.Location.Z = CDbl(Me.Location.Z) + Controls.SlideIncrement
End If
If Controls.KeyIsDown(CType(Controls.SlideTowards, Keys)) Then
If CDbl(Me.Location.Z) > 1.1 Then
Me.Location.Z = CDbl(Me.Location.Z) – Controls.SlideIncrement
End If
End If
If Controls.KeyIsDown(CType(Controls.AngleLeft, Keys)) Then
Me.RotateY(Me.Yangle + Controls.RotationIncrement)
End If
If Controls.KeyIsDown(CType(Controls.AngleRight, Keys)) Then
Me.RotateY(Me.Yangle – Controls.RotationIncrement)
End If
If Controls.KeyIsDown(CType(Controls.AngleBack, Keys)) Then
Me.RotateX(Me.Xangle – Controls.RotationIncrement)
End If
If Controls.KeyIsDown(CType(Controls.AngleForward, Keys)) Then
Me.RotateX(Me.Xangle + Controls.RotationIncrement)
End If

If Controls.KeyIsDown(CType(Controls.AngleUp, Keys)) Then
Me.RotateZ(Me.Zangle – Controls.RotationIncrement)
End If
If Controls.KeyIsDown(CType(Controls.AngleDown, Keys)) Then
Me.RotateZ(Me.Zangle + Controls.RotationIncrement)
End If
End Sub
Public Sub Initialize()
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)}
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}}
If WireColor = Nothing Then
WireColor = Color.Red
End If
IsInitialized = True
End Sub
Public Sub Animate()
ParentForm.Invalidate()
End Sub
#End Region
End Structure
Public Structure Cooridinate3D
Dim _X As Double
Dim _Y As Double
Dim _Z As Double
Dim MainForm As Form
Public Property X As Double
Get
Return _X
End Get
Set(value As Double)
_X = CDbl(value)
MainForm.Invalidate()
End Set
End Property
Public Property Y As Double
Get
Return _Y
End Get
Set(value As Double)
_Y = CDbl(value)
MainForm.Invalidate()
End Set
End Property
Public Property Z As Double
Get
Return _Z
End Get
Set(value As Double)
_Z = CDbl(value)
MainForm.Invalidate()
End Set
End Property
End Structure
Public Structure Sizes
Dim X As Double
Dim Y As Double
Dim Z As Double
End Structure
Public Structure Point3D
Private 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
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
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
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 As Double, ByVal viewHeight As Double, ByVal fov As Double, ByVal viewDistance As Double) As Point3D
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 Structure
Public Structure CubeController
Dim Up As Integer
Dim Down As Integer
Dim Left As Integer
Dim Right As Integer
Dim SlideTowards As Integer
Dim SlideAway As Integer
Dim AngleLeft As Integer
Dim AngleRight As Integer
Dim AngleUp As Integer
Dim AngleDown As Integer
Dim AngleBack As Integer
Dim AngleForward As Integer
Public XYMove As Double
Public SlideIncrement As Double
Public RotationIncrement As Double
Public Declare Function GetAsyncKeyState Lib “user32.dll” (ByVal vKey As Int32) As UShort
Public ReadOnly Property KeyIsDown(ByVal Key As Keys) As Boolean
Get
Return CBool(GetAsyncKeyState(Key) And &H8000US)
End Get
End Property
Public Sub Initialize()
XYMove = 2
SlideIncrement = 0.01
RotationIncrement = 0.3
End Sub
End Structure
#End Region

End Class

’3D Wireframe Cube Example Modified By:
‘ Paul Ishak 2011
[email protected]

‘Changes:

‘add the textboxes & combobox in a right side docked panel

‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘———————————————————————————————
‘ Simulation of a Wireframe Cube using GDI+
‘ Developed by leonelmachava
http://codentronix.com

‘ Copyright (c) 2011 Leonel Machava

‘ Permission is hereby granted, free of charge, to any person obtaining a copy of this
‘ software and associated documentation files (the “Software”), to deal in the Software
‘ without restriction, including without limitation the rights to use, copy, modify,
‘ merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
‘ permit persons to whom the Software is furnished to do so, subject to the following
‘ conditions:

‘ The above copyright notice and this permission notice shall be included in all copies
‘ or substantial portions of the Software.

‘ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
‘ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
‘ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
‘ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
‘ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
‘ DEALINGS IN THE SOFTWARE.
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
‘——————————————————————————————–
#End Region

2. Hi lefam,

I found this program of you for a rotating wireframe cube over a web search to this topic. After the content of you had awakened my interest I downloaded the source code you have offered and gave it a try in my visual basic 2010 express program on my computer and it ran right away. Thank you very much. I am really looking forward to explore your code and try to understand everything as a basis for further experiments by myself. I also found your program for a rotating solid cube out of this site and it also ran right away on my pc. Also a very intersting program to me that is waiting for its exploration now. Thank you very much for both source codes and your explanations. I appreciate it a lot.

Bye,
John D. Blue

3. If somebody may tell me the brands of a few dependable vision skincare items it’d be truly
useful.