Graphics Graphics operations are performed using a number of Classes: • The ‘Graphics’ class supplies a surface to draw on
(think of it as the paper). It is typically created by invoking the CreateGraphics method on a suitable object (e.g. a Form or a PictureBox)
• The ‘Pen’ class defines colours and styles for drawing lines
• The ‘Brush’ class defines colours and styles for filling areas
• The Graphics class has methods for drawing different primitive shapes onto itself, using Pens, Brushes, and other objects
A simple example: drawing is done onto a PictureBox
Public Class Form1 Private Sub btnDraw_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDraw.Click Dim paper As Graphics paper = PictureBox1.CreateGraphics() Dim myPen As Pen = New Pen(Color.Black) paper.DrawRectangle(myPen, 10, 10, 100, 50) ' x, y, wid, hgt paper.DrawRectangle(myPen, 10, 75, 100, 100) End Sub
End Class
The Co-ordinate System: • All drawing operations have their coordinates
specified using two numbers (x and y, or horizontal and vertical)
• X is measured, in pixels, from the left of the parent object’s client area
• Y is measured from the top of the parent object’s client area
• Note the picture box's nested co-ordinate system!
Example illustrating the use of the coordinate system: the bouncing radio button!
Public Class Form1 Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick Static dx As Integer = 5 Static dy As Integer = 5 Dim newLoc As Point newLoc = RadioButton1.Location newLoc.X += dx newLoc.Y += dy ' subtract 20 from width to approximate right-hand-side of client area and also to account for ' the radiobutton's own width If (newLoc.X <= 0 Or newLoc.X >= Me.Width - 20) Then dx = -dx End If ' subtract 40 from height for the same reason as above (client area vs. form size) If (newLoc.Y <= 0 Or newLoc.Y >= Me.Height - 40) Then dy = -dy End If RadioButton1.Location = newLoc End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Timer1.Interval = 10 Timer1.Enabled = True End Sub End Class
Drawing Methods of the Graphics class: • DrawRectangle(pen, x, y, wid, hgt) • DrawLine(pen, x1, y1, x2, y2) • DrawEllipse(pen, x, y, wid, hgt) • FillRectangle(brush, x, y, wid, hgt) • DrawImage(image, x, y) • and many others…
Public Class Form1 Private Sub btnDraw_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDraw.Click doDraw() End Sub Private Sub doDraw() Dim paper As Graphics paper = PictureBox1.CreateGraphics() Dim myPen As Pen = New Pen(Color.Black) Dim myPic As New Bitmap(Application.StartupPath & "\carPoster.jpg") Dim myBrush As SolidBrush = New SolidBrush(Color.Firebrick) paper.DrawLine(myPen, 10, 10, 110, 60) paper.DrawEllipse(myPen, 10, 80, 100, 50) paper.FillEllipse(myBrush, 10, 150, 100, 50) paper.DrawImage(myPic, 130, 10, 210, 298) End Sub Private Sub Form1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint ' handle the Paint event, which indicates the form has been resized or uncovered Timer1.Interval = 20 Timer1.Enabled = True End Sub Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick Timer1.Enabled = False doDraw() End Sub End Class
Example: a histogram-drawing application
Public Class Form1 Private Sub btnEnd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEnd.Click End End Sub Private Sub btnAddBar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddBar.Click Dim newValue As Integer newValue = CInt(InputBox("Enter Value for new Bar", "Histogram App.", "1")) If (newValue > 0) Then ListBox1.Items.Add(newValue) drawHistogram() End If End Sub Private Sub drawHistogram() Dim g As Graphics g = PictureBox1.CreateGraphics() g.Clear(Color.White) ' step 1: find maximum value, for y axis scaling ' (tallest bar will take up full height of picture box) Dim i, max, barWidth As Integer max = 0 For i = 0 To ListBox1.Items.Count() - 1 If CInt(ListBox1.Items.Item(i)) > max Then max = CInt(ListBox1.Items.Item(i)) End If Next
' step 2: calculate width of each bar as a fraction of picture box barWidth = PictureBox1.Width / ListBox1.Items.Count() ' step 3: draw the bars, each with a random brush colour
' (also draw text indicating the bar values) Dim startX, barHgt, val, halfBarWidth, barTop As Integer Dim stringSize As SizeF Dim halfStringWidth As Integer Dim b As SolidBrush Dim c As Color Dim alpha, red, green, blue As Integer Dim f As Font = New Font("Times", 10) alpha = 255 halfBarWidth = barWidth / 2 For i = 0 To ListBox1.Items.Count() - 1 red = CInt(Rnd() * 255) green = CInt(Rnd() * 255) blue = CInt(Rnd() * 255) c = Color.FromArgb(alpha, red, green, blue) b = New SolidBrush(c) val = CInt(ListBox1.Items.Item(i)) startX = i * barWidth barHgt = (val * PictureBox1.Height) / max barTop = PictureBox1.Height - barHgt g.FillRectangle(b, startX, barTop, barWidth, barHgt) stringSize = g.MeasureString(CStr(val), f) halfStringWidth = stringSize.ToSize().Width / 2 g.DrawString(CStr(val), f, Brushes.Black, startX + halfBarWidth - halfStringWidth, barTop) Next End Sub End Class
Example: an ‘Asteroids’ game ( http://www.codeguru.com/vb/gen/vb_graphics/animation/article.php/c15317__1/ )
FrmAsteroids Imports System.Math Public Class FrmAsteroids Private First As Boolean Private Loop_1 As Long Private Loop_2 As Long Private Loop_3 As Long Private MyImage As Bitmap Private MyGraphic As Graphics Private Mypen As Pen Private DelPen As Pen Private WithEvents Timer1 As Windows.Forms.Timer Private Sub Asteroids_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown Select Case e.KeyCode Case 39 Turn = Tri_Stat.pos Case 37 Turn = Tri_Stat.neg Case 38 Accelerate = True Case 40 Warp = True Case 32 Shoot = True End Select '32 = Space '38 = up '40 = Down '39 = right '37 = left End Sub Private Sub Asteroids_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp Select Case e.KeyCode Case 39, 37 Turn = Tri_Stat.Zero Case 38 Accelerate = False End Select End Sub Private Sub Asteroids_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Mypen = New Pen(Color.Black, 1) DelPen = New Pen(Color.White, 1) Timer1 = New Windows.Forms.Timer MyImage = New Bitmap(PictureBox1.Width, PictureBox1.Height) MyGraphic = Graphics.FromImage(MyImage) First = True Set_Default() Timer1.Interval = 15 Timer1.Enabled = True Dead = False Init_const()
Init_Ast() End Sub Private Sub Check_Input() Dim Tmp As Cords_Type Select Case Turn Case Tri_Stat.pos Ship.Angle = Ship.Angle + 1.5 If Ship.Angle >= 360 Then Ship.Angle = Ship.Angle -‐ 360 Case Tri_Stat.neg Ship.Angle = Ship.Angle -‐ 1.5 If Ship.Angle < 0 Then Ship.Angle = Ship.Angle + 360 End Select If Accelerate Then With Ship Tmp.X = .Speed * Sin(.Direction * PI / 180) Tmp.Y = .Speed * Cos(.Direction * PI / 180) Tmp.X = Tmp.X + (0.02 * Sin(.Angle * PI / 180)) Tmp.Y = Tmp.Y + (0.02 * Cos(.Angle * PI / 180)) If Tmp.Y = 0 Then .Direction = (Atan(Tmp.Y / Tmp.X)) / PI * 180 .Speed = Tmp.X / (Cos(.Direction * PI / 180)) Else .Direction = (Atan(Tmp.X / Tmp.Y)) / PI * 180 .Speed = Tmp.Y / (Cos(.Direction * PI / 180)) End If If .Speed > 2 Then .Speed = 2 If .Speed < -‐2 Then .Speed = -‐2 End With End If If Warp Then With Ship .Location.X = Rnd() * 640 .Location.Y = Rnd() * 480 .Angle = Rnd() * 360 .Direction = Rnd() * 360 End With Warp = False End If If Shoot Then For Loop_1 = 0 To 19 If Shots(Loop_1).TTL = 0 Then Shots(Loop_1).Direction = Ship.Angle Shots(Loop_1).Location.X = Ship.Location.X Shots(Loop_1).Location.Y = Ship.Location.Y Shots(Loop_1).Speed = 3 Shots(Loop_1).TTL = 320 Shots(Loop_1).First = True Exit For End If Next Loop_1 Shoot = False End If End Sub Private Sub Do_Score() MyGraphic.DrawString("Score: " & Score, Me.Font, Brushes.Black, 1, 1) End Sub
Private Sub Check_Space() Dim Diff As Cords_Type Dim Direction As Single Dim Distance As Single For Loop_1 = 0 To 39 With Asteroids(Loop_1) If .Vis Then Diff.X = .Location.X -‐ 320 Diff.Y = .Location.Y -‐ 240 If Diff.Y = 0 Then Direction = (Atan(Diff.Y / Diff.X)) / PI * 180 Distance = Diff.X / (Cos(Direction * PI / 180)) Else Direction = (Atan(Diff.X / Diff.Y)) / PI * 180 Distance = Diff.Y / (Cos(Direction * PI / 180)) End If If Abs(Distance) < (50 / .Size) + 40 Then Exit Sub End If End If End With Next Loop_1 Dead = False First = True With Ship .Speed = 0 .Angle = 180 .Direction = 0 .Location.X = 320 .Location.Y = 240 End With Put_Ship() First = False End Sub Private Sub Check_Stage() For Loop_1 = 0 To 39 With Asteroids(Loop_1) If .Vis Then Exit Sub End If End With Next Loop_1 Init_Ast() With Ship .Location.X = 320 .Location.Y = 240 .Speed = 0 End With End Sub Private Sub Hit_Aster() Dim Diff As Cords_Type Dim Direction As Single Dim Distance As Single For Loop_1 = 0 To 39 With Asteroids(Loop_1) If .Vis Then For Loop_2 = 0 To 19 If Shots(Loop_2).TTL > 0 Then Diff.X = .Location.X -‐ Shots(Loop_2).Location.X Diff.Y = .Location.Y -‐ Shots(Loop_2).Location.Y
If Diff.Y = 0 Then Direction = (Atan(Diff.Y / Diff.X)) / PI * 180 Distance = Diff.X / (Cos(Direction * PI / 180)) Else Direction = (Atan(Diff.X / Diff.Y)) / PI * 180 Distance = Diff.Y / (Cos(Direction * PI / 180)) End If If Abs(Distance) < 50 / .Size Then Score = Score + 5 * .Size MyGraphic.DrawRectangle(DelPen, Shots(Loop_2).Location.X -‐ 1, Shots(Loop_2).Location.Y -‐ 1, 2, 2) Shots(Loop_2).TTL = 0 Shots(Loop_2).First = True .Size = .Size * 2 If .Size <= 8 Then For Loop_3 = 0 To 39 If Not (Asteroids(Loop_3).Vis) Then Asteroids(Loop_3) = Asteroids(Loop_1) Asteroids(Loop_1).Direction = Rnd() * 360 Asteroids(Loop_1).Rotate = Rnd() * .Size -‐ (.Size / 2) Asteroids(Loop_3).Direction = Asteroids(Loop_1).Direction + 180 Asteroids(Loop_3).Rotate = Rnd() * .Size -‐ (.Size / 2) Asteroids(Loop_3).Vis = True Asteroids(Loop_3).First = True Exit For End If Next Loop_3 End If End If End If Next Loop_2 If Not Dead Then Diff.X = .Location.X -‐ Ship.Location.X Diff.Y = .Location.Y -‐ Ship.Location.Y If Diff.Y = 0 Then Direction = (Atan(Diff.Y / Diff.X)) / PI * 180 Distance = Diff.X / (Cos(Direction * PI / 180)) Else Direction = (Atan(Diff.X / Diff.Y)) / PI * 180 Distance = Diff.Y / (Cos(Direction * PI / 180)) End If If Abs(Distance) < (50 / .Size) + 6 Then Dead = True End If End If End If End With Next Loop_1 End Sub Private Sub Put_Aster() Dim Cord(9) As Cords_Type For Loop_1 = 0 To 39 With Asteroids(Loop_1) If .Vis Then
.Location.X = .Location.X + .Speed * Sin(.Direction * PI / 180) .Location.Y = .Location.Y -‐ .Speed * Cos(.Direction * PI / 180) .Angle = .Angle + .Rotate If .Location.X >= 640 Then .Location.X = .Location.X -‐ 640 If .Location.X < 0 Then .Location.X = .Location.X + 640 If .Location.Y >= 480 Then .Location.Y = .Location.Y -‐ 480 If .Location.Y < 0 Then .Location.Y = .Location.Y + 480 If .Angle >= 360 Then .Angle = .Angle -‐ 360 If .Angle < 0 Then .Angle = .Angle + 360 For Loop_2 = 0 To 9 Cord(Loop_2).X = .Location.X -‐ (.Len_Const(Loop_2) * Sin(.Ang_Const(Loop_2) + .Angle * PI / 180)) / .Size Cord(Loop_2).Y = .Location.Y + (.Len_Const(Loop_2) * Cos(.Ang_Const(Loop_2) + .Angle * PI / 180)) / .Size Next Loop_2 If .Size > 8 Then .Vis = False Else For Loop_2 = 0 To 8 MyGraphic.DrawLine(Mypen, Cord(Loop_2).X, Cord(Loop_2).Y, Cord(Loop_2 + 1).X, Cord(Loop_2 + 1).Y) Next Loop_2 MyGraphic.DrawLine(Mypen, Cord(9).X, Cord(9).Y, Cord(0).X, Cord(0).Y) .First = False End If End If End With Next Loop_1 End Sub Private Sub Put_Shots() 'MyGraphic = Graphics.FromImage(MyImage) For Loop_1 = 0 To 19 With Shots(Loop_1) If .TTL > 0 Then .Location.X = .Location.X -‐ .Speed * Sin(.Direction * PI / 180) .Location.Y = .Location.Y + .Speed * Cos(.Direction * PI / 180) If .Location.X >= 640 Then .Location.X = .Location.X -‐ 640 If .Location.X < 0 Then .Location.X = .Location.X + 640 If .Location.Y >= 480 Then .Location.Y = .Location.Y -‐ 480 If .Location.Y < 0 Then .Location.Y = .Location.Y + 480 MyGraphic.DrawRectangle(Mypen, .Location.X -‐ 1, .Location.Y -‐ 1, 2, 2) .TTL = .TTL -‐ .Speed .First = False End If If .TTL <= 0 And Not .First Then MyGraphic.DrawRectangle(DelPen, .Location.X -‐ 1, .Location.Y -‐ 1, 2, 2) .TTL = 0 .First = True
End If End With Next Loop_1 End Sub Private Sub Put_Ship() Dim Cord(2) As Cords_Type With Ship .Location.X = .Location.X -‐ .Speed * Sin(.Direction * PI / 180) .Location.Y = .Location.Y + .Speed * Cos(.Direction * PI / 180) If .Location.X >= 640 Then .Location.X = .Location.X -‐ 640 If .Location.X < 0 Then .Location.X = .Location.X + 640 If .Location.Y >= 480 Then .Location.Y = .Location.Y -‐ 480 If .Location.Y < 0 Then .Location.Y = .Location.Y + 480 For Loop_1 = 0 To 2 Cord(Loop_1).X = .Location.X -‐ (.Len_Const(Loop_1) * Sin(.Ang_Const(Loop_1) + .Angle * PI / 180)) Cord(Loop_1).Y = .Location.Y + (.Len_Const(Loop_1) * Cos(.Ang_Const(Loop_1) + .Angle * PI / 180)) Next Loop_1 End With MyGraphic.DrawLine(Mypen, Cord(0).X, Cord(0).Y, Cord(1).X, Cord(1).Y) MyGraphic.DrawLine(Mypen, Cord(1).X, Cord(1).Y, Cord(2).X, Cord(2).Y) MyGraphic.DrawLine(Mypen, Cord(2).X, Cord(2).Y, Cord(0).X, Cord(0).Y) End Sub Private Sub Timer1_Tick1(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick MyGraphic.Clear(Color.White) If Not Dead Then Check_Input() Put_Ship() Else Check_Space() End If Put_Shots() Put_Aster() Hit_Aster() Check_Stage() Do_Score() PictureBox1.Image = MyImage End Sub End Class
Functions.vb Imports System.Math Module Functions Public Const PI = 3.14159265 Public Enum Tri_Stat neg = -‐1 Zero = 0
pos = 1 End Enum Public Structure Cords_Type Public X As Single Public Y As Single End Structure Public Structure Ship_Type Public Speed As Single Public Direction As Single Public Angle As Single Public Location As Cords_Type Public Cords() As Cords_Type Public Ang_Const() As Single Public Len_Const() As Single End Structure Public Structure Blocks_Type Public Speed As Single Public Cords() As Cords_Type Public Ang_Const() As Single Public Len_Const() As Single Public Size As Byte Public Location As Cords_Type Public First As Boolean Public Vis As Boolean Public Direction As Single Public Rotate As Single Public Angle As Single End Structure Public Structure Shot_Type Public Speed As Single Public Direction As Single Public Location As Cords_Type Public TTL As Single Public First As Boolean End Structure Public Ship As Ship_Type Public Asteroids(39) As Blocks_Type Public Def_Ast As Blocks_Type Public Shots(19) As Shot_Type Public Turn As Tri_Stat Public Accelerate As Boolean Public Warp As Boolean Public Shoot As Boolean Public Dead As Boolean Public Score As Decimal Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination() As Cords_Type, ByVal Source() As Cords_Type, ByVal Length As Long) Private Loop_1 As Long Private Loop_2 As Long Public Sub Init_const() With Ship For Loop_1 = 0 To 2
.Ang_Const(Loop_1) = Atan((.Cords(Loop_1 + 1).X -‐ .Cords(0).X) / (.Cords(Loop_1 + 1).Y -‐ .Cords(0).Y)) .Len_Const(Loop_1) = (.Cords(0).Y -‐ .Cords(Loop_1 + 1).Y) / Cos(.Ang_Const(Loop_1)) Next Loop_1 End With With Def_Ast For Loop_1 = 0 To 9 .Ang_Const(Loop_1) = Atan((.Cords(Loop_1 + 1).X -‐ .Cords(0).X) / (.Cords(Loop_1 + 1).Y -‐ .Cords(0).Y)) .Len_Const(Loop_1) = (.Cords(0).Y -‐ .Cords(Loop_1 + 1).Y) / Cos(.Ang_Const(Loop_1)) Next Loop_1 End With Score = 0 End Sub Public Sub Init_Ast() Asteroids(0) = Def_Ast Asteroids(1) = Def_Ast Asteroids(2) = Def_Ast Asteroids(3) = Def_Ast With Asteroids(0) .Direction = Rnd() * 360 .Location.X = Rnd() * 80 .Location.Y = Rnd() * 80 .Angle = Rnd() * 360 .Rotate = Rnd() * 2 -‐ 1 End With With Asteroids(1) .Direction = Rnd() * 360 .Location.X = Rnd() * 80 + 560 .Location.Y = Rnd() * 80 .Angle = Rnd() * 360 .Rotate = Rnd() * 2 -‐ 1 End With With Asteroids(2) .Direction = Rnd() * 360 .Location.X = Rnd() * 80 + 560 .Location.Y = Rnd() * 80 + 400 .Angle = Rnd() * 360 .Rotate = Rnd() * 2 -‐ 1 End With With Asteroids(3) .Direction = Rnd() * 360 .Location.X = Rnd() * 80 .Location.Y = Rnd() * 80 + 400 .Angle = Rnd() * 360 .Rotate = Rnd() * 2 -‐ 1 End With End Sub Public Sub Set_Default() With Ship ReDim .Cords(3) ReDim .Ang_Const(2) ReDim .Len_Const(2) .Speed = 0 .Angle = 180 .Direction = 0
.Cords(0).X = 7 .Cords(0).Y = 10 .Cords(1).X = 7 .Cords(1).Y = 0 .Cords(2).X = 0 .Cords(2).Y = 20 .Cords(3).X = 14 .Cords(3).Y = 20 .Location.X = 320 .Location.Y = 240 End With With Def_Ast ReDim .Cords(10) ReDim .Ang_Const(9) ReDim .Len_Const(9) .Speed = 0.3 .Size = 1 .Cords(0).X = 50 .Cords(0).Y = 50 .Cords(1).X = 52 .Cords(1).Y = 0 .Cords(2).X = 73 .Cords(2).Y = 9 .Cords(3).X = 65 .Cords(3).Y = 20 .Cords(4).X = 95 .Cords(4).Y = 45 .Cords(5).X = 75 .Cords(5).Y = 80 .Cords(6).X = 50 .Cords(6).Y = 100 .Cords(7).X = 25 .Cords(7).Y = 75 .Cords(8).X = 0 .Cords(8).Y = 70 .Cords(9).X = 5 .Cords(9).Y = 30 .Cords(10).X = 25 .Cords(10).Y = 10 .Vis = True .First = True .Size = 1 End With End Sub End Module