An Analog Clock


Despite not having to worry about internationalization of date and time strings, an analog clock is quite a bit more complex to code than the digital clock of the previous section. To code an analog clock, knowledge of the mapping modes and some trigonometry needs to be acquired. A snapshot of the analog clock running is shown below.

By way of setting the mapping mode for the clock program, the function set_isotropic is shown below.

void set_isotropic(handle device_context,
                   int width_of_client,
                   int height_of_client)
{
 set_mapping_mode(device_context,unit::isotropic);
 set_window_extent(device_context,1000,1000);
 set_viewport_extent(device_context,width_of_client/2,-height_of_client/2);
 set_viewport_origin(device_context,width_of_client/2, height_of_client/2);
}

The unit unit::Isotropic is set and the window extent is set to 1000 in both the x and y directions. The viewport of origin is set to be at the center of the client area and the viewport extent is set to be 1/2 the width of the client in the x-direction and -1/2 the height of the client area in the y-direction. This yields an isotropic, cartesian coordinate system centered within the client area of the window.

The function rotate_points rotates a specified array of points a given angle in the clockwise direction. The equations for rotating the point(x,y) through an angle 'a' (anticlockwise) are

x' = x*cos(a) - y*sin(a);
y' = y*sin(a) + x*cos(a);

These are the standard linear algebra results obtained by a 2x2 matrix multiplication. To rotate clockwise, one must substitute -a for a to get:

x' =  x*cos(a) + y*sin(a);  // noting that sin(-a) == -sin(a)
y' = -y*sin(a) + x*cos(a);  //        and  cos(-a) ==  cos(a)

These formulae are used in the function rotate_points shown below.

void rotate_points(point point_array[],
                   int count,
                   int angle)
{ 
 double radians = TwoPi * angle / 360;

 double sine   = sin(radians);
 double cosine = cos(radians);

 matrix22 rotation(cosine,sine,-sine,cosine);

 for (int i=0; i<count; i++)
  point_array[i] = rotation * point_array[i];
}

The function rotate_points is used for drawing the 60 dots surrounding the clock face as well as the hands of the clock contained therein. The function draw_clock draws the 60 ellipses at intervals of 6 degrees, varying the size of the dots at five minute intervals. The code that does this is contained in the sample file.

The function draw_hands performs similar rotations, where the rotational amount is calculated by the time (in hours, minutes and seconds). It takes the system time as its input. The initial shapes of the hands are held in a static array. These are copied to a temporary array; where, the calculations are performed on this temporary array. An array of three angles is calculated based upon the current time. The flag change is used to determine whether to rotate and draw only the second hand or whether to include the minute and hour hands in the calculation (given that they change relatively infrequently).

When a timer message is received, the previous date/time is compared with the current date/time to determine which hands needs to be drawn. The previous positions of the hands are drawn in white (to erase them) after which the new positions of the hands are drawn.