Draw a clock: Difference between revisions
→{{header|Tcl}}: Added sample output |
|||
Line 101: | Line 101: | ||
if (argc <= 1 || (s = atoi(argv[1])) <= 0) s = 20; |
if (argc <= 1 || (s = atoi(argv[1])) <= 0) s = 20; |
||
draw(s); |
draw(s); |
||
}</lang> |
|||
=={{header|JavaScript}}== |
|||
Tested on Gecko. Put the following in a <script> tag somewhere, and call <code>init_clock()</code> after body load. |
|||
<lang JavaScript>var sec_old = 0; |
|||
function update_clock() { |
|||
var t = new Date(); |
|||
var arms = [t.getHours(), t.getMinutes(), t.getSeconds()]; |
|||
if (arms[2] == sec_old) return; |
|||
sec_old = arms[2]; |
|||
var c = document.getElementById('clock'); |
|||
var ctx = c.getContext('2d'); |
|||
ctx.fillStyle = "rgb(0,200,200)"; |
|||
ctx.fillRect(0, 0, c.width, c.height); |
|||
ctx.fillStyle = "white"; |
|||
ctx.fillRect(3, 3, c.width - 6, c.height - 6); |
|||
ctx.lineCap = 'round'; |
|||
var orig = { x: c.width / 2, y: c.height / 2 }; |
|||
arms[1] += arms[2] / 60; |
|||
arms[0] += arms[1] / 60; |
|||
draw_arm(ctx, orig, arms[0] * 30, c.width/2.5 - 15, c.width / 20, "green"); |
|||
draw_arm(ctx, orig, arms[1] * 6, c.width/2.2 - 10, c.width / 30, "navy"); |
|||
draw_arm(ctx, orig, arms[2] * 6, c.width/2.0 - 6, c.width / 100, "maroon"); |
|||
} |
|||
function draw_arm(ctx, orig, deg, len, w, style) |
|||
{ |
|||
ctx.save(); |
|||
ctx.lineWidth = w; |
|||
ctx.lineCap = 'round'; |
|||
ctx.translate(orig.x, orig.y); |
|||
ctx.rotate((deg - 90) * Math.PI / 180); |
|||
ctx.strokeStyle = style; |
|||
ctx.beginPath(); |
|||
ctx.moveTo(-len / 10, 0); |
|||
ctx.lineTo(len, 0); |
|||
ctx.stroke(); |
|||
ctx.restore(); |
|||
} |
|||
function init_clock() { |
|||
var clock = document.createElement('canvas'); |
|||
clock.width = 100; |
|||
clock.height = 100; |
|||
clock.id = "clock"; |
|||
document.body.appendChild(clock); |
|||
window.setInterval(update_clock, 200); |
|||
}</lang> |
}</lang> |
||
Revision as of 01:52, 6 July 2011
Task: draw a clock. More specific:
- 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.
- 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.
- 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.
- 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>
- include <stdlib.h>
- include <math.h>
- include <time.h>
- include <sys/time.h>
- 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) {
- define for_i for(int i = 0; i < size; i++)
- 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>
JavaScript
Tested on Gecko. Put the following in a <script> tag somewhere, and call init_clock()
after body load.
<lang JavaScript>var sec_old = 0;
function update_clock() {
var t = new Date();
var arms = [t.getHours(), t.getMinutes(), t.getSeconds()];
if (arms[2] == sec_old) return;
sec_old = arms[2];
var c = document.getElementById('clock'); var ctx = c.getContext('2d'); ctx.fillStyle = "rgb(0,200,200)"; ctx.fillRect(0, 0, c.width, c.height); ctx.fillStyle = "white"; ctx.fillRect(3, 3, c.width - 6, c.height - 6); ctx.lineCap = 'round';
var orig = { x: c.width / 2, y: c.height / 2 }; arms[1] += arms[2] / 60; arms[0] += arms[1] / 60; draw_arm(ctx, orig, arms[0] * 30, c.width/2.5 - 15, c.width / 20, "green"); draw_arm(ctx, orig, arms[1] * 6, c.width/2.2 - 10, c.width / 30, "navy"); draw_arm(ctx, orig, arms[2] * 6, c.width/2.0 - 6, c.width / 100, "maroon"); }
function draw_arm(ctx, orig, deg, len, w, style) { ctx.save(); ctx.lineWidth = w; ctx.lineCap = 'round'; ctx.translate(orig.x, orig.y); ctx.rotate((deg - 90) * Math.PI / 180); ctx.strokeStyle = style; ctx.beginPath(); ctx.moveTo(-len / 10, 0); ctx.lineTo(len, 0); ctx.stroke(); ctx.restore(); }
function init_clock() { var clock = document.createElement('canvas'); clock.width = 100; clock.height = 100; clock.id = "clock"; document.body.appendChild(clock);
window.setInterval(update_clock, 200); }</lang>
Tcl
![](http://static.miraheze.org/rosettacodewiki/4/4b/Clock_tcltk.png)
<lang tcl>package require Tcl 8.5 package require Tk
- 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)}]
}
}
- 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 four times 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.