CodeMusings

Kick back and get started at your own pace.

Skip to Content

Keeping time with the help of trig.

Trigonometry, Clocks, and You

Written by Curtis Dyer

Edit (9/28/2016): some major conceptual changes have been made to the section regarding explanations of the placement of the hour labels.

Departing Euclidean Planes

Most people are familiar with the basics of graphing things like linear functions of the form $Ax + By = C$ or, in slope-intercept form, $y = mx + b$. Two-dimensional Euclidean space is commonly defined using Cartesian coordinates. For example, if the point $P(x, y)$ is a coordinate pair on a Cartesian plane, then the $x$-coordinate gives the directed horizontal distance from the point of origin and the $y$-coordinate gives the directed vertical distance.

In trigonometry, we don't always find it convenient to deal with rectangular coordinates. For example, if I asked you to graph a circle, what would you need to know? You would be perfectly reasonable to ask only for the length of the radius (for the moment, we won't bother considering graphing circles not located at the origin). So $r=1$ probably seems more intuitive than $x^2+y^2=1$, which represents a circle at the origin with a radius of one unit on a Cartesian plane. Consider instead a coordinate system predicated upon radius length and angle measure: the polar coordinate system.

Graph showing how to plot: (r, theta) = (3, 45 degrees) Polar coordinates are defined in terms of $(r, \theta)$, where $r$ is the directed distance of a ray measured from the origin and $\theta$ (read as the Greek letter theta) is the angle measured between the ray and polar axis. The polar axis coincides with the positive $x$-axis. Further, due to the modular nature of angles, each point has an infinite amount of equivalent points. For example: \[ (r, \theta) = (3, 45\deg) = \left(3, \, 45\deg + (n \times 360\deg) \right) \] where $n$ is an integer. Since the ray's distance is directed, it can be negative. Given the angle remains constant, changing the sign of $r$ will reflect the coordinate with respect to the origin. You can also think of this as rotating the coordinate by $180\deg$. Using the previous example, we would have: \[ (r,\theta) = (3, 45\deg) = (-3, 45\deg\pm180\deg). \] Furthermore, angle measures can also be negative. In trigonometry (and in physics), angles are typically measured from standard position, which means positive angles are measured from the polar axis in the counterclockwise (CCW) direction and negative angles are measured in the clockwise (CW) direction. Another example shows: \[ (r, \theta) = (-3, 45\deg) = (3, -45\deg) \] and \[ (r, \theta) = (3, 45\deg) = (-3, -45\deg). \] I've set up a Desmos graph to facilitate experimenting visually with a unit circle and the corresponding relationships with the trigonometric functions.

When thinking about an analog clock shape and its hands, we find angle measure and radius are the most intuitively useful pieces of information, which is why graphics APIs generally require these pieces of information for drawing arcs, for example. However, we have a problem, because we also need to think about the pixels on the screen with respect to rectangular coordinates. To proceed, we need to marry these two worlds, but thankfully, it's not as difficult as it might seem. First, however, we need to go over the basics of the trigonometric functions sine and cosine.

Trig functions: sine and cosine

Trig definitions

Figure 1

The sine and cosine functions as defined by the ratios of the lengths of the sides of a right triangle to the hypotenuse.

The sine and cosine functions can be defined in different ways, but we're interested in the definitions with respect to right triangles. Considering a right triangle whose sides coincide with the $x$- and $y$-axes, we have (see Figure 1): \[ \cos \theta = \dfrac xr, \quad \sin \theta = \dfrac yr \] Given the definitions of sine and cosine, we can rearrange them to show the relationship between a rectangular $(x,y)$ coordinate pair and a polar coordinate pair, $(r,\theta)$. \[ x = r \cos \theta, \quad y = r \sin \theta \] This will be key in instructing our graphics libraries how and where to draw the components for our clock. It's easiest to think of arranging numbers around a clock face in terms of angular measurement. However, when we need to draw the hands, we often need to use rectangular coordinates, but thankfully, as shown above, we can relate a radius and angle of measure with the corresponding $x$- and $y$-coordinates. Now that we're armed with the capabilities of two different coordinate systems, we also need to consider a different type of angle measure, other than degrees. To that end, we will briefly cover the radian and its definition.

Measuring in radians

In many cases, it's often more convenient to measure angles in radians. A radian is the standard SI angle measure; it describes the angle subtended from the origin by a circular arc as a ratio of its length to the length of the radius. For a more rigorous definition, and helpful animations illustrating the concept, Wikipedia has you covered.

More formally, the definition of a radian is given by \[ \theta = \frac sr, \quad r \neq 0. \]

We especially want to note the case when $r = 1$, as this describes the unit circle. The unit circle is just a circle whose radius measure is 1 unit. When $r = 1$, we have $s = \theta$. This gives us a helpful equality with respect to arc length and angle, the values of which are tangible pure numbers. This is because the radian is a ratio of two lengths, so units cancel out. For this reason, you will often see that answers given in radians are not usually accompanied by a unit suffix. If you really need to make it clear you're working in radians, you can use something like: $2\pi$ (rad). This can sometimes be helpful when writing out conversion factors, where keeping track of units is especially important.

Knowing that the circumference of a circle is $2\pi r$, we can deduce that 1 revolution around the circle ($360\deg$) is $2\pi$. Since $1 \textrm{ revolution} = 2\pi \textrm{ radians} = 360\deg$, we simplify to get: $\pi = 180\deg$. We use this fact to convert between degrees and radians. For example, to convert $60\deg$ to radians, we would have \[ \left( \dfrac {60\deg}{1} \right) \left( \dfrac {\pi}{180\deg} \right) = \dfrac {\pi}{3} \]

Making a Clock

Thanks to the HTML5 <canvas> library, we can embed 2D and 3D drawing and animation in web pages. Mozilla began introducing <canvas> implementations from Firefox 1.5 and onward, Microsoft in IE9, and Google in all versions of Chrome (MDN). Most other browsers have had support for quite some time. While drawing a circle is fairly trivial, the primary challenges of making a clock require us to evenly distribute the hour labels along the edge of the clock and draw the hands on the clock. The lines need to be redrawn at least once per second (the animation).

Right about now, you must be thinking, this is going to be a lot of work!

Fortunately, we only really need the concepts just covered, a little JavaScript experience, and the art of Google-Fu to fill in the blanks. Let's break the task down into manageable bits. It's helpful if you make each step Google search friendly; it's an extremely useful process in refining your understanding of a problem, as well as making it easy to look up further information later.

  1. Use numeric variables to store data in JavaScript
  2. Draw a circle with HTML5 <canvas> API
  3. Draw a line with HTML5 <canvas> API
  4. Animate frames HTML5 <canvas> API
  5. Get date/time information in JavaScript
  6. Compute sine and cosine functions in JavaScript
  7. Divide $2\pi$ radians, which is the same as $360\deg$, into 12 equal measures
  8. Divide $2\pi$ radians into 60 equal measures (for minute marks)

Since the radius is used to determine how big our clock is, we can use the measurement of the radius to scale the length of all other useful lines related to the clock (like clock hands or minute hash marks). This gives us an elegant way to keep the clock in proportion if we decide to change its size later (or allow users to change the size). Next, we'll cover a brief example of how we might distribute hour labels along the clock, draw hands, and draw marks.

Distributing Hour Labels

We know there are twelve hours to place around the clock. We know one complete revolution is $2\pi$ radians. Therefore, it must be that $\frac{2\pi}{12} = \frac{\pi}{6}$ gives us the evenly distributed arc lengths around the circle. In our code below, we use the hours as multiples of $\frac{\pi}{6}$ to place each label around the clock face.

However, we need to account for the fact that screen pixels are generally measured differently than most people are used to. On the $y$-axis, $+5$ pixels measures five pixels from the top of the screen toward the bottom. In other words, all of the $y$ values are inverted. Further, recall that $0$ radians is at the 3-o'clock position. Therefore, unless we adjust our angles, our 12-o'clock will be where 3-o'clock should be, 11-o'clock will be at 4-o'clock, 10-o'clock will be at 5-o'clock, etc.

To make life easier, we'll assume that $y$ values will be negated later on, so we'll make an equation that correctly transforms all of our angles in a way that assumes the positive $y$ axis points up, not down. This means we can assume all measures will be measured from standard position! We need to:

  1. offset our starting point to begin at the 12-o'clock position and
  2. determine the signs for our offset and hour angle measures.
We ask ourselves, What is the angle between 3-o'clock and 12-o'clock? It's obviously a $90\deg$ separation, which we know to be $\frac {\pi}{2}$ radians. Since we're operating under the assumption that angles are measured from standard position, a positive $\frac {\pi}{2}$ indicates a counterclockwise rotation, which is exactly what we want. Now, if we start from the 12-o'clock position, in what direction is 1-o'clock? We see that it is clockwise with respect to 12. Therefore, we know that all of our hour angle measures must be negative. Putting this all together, we just add our initial $90\deg$ rotation to our negative offsets. Let's create an equation showing how this is done.

We begin by directly stating our findings above. From there, we use algebra to attempt to simplify whatever we can. We let $\theta\, '$ represent our transformed angle and we let $h$ be defined as the hour. We then have: \[ \begin{align} \theta\, ' &= \frac {\pi}{2} - \frac {h\pi}{6} \\[1em] &= \frac {3\pi - h\pi}{6} \\[1em] &= \frac {\pi(3 - h)}{6} \\[1em] &= \frac {\pi}{6}(3-h). \end{align} \] I prefer to simplify the expression here, because it looks a little neater, and there's only one instance of $\pi$, as opposed to two. Sometimes, doing the simplifications using algebra can lead to much cleaner expressions, so it's definitely worth brushing up on those skills.

  1. // canvas context
  2. var ctx = document.getElementById("canvas").getContext("2d");
  3.  
  4. var r = 150; // radius
  5. var pad = -20; // padding
  6.  
  7. // change origin of canvas to center of clock
  8. ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
  9.  
  10. // orient text alignment correctly
  11. ctx.textAlign = "center";
  12. ctx.textBaseline = "middle";
  13.  
  14. // set up some style options for how the clock looks
  15. ctx.strokeStyle = "black";
  16. ctx.font = "12pt arial";
  17.  
  18. // draw edge of clock
  19. ctx.arc(0, 0, r, 0, 2*Math.PI);
  20. ctx.stroke();
  21.  
  22. // drawing clock labels
  23. for (var label = 1; label <= 12; ++label) {
  24. // we offset the angle and subtract the measure of our current
  25. // hour angle. Here, we're also using symmetry
  26. var theta = Math.PI*(label - 3) / 6;
  27.  
  28. // these are the equivalent rectangular coordinates
  29. var x = (r+pad) * Math.cos(theta);
  30. var y = (r+pad) * Math.sin(theta);
  31.  
  32. // draw the text
  33. ctx.fillText(label, x, y);
  34. }

I've put up a demonstration of this code on JSFiddle. When dealing with pixel coordinates, it's important to remember that, by default, $(0, 0)$ is located at the top-left of the window. So, again, the $y$-values increase as we travel down toward the bottom of the window. However, the Canvas API does let us move our origin to a different location, so we can at least avoid dealing with that extra transformation headache. You'll notice that the JSFiddle code actually takes a different approach than what we've discussed above. It makes use of a useful property of the sine and cosine functions: symmetry!

I should point out that we don't necessarily have to reverse the $y$-values, because the sine and cosine functions have useful symmetry properties. We can just negate the angle, and our clock coordinates will turn out exactly as we want! You may recall learning about odd and even functions. As a quick refresher, let's consider functions $f$ and $g$ with respect to any real number $x$. If $f$ is an odd function, then $f(-x)=-f(x)$ for all values of $x$. If $g$ is an even function, then $g(-x)=g(x)$ for all values of $x$. Odd functions are symmetrical with respect to the origin, and even functions with respect to the $y$-axis. It just so happens that the sine function is odd, and the cosine function is even: \[ \sin(-\theta)=-\sin \theta \, ; \quad \cos(-\theta) = \cos \theta \] We can use this fact to simply negate our earlier equation so as to avoid negating the $y$ values. Our original equation becomes: \[ \theta \, ' = \frac {\pi}{6}(h - 3). \]

Drawing Hands and Marks

We can extend the same concept to drawing lines to represent the hands or hash marks. An interesting thing is that the <canvas> API lets us rotate the canvas itself. So, for example, if we wanted to draw 60 evenly spaced hash marks around the clock, we could simply draw a short line at the edge of the clock, then rotate the canvas by $\frac {2\pi}{60}=\frac {\pi}{30}$ radians and then repeat until we've rotated for a total of $2\pi$ radians.

Just Another Tool

Sometimes, you find things become unexpectedly useful when coming across various problems while programming. Although mathematics seems an obviously useful tool to have, it's not always clear in what ways it can specifically aid you. Sometimes, the things you learn only sit in your mind with a vague sense of utility. For me, this was a little exercise in bridging programming and trigonometry class. With a real-world project and concrete goals, it sometimes provides a deeper, pragmatic level of understanding to abstract concepts.

Challenges

If you're interested in the trigonometry behind these concepts, try out the problems below to help solidify the concepts.

Calculating angles

Consider a clock with a minute and hour hand. If the time reads 10:30 on the clock, where both hands' positions are accurate to within a minute, what is the radian measure of the least positive angle between both hands? What is the measure in degrees?

Tip: try drawing a sketch to help solve the problem.

Show solutions.

Concepts

Above, we cover canvas rotation as an alternative method to drawing hash marks or hands for the clock. Explain why this approach works well for drawing lines, but not necessarily for drawing text. Try getting an example running on JSFiddle to demonstrate the problem.

References

  1. ^ - Mozilla Developer Network. Accessed 25 May 2015.

Resources

Curtis Dyer — hobbyist computer programmer and Web developer.