Draw a clock

From Rosetta Code
Revision as of 08:16, 5 July 2011 by rosettacode>Dkf (→‎{{header|Tcl}}: added some notes)
Draw a clock is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Task: draw a clock. More specific:

  1. Draw a time keeping device. It can be a stopwatch, hourglass, sundial, a mouth counting "one thousand and one", anything. Only showing the seconds is required, e.g. a watch with just a second hand will suffice. However, it must clearly change every second, and the change must cycle every so often (one minute, 30 seconds, etc.) It must be drawn; printing a string of numbers to your terminal doesn't qualify. Both text-based and graphical drawing are OK.
  2. The clock is unlikely to be used to control space flights, so it needs not be hyper-accurate, but it should be usable, meaning if one can read the seconds off the clock, it must agree with the system clock.
  3. A clock is rarely (never?) a major application: don't be a CPU hog and poll the system timer every microsecond, use a proper timer/signal/event from your system or language instead. For a bad example, many OpenGL programs update the framebuffer in a busy loop even if no redraw is needed, which is very undesirable for this task.
  4. A clock is rarely (never?) a major application: try to keep your code simple and to the point. Don't write something too elaborate or convoluted, instead do whatever is natural, concise and clear in your language.

Key points: animate simple object; timed event; polling system resources; code clarity.

C

Draws a crude clock in terminal. C99, compiled with gcc -std=c99. <lang C>#include <stdio.h>

  1. include <stdlib.h>
  2. include <math.h>
  3. include <time.h>
  4. include <sys/time.h>
  1. define PI 3.14159265

char * shades = " .:-*ca&#%@";

/* distance of (x, y) from line segment (0, 0)->(x0, y0) */ double dist(double x, double y, double x0, double y0) { double l = (x * x0 + y * y0) / (x0 * x0 + y0 * y0);

if (l > 1) { x -= x0; y -= y0; } else if (l >= 0) { x -= l * x0; y -= l * y0; } return sqrt(x * x + y * y); }

enum { sec = 0, min, hur }; // for subscripts

void draw(int size) {

  1. define for_i for(int i = 0; i < size; i++)
  2. define for_j for(int j = 0; j < size * 2; j++)

double angle, cx = size / 2.; double sx[3], sy[3], sw[3]; double fade[] = { 1, .35, .35 }; /* opacity of each arm */ struct timeval tv; struct tm *t;

/* set width of each arm */ sw[sec] = size * .02; sw[min] = size * .03; sw[hur] = size * .05;

every_second: gettimeofday(&tv, 0); t = localtime(&tv.tv_sec);

angle = t->tm_sec * PI / 30; sy[sec] = -cx * cos(angle); sx[sec] = cx * sin(angle);

angle = (t->tm_min + t->tm_sec / 60.) / 30 * PI; sy[min] = -cx * cos(angle) * .8; sx[min] = cx * sin(angle) * .8;

angle = (t->tm_hour + t->tm_min / 60.) / 6 * PI; sy[hur] = -cx * cos(angle) * .6; sx[hur] = cx * sin(angle) * .6;

printf("\033[s"); /* save cursor position */ for_i { printf("\033[%d;0H", i); /* goto row i, col 0 */ double y = i - cx; for_j { double x = (j - 2 * cx) / 2;

int pix = 0; /* calcs how far the "pixel" is from each arm and set * shade, with some anti-aliasing. It's ghetto, but much * easier than a real scanline conversion. */ for (int k = hur; k >= sec; k--) { double d = dist(x, y, sx[k], sy[k]); if (d < sw[k] - .5) pix = 10 * fade[k]; else if (d < sw[k] + .5) pix = (5 + (sw[k] - d) * 10) * fade[k]; } putchar(shades[pix]); } } printf("\033[u"); /* restore cursor pos so you can bg the job -- value unclear */

fflush(stdout); sleep(1); /* sleep 1 can at times miss a second, but will catch up next update */ goto every_second; }

int main(int argc, char *argv[]) { int s; if (argc <= 1 || (s = atoi(argv[1])) <= 0) s = 20; draw(s); }</lang>

Tcl

Library: Tk

<lang tcl>package require Tcl 8.5 package require Tk

  1. GUI code

pack [canvas .c -width 200 -height 200] .c create oval 20 20 180 180 -width 10 -fill {} -outline grey70 .c create line 0 0 1 1 -tags hour -width 6 -cap round -fill black .c create line 0 0 1 1 -tags minute -width 4 -cap round -fill black .c create line 0 0 1 1 -tags second -width 2 -cap round -fill grey30 proc updateClock t {

   scan [clock format $t -format "%H %M %S"] "%d%d%d" h m s
   # On an analog clock, the hour and minute hands move gradually
   set m [expr {$m + $s/60.0}]
   set h [expr {($h % 12 + $m/60.0) * 5}]
   foreach tag {hour minute second} value [list $h $m $s] len {50 80 80} {

.c coords $tag 100 100 \ [expr {100 + $len*sin($value/30.0*3.14159)}] \ [expr {100 - $len*cos($value/30.0*3.14159)}]

   }

}

  1. Timer code, accurate to within a quarter second

set time 0 proc ticker {} {

   global time
   set t [clock seconds]
   after 250 ticker
   if {$t != $time} {

set time $t updateClock $t

   }

} ticker</lang> Note that though this code does poll the system timer approximately once a second, this is a cheap operation; the GUI update (the relatively expensive part) only happens once a second. The amount of system processing power consumed by this code isn't noticeable on my system; it vanishes with respect to the other processing normally happening.