
Fig 1 - Virtual Clock.
It's that time again! Time to check out a new project written VB.NET and the .NET library. This article describes how to create a virtual clock in VB.NET. The hands of the clock are drawn using GraphicPaths. The UML Design for the Virtual Clock is shown below:

Fig 2 - UML Design of the Virtual Clock reverse engineered using WithClass 2000.
The design is a simple Composite of Hands and a ClockFace. The Hand class serves as a base class for the HourHand, MinuteHand, and SecondHand, each of which have their own GraphicsPath instance stored in the base class. Each time the timer ticks 1 second, the hands are redrawn according to the current time. The sequence of events are shown below:

Fig 3 - One Second Timer Tick Sequence Diagram drawn in WithClass.
The Transform method takes the DateTime(which is actually the current time) and rotates the GraphicsPath of the clock hand by the corresponding Time component for the particular hand. Since the time component is different for each hand, the Transform Method is overridden for each hand. Below is the transform method for the MinuteHand:
Public
Overrides Sub Transform(ByVal d As DateTime)
' turn the datetime minute component into an angle
' remember: 60 seconds in 2 pi (1 circle)
' Also note that we need to add the fractional seconds component to get the most accuracy
Dim minuteTime As Double = CDbl(d.Minute) + CDbl(d.Second / 60.0)
Dim angle As Double = CDbl(minuteTime) / 60.0 * 360 ' 2.0 * Math.PI;
' Rotate the graphics path
Rotate(angle)
End Sub 'Transform The Rotation of the GraphicsPath is performed in the base class, because it's the same for all Hands on the Clock. Rotation of a GraphicsPath is done through a Matrix. The code for the rotation of the hands is shown below:
Public
Sub Rotate(ByVal angle As Double)
' Create a copy of the Graphics Path from the 0 degree path
gp = CType(gpBase.Clone(), GraphicsPath)
' create a new transformation matrix
Dim mTransform As New Matrix
' rotate the matrix around the midpoint of the clock at the angle passed in.
mTransform.RotateAt(CSng(angle), New PointF(midX, midY))
' Transform the Graphics Path (which is actually the clock hand)
gp.Transform(mTransform)
End Sub 'Rotate The other noteworthy code you might find interesting is the drawing of the clock face. The clock face is drawn by the ClockFace class. This class draws the circle for the clock as well as the numbers around the clock and the image inside the clock:
Public
Sub Draw(ByVal g As Graphics)
' draw blank face of clock
DrawClockFace(g)
' Draw clock Image
DrawImage(g)
' draw numbers around the face
DrawNumbers(g)
' draw clock pin
DrawPin(g)
End Sub 'Draw.
The Numbers on the clock are drawn using the sine and cosine functions to determine the numeral positions: Private
Sub DrawNumbers(ByVal g As Graphics)
' initialize the count of the numbers around the clock
Dim count As Integer = 1
' cycle around the circle on the circle on the clock and draw each number in the correct position
Dim a As Double
For a = 0 To (2 * Math.PI) - (2.0 * Math.PI / 12) Step 2.0 * Math.PI / 12
' calculate the x position of the next number
Dim x As Double = (ClockRectangle.Width - 70) / 2 * Math.Cos((a - Math.PI / 3)) + (ClockRectangle.Width - 70) / 2 + 25
' calculate the y position of the next number
Dim y As Double = (ClockRectangle.Width - 70) / 2 * Math.Sin((a - Math.PI / 3)) + (ClockRectangle.Width - 70) / 2 + 20
' Draw the next Number according to the calculated position around the inner circumference of the circle
g.DrawString(Convert.ToString(count), ClockFont, Brushes.Black, CSng(x), CSng(y), New StringFormat)
' Increment the next Number
count += 1
Next a
End Sub 'DrawNumbers
Improvements: This clock could use one of those little date squares on the right side showing the day of the week. Also an alarm wouldn't be bad <g>. Maybe on the next go around of the virtual clock. Time's up!