Draw a clock: Difference between revisions
{{header|GUISS}} |
No edit summary |
||
Line 156: | Line 156: | ||
window.setInterval(update_clock, 200); |
window.setInterval(update_clock, 200); |
||
}</lang> |
}</lang> |
||
=={{header|Liberty BASIC}}== |
|||
LB has a timer to call a routine at regular intervals. The example is a cut-down version of the full clock supplied with LB as an example. |
|||
<lang lb> |
|||
WindowWidth =120 |
|||
WindowHeight =144 |
|||
nomainwin |
|||
open "Clock" for graphics_nsb_nf as #clock |
|||
#clock "trapclose [exit]" |
|||
#clock "fill white" |
|||
for angle =0 to 330 step 30 |
|||
#clock "up ; home ; north ; turn "; angle |
|||
#clock "go 40 ; down ; go 5" |
|||
next angle |
|||
#clock "flush" |
|||
timer 1000, [display] |
|||
wait |
|||
[display] ' called only when seconds have changed |
|||
time$ =time$() |
|||
seconds =val( right$( time$, 2)) |
|||
' delete the last drawn segment, if there is one |
|||
if segId >2 then #clock "delsegment "; segId -1 |
|||
' center the turtle |
|||
#clock "up ; home ; down ; north" |
|||
' erase each hand if its position has changed |
|||
if oldSeconds <>seconds then #clock, "size 1 ; color white ; turn "; oldSeconds *6 ; " ; go 38 ; home ; color black ; north" : oldSeconds =seconds |
|||
' redraw all three hands, second hand first |
|||
#clock "size 1 ; turn "; seconds * 6 ; " ; go 38" |
|||
' flush to end segment, then get the next segment id # |
|||
#clock "flush" |
|||
#clock "segment" |
|||
input #clock, segId |
|||
wait |
|||
[exit] |
|||
close #clock |
|||
end |
|||
</lang> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
Revision as of 15:21, 8 August 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>
GUISS
<lang guiss>Start,Programs,Accessories,Analogue Clock</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>
Liberty BASIC
LB has a timer to call a routine at regular intervals. The example is a cut-down version of the full clock supplied with LB as an example. <lang lb>
WindowWidth =120 WindowHeight =144 nomainwin
open "Clock" for graphics_nsb_nf as #clock #clock "trapclose [exit]" #clock "fill white" for angle =0 to 330 step 30 #clock "up ; home ; north ; turn "; angle #clock "go 40 ; down ; go 5" next angle
#clock "flush"
timer 1000, [display] wait
[display] ' called only when seconds have changed
time$ =time$() seconds =val( right$( time$, 2)) ' delete the last drawn segment, if there is one if segId >2 then #clock "delsegment "; segId -1 ' center the turtle #clock "up ; home ; down ; north" ' erase each hand if its position has changed if oldSeconds <>seconds then #clock, "size 1 ; color white ; turn "; oldSeconds *6 ; " ; go 38 ; home ; color black ; north" : oldSeconds =seconds ' redraw all three hands, second hand first #clock "size 1 ; turn "; seconds * 6 ; " ; go 38" ' flush to end segment, then get the next segment id # #clock "flush" #clock "segment" input #clock, segId
wait
[exit]
close #clock
end
</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.