Analog clock in WPF C#

Wanted to teach my son, how to read time from an analog clock. As a developer, obviously I have to write a program which can quiz him again and again and let him practice, basically outsourced my work of teaching him to a computer :)

To draw clock programmatically, I need to convert hours, minutes and seconds in to points on a circle. So first thing is to convert these values in to Radians. So what is radian? a radian is the measure of an angle that, when drawn a central angle of a circle, intercepts an arc whose length is equal to the length of the radius of the circle. So as shown in picture, the angle which cut an arc of same length as a radius is a radian.

220px-Radian_cropped_color_svg
(Source http://en.wikipedia.org/wiki/Radian)
The general formula for converting from degrees to radians is to simply multiply the number of degree by pi /180°.

float hourRadian = hour * 360 / 12 * PI / 180;
float minRadian = minute * 360 / 60 * PI / 180;
float secRadian = sec * 360 / 60 * PI / 180;


Now, you need to find out the corresponding points on the arc to draw straight lines for Hours and Minutes etc.
trig

The sine of the angle = the length of the opposite side / the length of the hypotenuse

The cosine of the angle = the length of the adjacent side /the length of the hypotenuse

trig2

The angle is 60 degrees. We are given the hypotenuse and need to find the adjacent side. This formula which connects these three is:
cos(angle) = adjacent / hypotenuse
therefore, cos60 = x / 13
therefore, x = 13 × cos60 = 6.5
therefore the length of side x is 6.5cm.
(Source http://mathsrevision.net/gcse/pages.php?page=39 )

So now arm with this information, I can find out the x, y and coordinate values on the circle by using the following formula.

  float hourEndPointX = HourLength * (float)System.Math.Sin(hourRadian);
  float hourEndPointY = HourLength * (float)System.Math.Cos(hourRadian);

Here is the code snippet, first define the canvas on the page.

  <StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="1" Background="Yellow" >
            <Canvas Name="InkCanvas"  Margin="0,0,0,0"  Width="700" > </Canvas>

        const float PI = 3.141592654F;
  DateTime dateTime;
  DispatcherTimer dispatchTimer;

  public EverydayMath()
        {
            this.InitializeComponent();

  dateTime = DateTime.Now;

  DrawClock();

  this.dispatchTimer = new DispatcherTimer();
  this.dispatchTimer.Interval = new TimeSpan(0, 0, 1);
            this.dispatchTimer.Tick += dispatchTimer_Tick;
            this.dispatchTimer.Start();

        }

  private void DrawClock()
        {

  float clockHeight = 500;
  float clockRadius = 300;

  float centerX = 400;
  float centerY = 300;

  float secThinkness = 1;
  float minThinkness = 5;
  float hourThinkness = 10;

  float hourLength = clockHeight / 3 / 1.65F;
  float minLength = clockHeight / 2.8F ;
  float secLength = clockHeight / 3 / 1.15F;

  float hourThickness = clockHeight / 100;
  float minThickness = clockHeight / 150;
  float secThickness = clockHeight / 200;

  float center = clockHeight / 50;

            InkCanvas.Children.Clear();

            int minute = dateTime.Minute;
            int sec = dateTime.Second;

            float hour = dateTime.Hour % 12 + (float)dateTime.Minute / 60;

  float hourRadian = hour * 360 / 12 * PI / 180;
  float minRadian = minute * 360 / 60 * PI / 180;
  float secRadian = sec * 360 / 60 * PI / 180;

  float hourEndPointX = hourLength * (float)System.Math.Sin(hourRadian);
  float hourEndPointY = hourLength * (float)System.Math.Cos(hourRadian);

            //Hour
  DrawLine(centerX, centerY, centerX + hourEndPointX, centerY - hourEndPointY, Colors.Black, hourThinkness);

            //minute
  for (int i = 0; i < 60; i++)
            {
  if ( i % 5 == 0) {
  DrawLine(
  centerX + (float)(clockRadius / 1.50F * System.Math.Sin(i * 6 * PI / 180)),
  centerY - (float)(clockRadius / 1.50F * System.Math.Cos(i * 6 * PI / 180)),
  centerX + (float)(clockRadius / 1.65F * System.Math.Sin(i * 6 * PI / 180)),
  centerY - (float)(clockRadius / 1.65F * System.Math.Cos(i * 6 * PI / 180)), Colors.Black, hourThinkness);

                }
                else
                {
  DrawLine(
  centerX + (float)(clockRadius / 1.50F * System.Math.Sin(i * 6 * PI / 180)),
  centerY - (float)(clockRadius / 1.50F * System.Math.Cos(i * 6 * PI / 180)),
  centerX + (float)(clockRadius / 1.55F * System.Math.Sin(i * 6 * PI / 180)),
  centerY - (float)(clockRadius / 1.55F * System.Math.Cos(i * 6 * PI / 180)), Colors.Black, hourThinkness);

                }
            }

  float minEndPointX = minLength * (float)System.Math.Sin(minRadian);
  float minEndPointY = minLength * (float)System.Math.Cos(minRadian);

  DrawLine(centerX, centerY, centerX + minEndPointX, centerY - minEndPointY, Colors.Blue , minThinkness);

            //Second
  float secEndPointX = secLength * (float)System.Math.Cos(secRadian);
  float secEndPointY = secLength * (float)System.Math.Sin(secRadian);

  DrawLine(centerX, centerY, centerX + secEndPointX, centerY - secEndPointY, Colors.Green, secThinkness);

        }

  void dispatchTimer_Tick(object sender, object e)
        {
  DrawClock();
        }

private void DrawLine(double x1, double y1, double x2, double y2, Color color, float thinkness)
        {

            Line line = new Line()
            {
  X1= x1,Y1= y1, X2 = x2, Y2 = y2,
  StrokeThickness = thinkness,
  Stroke = new SolidColorBrush(color)
            };

            InkCanvas.Children.Add(line);
        }

here is the output of code.

clock

To see this clock in action, get the game ABC123 for windows8.

Now, my kid is taught by a computer and I can do some more useful work and develop some more kids learning applications .

More information:

http://mathsrevision.net/gcse/pages.php?page=39
http://www.codeproject.com/Articles/9593/Analog-clock-control-in-C
http://en.wikipedia.org/wiki/Radian
http://www.mathwarehouse.com/trigonometry/radians/convert-degee-to-radians.php

Advertisements

3 thoughts on “Analog clock in WPF C#

  1. Hi, thanks a lot. Great article.

    lines 088-089 tiny mistake (I suppose), second hand goes backwards (my machine).
    fix:
    float minEndPointX = minLength * (float)System.Math.Sin(minRadian);
    float minEndPointY = minLength * (float)System.Math.Cos(minRadian);

    • Hi, thank you very much.
      The second hand goes wrong.
      //Second
      float secEndPointX = secLength * (float)System.Math.Sin(secRadian);
      float secEndPointY = secLength * (float)System.Math.Cos(secRadian);

      That works fine on my Computer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s