12,078,259 members (45,948 online)
alternative version

132.5K views
109 bookmarked
Posted

# Yet Another Analog Clock

, 8 Jun 2005
 Rate this:
Yet another analog clock control in C#, but this one is different.

## Introduction

There are many 'Analog clocks' at CodeProject and this is yet another (hence the title), but I have designed this one to be slight different. This analog clock is very much customizable (besides being pretty ). You can resize it, change the colors, time, rendering quality add a background image and a lot. Take a look at the list of properties. Much of the inspiration comes from A Vector Graphics Rendered Animated Clock and Public Joe.

## Background Mathematics

Let us delve back into some basic mathematics before getting involved in the programming aspects. You all know that a circle is of 360° degrees or 2Π in radians and that there are 12 digits on the face of the clock from one to twelve. To draw the twelve digits on the face, we need to place each of them at same degree from each other, so 360/12 = 30, so we need to place each digit 30° apart from the other. This means starting from 0° , 30°, 60° up to 360°. Also recall that angles are measured positive clockwise and negative anticlockwise. Now how are the minute, hour and second hand drawn. Quite simple, recall that we can calculate any point by using the formulas as seen in the picture below:

We need to know the angle θ and then we can draw a line from the centre of the circle. I.e., from origin to that point for a given θ. θ is the angle or should I say rotation. Note that a second hand takes 60 ticks to complete one complete rotation. So what should be the angle of rotation when sec = 1? Well it should be:

`Second Rotation = 2.0 *  * sec/60.0`

Think of the term sec/60.0 as the percentage of how much rotation must be given for the given value of second. For example:

```when sec = 0 , rotation is 0° ( i.e. the second hand is at 12)
when sec = 15 , rotation is 25% ( i.e. the second hand is at 3)
when sec = 30 , rotation is 50% ( i.e. the second hand is at 6)```

Now that we have the value of θ or rotation, we can find the value of x and y and draw a line from the origin to the point P using the above mentioned formula.

`Minute Rotation = 2.0 *  * ( currentmin + currentsec/60.0 )/60.0`

Now again think of the term ( currentmin + currentsec/60.0 )/60.0 as the percentage of how much rotation must be given for the given value of currentmin and currentsec. Note that in the above term we have also added second rotation so that the minute hand is updated every second. But updating minute hand every second doesn't look neat as it is updated only as significant changes occur in the value of minute rotation and it would look like that the minute hand moves unevenly. If you wish to update the minute hand after every minute only, just remove the term currentsec/60.0. Now when the second hand completes one rotation, the minute hand will move ahead. (In order to see what I am trying to tell, download the source code and observe how the minute hand behaves in the clock that displays the time for Honk Kong and the rest.)

`Hour Rotation = 2.0 *  * ( currenthour + currentmin/60.0 )/12.0`

Similar technique here. The hour hand is updated every minute so we have added the minute rotation too. You can think of how the clock would behave if we didn't update the hour hand every minute.

Now let's move on to the implementation details. All the drawing is done in the function `DrawClock: private void DrawClock(Graphics e)`. The Analog Clock exposes a property named `Time`. Whenever the value of this property is changed, the clock is updated.

## Enabling Double Buffering

In the constructor of the UserControl, I have enabled Double-buffering on the control which prevents flicker caused by the redrawing of the control. To fully enable double-buffering, we must also set the `UserPaint` and `AllPaintingInWmPaint` style bits to `true` (to read more about Double Buffering read this).

```this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint,true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);```

## Enabling Anti-aliasing

To make the clock look smooth, I have set the `SmoothingMode` and `TextRenderingHint` property of the graphics object to Anti-alias. The `SmoothingMode` property makes the face of the clock look smooth and the `TextRenderingHint` property makes the clock numerals look smooth. Please note that `SmoothingMode` has no effect on the numeral that is drawn on the clock.

```grfx.SmoothingMode = smoothingMode;
grfx.TextRenderingHint = textRenderingHint;
grfx.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; ```

Here is how the analog clock appears without anti-aliasing.

## Drawing the clock face

Next I have made four rectangles inside which circles are drawn. The drop shadow is also an eclipse which is at a slightly offset from the face eclipse. (Please note that there can be other ways to implement this, such as drawing eclipse directly).

```//Define rectangles inside which we will draw circles.
Rectangle rect = new Rectangle(0+10 ,0+10 ,(int)x-20 ,(int)y-20);
Rectangle rectinner = new Rectangle(0+40 ,0+40 ,(int)x-80 ,(int)y-80);
Rectangle rectrim = new Rectangle(0+20 ,0+20 ,(int)x-40 ,(int)y-40);
Rectangle rectdropshadow = new Rectangle(0+10 ,0+10 ,(int)x-17 ,(int)y-17);```

Next I have drawn a gradient filled eclipse inside all these rectangles,

```//Drop Shadow

//Face
if(this.drawRim)
grfx.FillEllipse(gb, rect);

//Rim
grfx.FillEllipse(gb, rectrim);```

This gives the basic face of the clock as follows:

## Drawing the circular face image

To draw a circular image within the face of the clock, we have to define a circular clipping region and draw image inside it. Once we have done that, we'll have to reset the clipping region. Here is how it is done:

```//Define a circular clip region and draw the image inside it.
GraphicsPath path = new GraphicsPath();
grfx.SetClip(path);
if(this.img != null)
grfx.DrawImage(this.img, rect);
path.Dispose();
//Reset clip region
grfx.ResetClip();```

where `img` represents the image. It is declared as:

`private Image img;`

The output is something like this:

## Drawing the Numerals

Next we need to draw the numerals. But before we do that, we need to move the origin of the control to the center of the client area. (By default it is at the top right corner). We can do so by using the graphic object's `TranslateTransform` method. Here's how it is done.

`grfx.TranslateTransform(midx, midy);`

I have already defined `midx` and `midy` as the center of the control. Next we draw the numerals:

```StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;

//Draw Numerals on the Face
int deg = 360/12;
if(drawnumerals)
{
for(int i=1; i <= 12; i++)
{
grfx.DrawString(i.ToString() , textFont ,stringBrush ,
-1 * GetX(i * deg +90) , -1 * GetY(i * deg + 90),format); ```

As you can see, we have multiplied both the value of x and y by -1 so that the numeral 12 appears on the top, other wise it'll appear on bottom as seen below.

## Drawing the Second Hand

First, we will draw the hour hand, then the minute hand and finally the second hand. The order is important because it is the same in real world clocks.

Our second hand can tick in two different ways -- as explained earlier, one is normal tick style which we usually see in quartz clocks and the other is smooth style usually seen in automatic clocks. I have defined enumerations for both of these styles which is used to determine the value of the rotation.

```//Draw Minute hand
if(drawMinuteHand)
{

if(minHandTickStyle == TickStyle.Smooth)
minuteAngle = (float)(2.0 * Math.PI* ( min + sec/60.0 )/60.0);
else
minuteAngle = (float)(2.0 * Math.PI* ( min /60.0));

pen.EndCap = LineCap.Round;
pen.StartCap = LineCap.RoundAnchor;
//--End Minute Hand```

The drop shadow of the second hand is nothing but another second hand.

```//Drop shadow
centre.Offset(2, 2);
pen.Color = Color.Gray;

{
}

centre.X = centre.Y = 0;
pen.Color = minHandColor;
Point minHand = new Point( (int)( radius * Math.Sin(minuteAngle) ),
grfx.DrawLine(pen, centre, minHand);
}```

Similar technique prevails for the hour and minute hand.

## How to use the Analog Clock.

Add the `AnalogClock` control to your VS.NET toolbox. Then place the analog clock and a timer to your form. Set an appropriate interval for the timer and enable it. Double click the timer and add the following code:

`analogClock1.Time = DateTime.Now;`

## List of Properties for the Analog Clock control

• `DrawDropShadow` - Determines whether drop shadow for the clock is drawn or not.
• `DrawHourHand` - Determines whether the hour Hand is shown.
• `DrawHourHandShadow` - Determines whether the hour hand casts a drop shadow for added realism.
• `DrawMinuteHand` - Determines whether the minute hand is shown.
• `DrawMinuteHandShadow` - Determines whether the minute hand casts a drop shadow for added realism.
• `DrawNumerals` - Determines whether the numerals are drawn on the clock face.
• `DrawRim` - Determines whether the clock rim is drawn or not.
• `DrawSecondHand` - Determines whether the second hand is shown.
• `DrawSecondHandShadow` - Determines whether the second hand casts a drop shadow for added realism.
• `DropShadowColor` - Sets or gets the color of the drop Shadow.
• `DropShadowOffset` - Gets or sets the drop shadow offset.
• `FaceColorHigh` - Determines the first color of the clock face gradient.
• `FaceColorLow` - Determines the second color of the clock face gradient.
• `FaceGradientMode` - Gets or sets the direction of the clock face gradient.
• `FaceImage` - The Background image used in the clock face.
• `HourHandColor` - Gets or sets the color of the hour hand.
• `HourHandDropShadowColor` - Sets or gets the color of the hour hand drop shadow.
• `MinuteHandColor` - Gets or sets the color of the minute hand.
• `MinuteHandDropShadowColor` - Sets or gets the color of the minute hand drop shadow.
• `MinuteHandTickStyle` - Defines the minute hand tick style.
• `NumeralColor` - Sets or gets the color of the clock numerals.
• `RimColorHigh` - Determines the first color of the rim gradient.
• `RimColorLow` - Determines the second color of the rim face gradient.
• `RimGradientMode` - Gets or sets the direction of the rim gradient.
• `SecondHandColor` - Gets or sets the color of the seconds hand.
• `SecondHandDropShadowColor` - Sets or gets the color of the second hand drop shadow.
• `SecondHandEndCap` - Determines the seconds hand end line shape.
• `SecondHandTickStyle` - Defines the second hand tick style.
• `SmoothingMode` - Sets or gets the rendering quality of the clock.
• `TextRenderingHint` - Sets or gets the text rendering mode used for the clock numerals.
• `Time` - The `System.DateTime` structure which is used to display time.

## Know Problems/Issues

• Using a large image for the `FaceImage` property results in poor performance and increased memory consumption, since the image is redrawn every time the `Time` property is changed.

• Using large font size on a large clock results in numerals going out the clock face. I haven't given much attention to this detail. This is a design flaw.

• The width of the rim remains constant and doesn't change with the size of the clock. This is yet another design flaw.

• The numeral font size doesn't change automatically with the size of the clock. This hasn't been implemented.

## History

• v1.0 - 08 June 2005

A list of licenses authors might use can be found here

## Share

 United States
Obaid ur Rehman is an undergraduate Computer Science student at the University of Karachi.

## You may also be interested in...

 First Prev Next
 My vote of 5 Chana Winkler13-Dec-10 22:41 Chana Winkler 13-Dec-10 22:41
 oscillating effect yyxl25-Nov-10 15:04 yyxl 25-Nov-10 15:04
 I did it too gajatko24-Aug-07 9:15 gajatko 24-Aug-07 9:15
 Re: I did it too gajatko24-Aug-07 9:19 gajatko 24-Aug-07 9:19
 Round Control flow19659-Apr-07 9:38 flow1965 9-Apr-07 9:38
 HI /-\ \/\/ /-\ /\/ [GUY]13-Mar-07 8:01 /-\ \/\/ /-\ /\/ [GUY] 13-Mar-07 8:01
 CAN you tell me the Book of C# which will guide me the best and available in pakistan thankz /-\ \/\/ /-\ /\/ [G U Y]
 HI /-\ \/\/ /-\ /\/ [GUY]13-Mar-07 7:33 /-\ \/\/ /-\ /\/ [GUY] 13-Mar-07 7:33
 How to add to toolbox? wizkid126-Aug-06 16:31 wizkid1 26-Aug-06 16:31
 Re: Chews up CPU wizkid126-Aug-06 17:45 wizkid1 26-Aug-06 17:45
 Re: Chews up CPU wizkid126-Aug-06 17:55 wizkid1 26-Aug-06 17:55
 Re: How to add to toolbox? SystemExam25-Dec-09 21:44 SystemExam 25-Dec-09 21:44
 Spelling nit jmueller9-Jan-06 13:29 jmueller 9-Jan-06 13:29
 How to build? 852y3agnna3-Jul-05 20:27 852y3agnna 3-Jul-05 20:27
 Re: How to build? Obaid ur Rehman4-Jul-05 4:19 Obaid ur Rehman 4-Jul-05 4:19
 Re: How to build? 852y3agnna4-Jul-05 20:04 852y3agnna 4-Jul-05 20:04
 Re: How to build? Obaid ur Rehman4-Jul-05 22:34 Obaid ur Rehman 4-Jul-05 22:34
 Re: How to build? 852y3agnna4-Jul-05 22:43 852y3agnna 4-Jul-05 22:43
 Smooth mode suggestion SGInut17-Jun-05 3:46 SGInut 17-Jun-05 3:46
 Karachi numerals AndyGarcia16-Jun-05 5:39 AndyGarcia 16-Jun-05 5:39
 Re: Karachi numerals Obaid ur Rehman16-Jun-05 8:02 Obaid ur Rehman 16-Jun-05 8:02
 Converted to VB.NET GraGra_3314-Jun-05 19:02 GraGra_33 14-Jun-05 19:02
 Re: Converted to VB.NET Obaid ur Rehman14-Jun-05 22:47 Obaid ur Rehman 14-Jun-05 22:47
 Re: Converted to VB.NET GraGra_3314-Jun-05 23:07 GraGra_33 14-Jun-05 23:07
 Re: Converted to VB.NET GraGra_3314-Jun-05 23:58 GraGra_33 14-Jun-05 23:58
 Re: Converted to VB.NET Obaid ur Rehman16-Jun-05 21:56 Obaid ur Rehman 16-Jun-05 21:56
 Re: Converted to VB.NET GraGra_3326-Jun-05 23:59 GraGra_33 26-Jun-05 23:59
 Re: Converted to VB.NET GraGra_3328-Jun-05 1:40 GraGra_33 28-Jun-05 1:40
 Re: Converted to VB.NET tp57020-Jun-05 17:11 tp570 20-Jun-05 17:11
 Increasing Performance Heath Stewart14-Jun-05 8:26 Heath Stewart 14-Jun-05 8:26
 Re: Increasing Performance GraGra_3326-Jun-05 23:53 GraGra_33 26-Jun-05 23:53
 Good norm.net9-Jun-05 2:21 norm.net 9-Jun-05 2:21
 Re: Good Obaid ur Rehman9-Jun-05 3:47 Obaid ur Rehman 9-Jun-05 3:47
 Re: Good Hamburger19849-Jun-05 6:39 Hamburger1984 9-Jun-05 6:39
 Re: Good Obaid ur Rehman13-Jun-05 23:44 Obaid ur Rehman 13-Jun-05 23:44
 Latest version of VG.net Analog Clock Frank Hileman8-Jun-05 7:24 Frank Hileman 8-Jun-05 7:24
 Re: Latest version of VG.net Analog Clock Frank Hileman8-Jun-05 13:57 Frank Hileman 8-Jun-05 13:57
 Re: Latest version of VG.net Analog Clock Obaid ur Rehman8-Jun-05 23:38 Obaid ur Rehman 8-Jun-05 23:38
 More than "Yet Another Analog clock" ddmcr8-Jun-05 7:03 ddmcr 8-Jun-05 7:03
 Re: More than "Yet Another Analog clock" Obaid ur Rehman8-Jun-05 23:43 Obaid ur Rehman 8-Jun-05 23:43
 Last Visit: 31-Dec-99 19:00     Last Update: 13-Feb-16 11:36 Refresh 1