Draw a clock
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.
Contents |
[edit] AutoHotkey
requires the GDI+ Library from http://www.autohotkey.com/forum/viewtopic.php?t=32238 this code from http://www.autohotkey.com/forum/viewtopic.php?p=231836#231836 draws a very nice clock with GDI+
; gdi+ ahk analogue clock example written by derRaphael
; Parts based on examples from Tic's GDI+ Tutorials and of course on his GDIP.ahk
; This code has been licensed under the terms of EUPL 1.0
#SingleInstance, Force
#NoEnv
SetBatchLines, -1
; Uncomment if Gdip.ahk is not in your standard library
;#Include, Gdip.ahk
If !pToken := Gdip_Startup()
{
MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
ExitApp
}
OnExit, Exit
SysGet, MonitorPrimary, MonitorPrimary
SysGet, WA, MonitorWorkArea, %MonitorPrimary%
WAWidth := WARight-WALeft
WAHeight := WABottom-WATop
Gui, 1: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
Gui, 1: Show, NA
hwnd1 := WinExist()
ClockDiameter := 180
Width := Height := ClockDiameter + 2 ; make width and height slightly bigger to avoid cut away edges
CenterX := CenterY := floor(ClockDiameter/2) ; Center x
; Prepare our pGraphic so we have a 'canvas' to work upon
hbm := CreateDIBSection(Width, Height), hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm), G := Gdip_GraphicsFromHDC(hdc)
Gdip_SetSmoothingMode(G, 4)
; Draw outer circle
Diameter := ClockDiameter
pBrush := Gdip_BrushCreateSolid(0x66008000)
Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
Gdip_DeleteBrush(pBrush)
; Draw inner circle
Diameter := ceil(ClockDiameter - ClockDiameter*0.08) ; inner circle is 8 % smaller than clock's diameter
pBrush := Gdip_BrushCreateSolid(0x80008000)
Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
Gdip_DeleteBrush(pBrush)
; Draw Second Marks
R1 := Diameter//2-1 ; outer position
R2 := Diameter//2-1-ceil(Diameter//2*0.05) ; inner position
Items := 60 ; we have 60 seconds
pPen := Gdip_CreatePen(0xff00a000, floor((ClockDiameter/100)*1.2)) ; 1.2 % of total diameter is our pen width
GoSub, DrawClockMarks
Gdip_DeletePen(pPen)
; Draw Hour Marks
R1 := Diameter//2-1 ; outer position
R2 := Diameter//2-1-ceil(Diameter//2*0.1) ; inner position
Items := 12 ; we have 12 hours
pPen := Gdip_CreatePen(0xc0008000, ceil((ClockDiameter//100)*2.3)) ; 2.3 % of total diameter is our pen width
GoSub, DrawClockMarks
Gdip_DeletePen(pPen)
; The OnMessage will let us drag the clock
OnMessage(0x201, "WM_LBUTTONDOWN")
UpdateLayeredWindow(hwnd1, hdc, WALeft+((WAWidth-Width)//2), WATop+((WAHeight-Height)//2), Width, Height)
SetTimer, sec, 1000
sec:
; prepare to empty previously drawn stuff
Gdip_SetSmoothingMode(G, 1) ; turn off aliasing
Gdip_SetCompositingMode(G, 1) ; set to overdraw
; delete previous graphic and redraw background
Diameter := ceil(ClockDiameter - ClockDiameter*0.18) ; 18 % less than clock's outer diameter
; delete whatever has been drawn here
pBrush := Gdip_BrushCreateSolid(0x00000000) ; fully transparent brush 'eraser'
Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
Gdip_DeleteBrush(pBrush)
Gdip_SetCompositingMode(G, 0) ; switch off overdraw
pBrush := Gdip_BrushCreateSolid(0x66008000)
Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
Gdip_DeleteBrush(pBrush)
pBrush := Gdip_BrushCreateSolid(0x80008000)
Gdip_FillEllipse(G, pBrush, CenterX-(Diameter//2), CenterY-(Diameter//2),Diameter, Diameter)
Gdip_DeleteBrush(pBrush)
; Draw HoursPointer
Gdip_SetSmoothingMode(G, 4) ; turn on antialiasing
t := A_Hour*360//12 + (A_Min*360//60)//12 +90
R1 := ClockDiameter//2-ceil((ClockDiameter//2)*0.5) ; outer position
pPen := Gdip_CreatePen(0xa0008000, floor((ClockDiameter/100)*3.5))
Gdip_DrawLine(G, pPen, CenterX, CenterY
, ceil(CenterX - (R1 * Cos(t * Atan(1) * 4 / 180)))
, ceil(CenterY - (R1 * Sin(t * Atan(1) * 4 / 180))))
Gdip_DeletePen(pPen)
; Draw MinutesPointer
t := A_Min*360//60+90
R1 := ClockDiameter//2-ceil((ClockDiameter//2)*0.25) ; outer position
pPen := Gdip_CreatePen(0xa0008000, floor((ClockDiameter/100)*2.7))
Gdip_DrawLine(G, pPen, CenterX, CenterY
, ceil(CenterX - (R1 * Cos(t * Atan(1) * 4 / 180)))
, ceil(CenterY - (R1 * Sin(t * Atan(1) * 4 / 180))))
Gdip_DeletePen(pPen)
; Draw SecondsPointer
t := A_Sec*360//60+90
R1 := ClockDiameter//2-ceil((ClockDiameter//2)*0.2) ; outer position
pPen := Gdip_CreatePen(0xa000FF00, floor((ClockDiameter/100)*1.2))
Gdip_DrawLine(G, pPen, CenterX, CenterY
, ceil(CenterX - (R1 * Cos(t * Atan(1) * 4 / 180)))
, ceil(CenterY - (R1 * Sin(t * Atan(1) * 4 / 180))))
Gdip_DeletePen(pPen)
UpdateLayeredWindow(hwnd1, hdc) ;, xPos, yPos, ClockDiameter, ClockDiameter)
return
DrawClockMarks:
Loop, % Items
Gdip_DrawLine(G, pPen
, CenterX - ceil(R1 * Cos(((a_index-1)*360//Items) * Atan(1) * 4 / 180))
, CenterY - ceil(R1 * Sin(((a_index-1)*360//Items) * Atan(1) * 4 / 180))
, CenterX - ceil(R2 * Cos(((a_index-1)*360//Items) * Atan(1) * 4 / 180))
, CenterY - ceil(R2 * Sin(((a_index-1)*360//Items) * Atan(1) * 4 / 180)) )
return
WM_LBUTTONDOWN() {
PostMessage, 0xA1, 2
return
}
esc::
Exit:
SelectObject(hdc, obm)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(G)
Gdip_Shutdown(pToken)
ExitApp
Return
[edit] C
Draws a crude clock in terminal. C99, compiled with gcc -std=c99.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#define PI 3.14159265
const 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);
return 0;
}
[edit] C++
#include <windows.h>
#include <string>
#include <math.h>
//--------------------------------------------------------------------------------------------------
using namespace std;
//--------------------------------------------------------------------------------------------------
const int BMP_SIZE = 300, MY_TIMER = 987654, CENTER = BMP_SIZE >> 1, SEC_LEN = CENTER - 20,
MIN_LEN = SEC_LEN - 20, HOUR_LEN = MIN_LEN - 20;
const float PI = 3.1415926536f;
//--------------------------------------------------------------------------------------------------
class vector2
{
public:
vector2() { x = y = 0; }
vector2( int a, int b ) { x = a; y = b; }
void set( int a, int b ) { x = a; y = b; }
void rotate( float angle_r )
{
float _x = static_cast<float>( x ),
_y = static_cast<float>( y ),
s = sinf( angle_r ),
c = cosf( angle_r ),
a = _x * c - _y * s,
b = _x * s + _y * c;
x = static_cast<int>( a );
y = static_cast<int>( b );
}
int x, y;
};
//--------------------------------------------------------------------------------------------------
class myBitmap
{
public:
myBitmap() : pen( NULL ), brush( NULL ), clr( 0 ), wid( 1 ) {}
~myBitmap()
{
DeleteObject( pen );
DeleteObject( brush );
DeleteDC( hdc );
DeleteObject( bmp );
}
bool create( int w, int h )
{
BITMAPINFO bi;
ZeroMemory( &bi, sizeof( bi ) );
bi.bmiHeader.biSize = sizeof( bi.bmiHeader );
bi.bmiHeader.biBitCount = sizeof( DWORD ) * 8;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biWidth = w;
bi.bmiHeader.biHeight = -h;
HDC dc = GetDC( GetConsoleWindow() );
bmp = CreateDIBSection( dc, &bi, DIB_RGB_COLORS, &pBits, NULL, 0 );
if( !bmp ) return false;
hdc = CreateCompatibleDC( dc );
SelectObject( hdc, bmp );
ReleaseDC( GetConsoleWindow(), dc );
width = w; height = h;
return true;
}
void clear( BYTE clr = 0 )
{
memset( pBits, clr, width * height * sizeof( DWORD ) );
}
void setBrushColor( DWORD bClr )
{
if( brush ) DeleteObject( brush );
brush = CreateSolidBrush( bClr );
SelectObject( hdc, brush );
}
void setPenColor( DWORD c )
{
clr = c;
createPen();
}
void setPenWidth( int w )
{
wid = w;
createPen();
}
void saveBitmap( string path )
{
BITMAPFILEHEADER fileheader;
BITMAPINFO infoheader;
BITMAP bitmap;
DWORD wb;
GetObject( bmp, sizeof( bitmap ), &bitmap );
DWORD* dwpBits = new DWORD[bitmap.bmWidth * bitmap.bmHeight];
ZeroMemory( dwpBits, bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD ) );
ZeroMemory( &infoheader, sizeof( BITMAPINFO ) );
ZeroMemory( &fileheader, sizeof( BITMAPFILEHEADER ) );
infoheader.bmiHeader.biBitCount = sizeof( DWORD ) * 8;
infoheader.bmiHeader.biCompression = BI_RGB;
infoheader.bmiHeader.biPlanes = 1;
infoheader.bmiHeader.biSize = sizeof( infoheader.bmiHeader );
infoheader.bmiHeader.biHeight = bitmap.bmHeight;
infoheader.bmiHeader.biWidth = bitmap.bmWidth;
infoheader.bmiHeader.biSizeImage = bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD );
fileheader.bfType = 0x4D42;
fileheader.bfOffBits = sizeof( infoheader.bmiHeader ) + sizeof( BITMAPFILEHEADER );
fileheader.bfSize = fileheader.bfOffBits + infoheader.bmiHeader.biSizeImage;
GetDIBits( hdc, bmp, 0, height, ( LPVOID )dwpBits, &infoheader, DIB_RGB_COLORS );
HANDLE file = CreateFile( path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
WriteFile( file, &fileheader, sizeof( BITMAPFILEHEADER ), &wb, NULL );
WriteFile( file, &infoheader.bmiHeader, sizeof( infoheader.bmiHeader ), &wb, NULL );
WriteFile( file, dwpBits, bitmap.bmWidth * bitmap.bmHeight * 4, &wb, NULL );
CloseHandle( file );
delete [] dwpBits;
}
HDC getDC() const { return hdc; }
int getWidth() const { return width; }
int getHeight() const { return height; }
private:
void createPen()
{
if( pen ) DeleteObject( pen );
pen = CreatePen( PS_SOLID, wid, clr );
SelectObject( hdc, pen );
}
HBITMAP bmp;
HDC hdc;
HPEN pen;
HBRUSH brush;
void *pBits;
int width, height, wid;
DWORD clr;
};
//--------------------------------------------------------------------------------------------------
class clock
{
public:
clock()
{
_bmp.create( BMP_SIZE, BMP_SIZE );
_bmp.clear( 100 );
_bmp.setPenWidth( 2 );
_ang = DegToRadian( 6 );
}
void setNow()
{
GetLocalTime( &_sysTime );
draw();
}
float DegToRadian( float degree ) { return degree * ( PI / 180.0f ); }
void setHWND( HWND hwnd ) { _hwnd = hwnd; }
private:
void drawTicks( HDC dc )
{
vector2 line;
_bmp.setPenWidth( 1 );
for( int x = 0; x < 60; x++ )
{
line.set( 0, 50 );
line.rotate( static_cast<float>( x + 30 ) * _ang );
MoveToEx( dc, CENTER - static_cast<int>( 2.5f * static_cast<float>( line.x ) ), CENTER - static_cast<int>( 2.5f * static_cast<float>( line.y ) ), NULL );
LineTo( dc, CENTER - static_cast<int>( 2.81f * static_cast<float>( line.x ) ), CENTER - static_cast<int>( 2.81f * static_cast<float>( line.y ) ) );
}
_bmp.setPenWidth( 3 );
for( int x = 0; x < 60; x += 5 )
{
line.set( 0, 50 );
line.rotate( static_cast<float>( x + 30 ) * _ang );
MoveToEx( dc, CENTER - static_cast<int>( 2.5f * static_cast<float>( line.x ) ), CENTER - static_cast<int>( 2.5f * static_cast<float>( line.y ) ), NULL );
LineTo( dc, CENTER - static_cast<int>( 2.81f * static_cast<float>( line.x ) ), CENTER - static_cast<int>( 2.81f * static_cast<float>( line.y ) ) );
}
}
void drawHands( HDC dc )
{
float hp = DegToRadian( ( 30.0f * static_cast<float>( _sysTime.wMinute ) ) / 60.0f );
int h = ( _sysTime.wHour > 12 ? _sysTime.wHour - 12 : _sysTime.wHour ) * 5;
_bmp.setPenWidth( 3 );
_bmp.setPenColor( RGB( 0, 0, 255 ) );
drawHand( dc, HOUR_LEN, ( _ang * static_cast<float>( 30 + h ) ) + hp );
_bmp.setPenColor( RGB( 0, 128, 0 ) );
drawHand( dc, MIN_LEN, _ang * static_cast<float>( 30 + _sysTime.wMinute ) );
_bmp.setPenWidth( 2 );
_bmp.setPenColor( RGB( 255, 0, 0 ) );
drawHand( dc, SEC_LEN, _ang * static_cast<float>( 30 + _sysTime.wSecond ) );
}
void drawHand( HDC dc, int len, float ang )
{
vector2 line;
line.set( 0, len );
line.rotate( ang );
MoveToEx( dc, CENTER, CENTER, NULL );
LineTo( dc, line.x + CENTER, line.y + CENTER );
}
void draw()
{
HDC dc = _bmp.getDC();
_bmp.setBrushColor( RGB( 250, 250, 250 ) );
Ellipse( dc, 0, 0, BMP_SIZE, BMP_SIZE );
_bmp.setBrushColor( RGB( 230, 230, 230 ) );
Ellipse( dc, 10, 10, BMP_SIZE - 10, BMP_SIZE - 10 );
drawTicks( dc );
drawHands( dc );
_bmp.setPenColor( 0 ); _bmp.setBrushColor( 0 );
Ellipse( dc, CENTER - 5, CENTER - 5, CENTER + 5, CENTER + 5 );
_wdc = GetDC( _hwnd );
BitBlt( _wdc, 0, 0, BMP_SIZE, BMP_SIZE, dc, 0, 0, SRCCOPY );
ReleaseDC( _hwnd, _wdc );
}
myBitmap _bmp;
HWND _hwnd;
HDC _wdc;
SYSTEMTIME _sysTime;
float _ang;
};
//--------------------------------------------------------------------------------------------------
class wnd
{
public:
wnd() { _inst = this; }
int wnd::Run( HINSTANCE hInst )
{
_hInst = hInst;
_hwnd = InitAll();
SetTimer( _hwnd, MY_TIMER, 1000, NULL );
_clock.setHWND( _hwnd );
ShowWindow( _hwnd, SW_SHOW );
UpdateWindow( _hwnd );
MSG msg;
ZeroMemory( &msg, sizeof( msg ) );
while( msg.message != WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) != 0 )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
return UnregisterClass( "_MY_CLOCK_", _hInst );
}
private:
void wnd::doPaint( HDC dc ) { _clock.setNow(); }
void wnd::doTimer() { _clock.setNow(); }
static int WINAPI wnd::WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_DESTROY: PostQuitMessage( 0 ); break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC dc = BeginPaint( hWnd, &ps );
_inst->doPaint( dc );
EndPaint( hWnd, &ps );
return 0;
}
case WM_TIMER: _inst->doTimer(); break;
default:
return DefWindowProc( hWnd, msg, wParam, lParam );
}
return 0;
}
HWND InitAll()
{
WNDCLASSEX wcex;
ZeroMemory( &wcex, sizeof( wcex ) );
wcex.cbSize = sizeof( WNDCLASSEX );
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = ( WNDPROC )WndProc;
wcex.hInstance = _hInst;
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
wcex.lpszClassName = "_MY_CLOCK_";
RegisterClassEx( &wcex );
RECT rc = { 0, 0, BMP_SIZE, BMP_SIZE };
AdjustWindowRect( &rc, WS_SYSMENU | WS_CAPTION, FALSE );
int w = rc.right - rc.left, h = rc.bottom - rc.top;
return CreateWindow( "_MY_CLOCK_", ".: Clock -- PJorente :.", WS_SYSMENU, CW_USEDEFAULT, 0, w, h, NULL, NULL, _hInst, NULL );
}
static wnd* _inst;
HINSTANCE _hInst;
HWND _hwnd;
clock _clock;
};
wnd* wnd::_inst = 0;
//--------------------------------------------------------------------------------------------------
int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow )
{
wnd myWnd;
return myWnd.Run( hInstance );
}
//--------------------------------------------------------------------------------------------------
[edit] GUISS
Start,Programs,Accessories,Analogue Clock
[edit] JavaScript
Tested on Gecko. Put the following in a <script> tag somewhere, and call init_clock() after body load.
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);
}
[edit] 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.
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
[edit] Locomotive Basic
Draw a clock with just a second hand:
10 mode 1:defint a-z:deg
20 r=150:s=0:p=0
30 origin 320,200
40 move 0,r
50 for a=0 to 360 step 2
60 draw r*sin(a),r*cos(a)
70 next
80 for a=0 to 360 step 6
90 move .9*r*sin(a),.9*r*cos(a)
100 draw r*sin(a),r*cos(a)
110 next
120 every 50 gosub 160
130 ' main loop
140 goto 140
150 ' draw clock
160 gosub 230
170 s=(s+1) mod 60
180 locate 1,1
190 print s
200 gosub 230
210 return
220 ' draw hand
230 a=s/60*360
240 move 0,0
250 frame:draw .8*r*sin(a),.8*r*cos(a),p
260 p=1-p
270 return
[edit] Mathematica
makeHand[fl_, bl_, fw_, bw_] := Polygon[{{-bw, -bl}, {bw, -bl}, {fw, fl}, {0, fl + 8 fw}, {-fw, fl}}/9];
hourHand = makeHand[5, 5/3, .1, .3];minuteHand = makeHand[7, 7/3, .1, .3];
secondHand = {Red, EdgeForm[Black], makeHand[7, 7/3, .1/2, .3/2]};
Graphics[{
{Thickness[.03], Circle[]},(* Rim *)
{Thickness[.003], Table[Line[{.9 {Cos[a], Sin[a]}, .95 {Cos[a], Sin[a]}}], {a, 0, 2 \[Pi], 2 \[Pi]/60}]}, (* Thin ticks *)
{Thickness[.01], Table[Line[{.9 {Cos[a], Sin[a]}, .95 {Cos[a], Sin[a]}}], {a, 0, 2 \[Pi], 2 \[Pi]/12}]}, (* Thick ticks *)
Style[Table[Text[i, .77 {Cos[-i \[Pi]/6 + \[Pi]/2], Sin[-i \[Pi]/6 + \[Pi]/2]}], {i, 1, 12}], FontFamily -> "Helvetica", FontSize -> 36], (* Numbers *)
Rotate[hourHand, Dynamic[Refresh[-30 Mod[AbsoluteTime[]/3600, 60] \[Degree], UpdateInterval -> 60]], {0, 0}],
Rotate[minuteHand, Dynamic[Refresh[-6 Mod[AbsoluteTime[]/60, 60] \[Degree], UpdateInterval -> 1]], {0, 0}],
Rotate[secondHand, Dynamic[Refresh[-6 Mod[AbsoluteTime[], 60] \[Degree], UpdateInterval -> 1/20]], {0, 0}]
}]
[edit] MATLAB / Octave
u = [0:360]*pi/180;
while(1)
s = mod(now*60*24,1)*2*pi;
plot([0,sin(s)],[0,cos(s)],'-',sin(u),cos(u),'k-');
pause(1);
end;
[edit] OCaml
Using only the standard library of OCaml with its Graphics module:
#!/usr/bin/env ocaml
#load "unix.cma"
#load "graphics.cma"
open Graphics
let pi = 4.0 *. atan 1.0
let angle v max = float v /. max *. 2.0 *. pi
let () =
open_graph "";
set_window_title "OCaml Clock";
resize_window 256 256;
auto_synchronize false;
let w = size_x ()
and h = size_y () in
let rec loop () =
clear_graph ();
let point radius r a =
let x = int_of_float (radius *. sin a)
and y = int_of_float (radius *. cos a) in
fill_circle (w/2+x) (h/2+y) r;
in
set_color (rgb 192 192 192);
point 84.0 8 0.0;
point 84.0 8 (angle 90 360.0);
point 84.0 8 (angle 180 360.0);
point 84.0 8 (angle 270 360.0);
set_color (rgb 224 224 224);
point 84.0 6 (angle 30 360.0);
point 84.0 6 (angle 60 360.0);
point 84.0 6 (angle 120 360.0);
point 84.0 6 (angle 150 360.0);
point 84.0 6 (angle 210 360.0);
point 84.0 6 (angle 240 360.0);
point 84.0 6 (angle 300 360.0);
point 84.0 6 (angle 330 360.0);
set_line_width 9;
set_color (rgb 192 192 192);
draw_circle (w/2) (h/2) 100;
let tm = Unix.localtime (Unix.gettimeofday ()) in
let sec = angle tm.Unix.tm_sec 60.0 in
let min = angle tm.Unix.tm_min 60.0 in
let hour = angle (tm.Unix.tm_hour * 60 + tm.Unix.tm_min) (24.0 *. 60.0) in
let hour = hour *. 2.0 in
let hand t radius width color =
let x = int_of_float (radius *. sin t)
and y = int_of_float (radius *. cos t) in
set_line_width width;
set_color color;
moveto (w/2) (h/2); rlineto x y;
in
hand sec 90.0 2 (rgb 0 128 255);
hand min 82.0 4 (rgb 0 0 128);
hand hour 72.0 6 (rgb 255 0 128);
synchronize ();
Unix.sleep 1;
loop ()
in
try loop ()
with _ -> close_graph ()
[edit] GTK + Cairo
Using the libraries GTK2 and Cairo and their OCaml bindings LablGTK and ocaml-cairo.
# compile with:
ocamlopt -I +lablgtk2 -I +cairo -o gtkclock.opt \
unix.cmxa lablgtk.cmxa cairo.cmxa cairo_lablgtk.cmxa gtkInit.cmx gtkclock.ml
let pi = 4.0 *. atan 1.0
let angle v max = float v /. max *. 2.0 *. pi
let draw area _ =
let cr = Cairo_lablgtk.create area#misc#window in
let { Gtk.width = width; Gtk.height = height } = area#misc#allocation in
let scale p = float (min width height) *. 0.5 *. p in
let center_x, center_y = float width /. 2.0, float height /. 2.0 in
let invert_y y = float height -. y in
Cairo.set_source_rgb cr 0.8 0.8 0.8;
Cairo.paint cr; (* background *)
Cairo.set_source_rgb cr 1.0 1.0 1.0;
Cairo.arc cr center_x center_y (scale 0.9) 0.0 (2.0 *. pi);
Cairo.set_line_width cr (scale 0.02);
Cairo.stroke cr;
let point a =
let radius = (scale 0.9) in
let x = radius *. sin a
and y = radius *. cos a in
let r = scale 0.04 in
Cairo.arc cr (center_x +. x) (invert_y (center_y +. y)) r 0.0 (2.0 *. pi);
Cairo.fill cr;
in
for i = 0 to pred 12 do
point (angle (i * 30) 360.0)
done;
let tm = Unix.localtime (Unix.gettimeofday ()) in
let sec = angle tm.Unix.tm_sec 60.0 in
let min = angle tm.Unix.tm_min 60.0 in
let hour = angle (tm.Unix.tm_hour * 60 + tm.Unix.tm_min) (12.0 *. 60.0) in
Cairo.set_line_cap cr Cairo.LINE_CAP_ROUND;
let hand t radius lwidth (r, g, b) =
let x = radius *. sin t
and y = radius *. cos t in
Cairo.set_line_width cr (scale lwidth);
Cairo.move_to cr center_x center_y;
Cairo.line_to cr (center_x +. x) (invert_y (center_y +. y));
Cairo.set_source_rgb cr r g b;
Cairo.stroke cr;
in
hand sec (scale 0.9) 0.04 (0.0, 0.5, 1.0);
hand min (scale 0.7) 0.06 (0.0, 0.0, 0.5);
hand hour (scale 0.6) 0.09 (1.0, 0.0, 0.5);
true
let animate area =
ignore (GMain.Timeout.add 200 (fun () ->
GtkBase.Widget.queue_draw area#as_widget; true))
let () =
let w = GWindow.window ~title:"OCaml GtkCairo Clock" () in
ignore (w#connect#destroy GMain.quit);
let f = GBin.frame ~shadow_type:`IN ~packing:w#add () in
let area = GMisc.drawing_area ~width:200 ~height:200 ~packing:f#add () in
area#misc#set_double_buffered true;
ignore (area#event#connect#expose (draw area));
animate area;
w#show ();
GMain.main ()
[edit] PicoLisp
This is an animated ASCII drawing of the "Berlin-Uhr", a clock built to display the time according to the principles of set theory, which is installed in Berlin since 1975. See www.surveyor.in-berlin.de/berlin/uhr/indexe.html and www.cs.utah.edu/~hatch/berlin_uhr.html.
(de draw Lst
(for L Lst
(for X L
(cond
((num? X) (space X))
((sym? X) (prin X))
(T (do (car X) (prin (cdr X)))) ) )
(prinl) ) )
(de bigBox (N)
(do 2
(prin "|")
(for I 4
(prin (if (> I N) " |" " ======== |")) )
(prinl) ) )
(call 'clear) # Clear screen
(call "tput" "civis") # Set cursor invisible
(push '*Bye '(call "tput" "cnorm")) # Set cursor visible on exit
(loop
(call "tput" "cup" 0 0) # Cursor to top left
(let Time (time (time))
(draw (20 (5 . _)) (19 / 5 \\))
(if (onOff (NIL))
(draw (18 / 7 \\) (18 \\ 7 /))
(draw (18 / 2 (3 . "#") 2 \\) (18 \\ 2 (3 . "#") 2 /)) )
(draw
(19 \\ (5 . _) /)
(+ (10 . -) + (10 . -) + (10 . -) + (10 . -) +) )
(bigBox (/ (car Time) 5))
(draw (+ (10 . -) + (10 . -) + (10 . -) + (10 . -) +))
(bigBox (% (car Time) 5))
(draw (+ (43 . -) +))
(do 2
(prin "|")
(for I `(range 5 55 5)
(prin
(cond
((> I (cadr Time)) " |")
((=0 (% I 3)) " # |")
(T " = |") ) ) )
(prinl) )
(draw (+ (43 . -) +))
(bigBox (% (cadr Time) 5))
(draw (+ (10 . -) + (10 . -) + (10 . -) + (10 . -) +)) )
(wait 1000) )
The six '#' characters in the "circle" on top toggle on/off every second. This is the display at 17:46:
_____
/ \
/ ### \
\ ### /
\_____/
+----------+----------+----------+----------+
| ======== | ======== | ======== | |
| ======== | ======== | ======== | |
+----------+----------+----------+----------+
| ======== | ======== | | |
| ======== | ======== | | |
+-------------------------------------------+
| = | = | # | = | = | # | = | = | # | | |
| = | = | # | = | = | # | = | = | # | | |
+-------------------------------------------+
| ======== | | | |
| ======== | | | |
+----------+----------+----------+----------+
[edit] PureBasic
#MiddleX = 90 + 1 ;x,y must be odd numbers, minimum width is 67
#MiddleY = #MiddleX
#len_sh = (#MiddleX - 8) * 0.97 ;length of second-hand
#len_mh = (#MiddleX - 8) * 0.88 ;length of minute-hand
#len_hh = (#MiddleX - 8) * 0.66 ;length of hour-hand
#clockFace_img = 0
#clock_gad = 0
#clock_win = 0
Define cx = #MiddleX, cy = #MiddleY, i, ri.f
Define c_gray = RGB($CC, $CC, $CC), c_mgray = RGB($99, $99, $99)
Define c_white = RGB(255, 255, 255), c_black =RGB(0, 0, 0)
Define c_red = RGB(255, 0, 0), c_blue = RGB(0, 0, 255)
Define c_dcyan = RGB($27, $BC, $D8), c_lgreen = RGB($60, $E0, $9)
Define c_yellow = RGB($F4, $D5, $0B)
CreateImage(#clockFace_img, cx * 2 - 1, cy * 2 - 1)
StartDrawing(ImageOutput(#clockFace_img))
Box(0, 0, cx * 2 - 1, cy * 2 - 1, c_mgray)
Circle(cx, cy, cx - 2, c_dcyan)
For i = 0 To 359 Step 30
ri = Radian(i)
Circle(cx + Sin(ri) * (cx - 5), cy + Cos(ri) * (cx - 5), 3, c_gray)
Next
StopDrawing()
OpenWindow(#clock_win, 0, 0, cx * 2, cy * 2, "Clock")
ImageGadget(#clock_gad, 0, 0, cx * 2, cy * 2, ImageID(#clockFace_img))
Define x, y, rad_s.f, rad_m.f, rad_h.f, t$
Repeat
event = WaitWindowEvent(25)
If event = 0
rad_s = Radian(360 - (Second(Date()) * 6) + 180)
rad_m = Radian(360 - (Minute(Date()) * 6) + 180)
rad_h = Radian(360 - (((Hour(Date()) - 1) * 30) + 180) - (Minute(Date()) / 2))
StartDrawing(ImageOutput(#clockFace_img))
Circle(cx, cy, cx - 8, c_lgreen)
t$ = FormatDate("%mm-%dd-%yyyy", Date())
x = cx - (TextWidth(t$) + 2) / 2
y = (cy - (TextHeight(t$) + 2) - 4) / 2
Box(x, y, TextWidth(t$) + 2, TextHeight(t$) + 2, c_black)
DrawText(x + 2, y + 2, t$, c_black, c_yellow)
LineXY(cx, cy, cx + Sin(rad_s) * #len_sh, cy + Cos(rad_s) * #len_sh, c_white)
LineXY(cx, cy, cx + Sin(rad_m) * #len_mh, cy + Cos(rad_m) * #len_mh, c_red)
LineXY(cx, cy, cx + Sin(rad_h) * #len_hh, cy + Cos(rad_h) * #len_hh, c_black)
Circle(cx, cy, 4, c_blue)
StopDrawing()
SetGadgetState(#clock_gad, ImageID(#clockFace_img))
EndIf
Until event = #PB_Event_CloseWindow
[edit] Python
import time
def chunks(l, n=5):
return [l[i:i+n] for i in range(0, len(l), n)]
def binary(n, digits=8):
n=int(n)
return '{0:0{1}b}'.format(n, digits)
def secs(n):
n=int(n)
h='x' * n
return "|".join(chunks(h))
def bin_bit(h):
h=h.replace("1","x")
h=h.replace("0"," ")
return "|".join(list(h))
x=str(time.ctime()).split()
y=x[3].split(":")
s=y[-1]
y=map(binary,y[:-1])
print bin_bit(y[0])
print bin_bit(y[1])
print secs(s)
[edit] Racket
Draws an analog clock in a new GUI window:
#lang racket/gui
(require racket/date slideshow/pict)
(define (clock h m s [r 100])
(define (draw-hand length angle
#:width [width 1]
#:color [color "black"])
(dc (λ (dc dx dy)
(define old-pen (send dc get-pen))
(send dc set-pen (new pen% [width width] [color color]))
(send dc draw-line
(+ dx r) (+ dy r)
(+ dx r (* length (sin angle)))
(+ dy r (* length (cos angle))))
(send dc set-pen old-pen))
(* 2 r) (* 2 r)))
(cc-superimpose
(for/fold ([pict (circle (* 2 r))])
([angle (in-range 0 (* 2 pi) (/ pi 6))]
[hour (cons 12 (range 1 12))])
(define angle* angle)
(define r* (* r 0.8))
(define txt (text (number->string hour) '(bold . "Helvetica")))
(define x (- (* r* (sin angle*)) (/ (pict-width txt) 2)))
(define y (+ (* r* (cos angle*)) (/ (pict-height txt) 2)))
(pin-over pict (+ r x) (- r y) txt))
(draw-hand (* r 0.7) (+ pi (* (modulo h 12) (- (/ pi 6))))
#:width 3)
(draw-hand (* r 0.5) (+ pi (* m (- (/ pi 30))))
#:width 2)
(draw-hand (* r 0.7) (+ pi (* s (- (/ pi 30))))
#:color "red")
(disk (* r 0.1))))
(define f (new frame% [label "Clock"] [width 300] [height 300]))
(define c
(new canvas%
[parent f]
[paint-callback
(λ (c dc)
(define date (current-date))
(draw-pict (clock (date-hour date)
(date-minute date)
(date-second date)
(/ (send c get-width) 2))
dc 0 0))]))
(define t
(new timer%
[notify-callback (λ () (send c refresh-now))]
[interval 1000]))
(send f show #t)
[edit] REXX
This REXX program draws a digital clock; it shows the seconds if the terminal screen is
wide enough.
The $T.REX program does the heavy lifting of actually creating the blocked characters.
If using
- PC/REXX
- Personal REXX
- R4
- ROO
the color of the display can be specified.
The $CLOCK.REX REXX program makes use of $T REXX program which is used to display text and/or create big blocked characters.
The $T.REX REXX program is included here ──► $T.REX.
The help for the $T.REX REXX program is included here ──► $T.HEL.
The $CLOCK.REX REXX program makes use of $ERR.REX REXX program which is used to display error messages (via $T.REX).
The $ERR REXX program is included here ──► $ERR.REX.
Some older REXXes don't have a changestr BIF, so one is included here ──► CHANGESTR.REX.
REXX programs not included are $H.REX which shows help and other documentation.
/**/trace o;parse arg !;if !all(arg()) then exit;if !cms then address ''
signal on halt; signal on novalue; signal on syntax
parse var ! ops; ops = space(ops) /*obtain command line options. */
@abc = 'abcdefghijklmnopqrstuvwxyz' /*alphabet str used by ABB/ABBN. */
blinkSecs = 1
creep = 1
tops = '.C=blue .BC=░ .BS=1 .BLOCK=12'
do while ops\==''; parse var ops _1 2 1 _ . 1 y ops; upper _
select
when _==',' then nop
when _1=='.' & pos("=",_)\==0 then tops=tops y
when abbn('BLINKSECs') then blinksecs=no()
when abbn('CREEPs') then creep=no()
otherwise call er 55,y
end /*select*/
end /*while ops¬==''*/
if \!pcrexx then blinkSecs=0 /*if ¬PC/REXX, turn off BLINKSECS*/
tops=space(tops) /*elide extraneous TOPS blanks.*/
parse value scrsize() with sd sw . /*get the term screens dimensions*/
oldTime=
do until queued()\==0
ct=time(); mn=substr(ct,4,2); ss=right(ct,2); i_=0; p_=0
call blinksec
if ct==oldTime then if !cms then 'CP SLEEP'; else call delay 1
if creep then do; p_ = 3 + right(mn,1)
if sd>26 then p_ = p_ + left(mn,1)
if sd>33 then p_ = p_ + left(mn,1)
if sd>44 then p_ = p_ + left(mn,1) +right(mn,1)
end
_p=-p_
i_=2+left(ct,1); ctt=left(ct,5); if sw>108 then ctt=ct
r=$t('.P='_p ".I="i_ tops ctt); if r\==0 then leave
oldTime=time()
end /*forever*/
exit /*stick a fork in it, we're done.*/
/*═════════════════════════════general 1-line subs════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════*/
!all:!!=!;!=space(!);upper !;call !fid;!nt=right(!var('OS'),2)=='NT';!cls=word('CLS VMFCLEAR CLRSCREEN',1+!cms+!tso*2);if arg(1)\==1 then return 0;if wordpos(!,'? ?SAMPLES ?AUTHOR ?FLOW')==0 then return 0;!call=']$H';call '$H' !fn !;!call=;return 1
!cal: if symbol('!CALL')\=="VAR" then !call=;return !call
!env: !env='ENVIRONMENT';if !sys=='MSDOS'|!brexx|!r4|!roo then !env='SYSTEM';if !os2 then !env='OS2'!env;!ebcdic=1=='f0'x;return
!fid: parse upper source !sys !fun !fid . 1 . . !fn !ft !fm .;call !sys;if !dos then do;_=lastpos('\',!fn);!fm=left(!fn,_);!fn=substr(!fn,_+1);parse var !fn !fn '.' !ft;end;return word(0 !fn !ft !fm,1+('0'arg(1)))
!rex: parse upper version !ver !vernum !verdate .;!brexx='BY'==!vernum;!kexx='KEXX'==!ver;!pcrexx='REXX/PERSONAL'==!ver|'REXX/PC'==!ver;!r4='REXX-R4'==!ver;!regina='REXX-REGINA'==left(!ver,11);!roo='REXX-ROO'==!ver;call !env;return
!sys: !cms=!sys=='CMS';!os2=!sys=='OS2';!tso=!sys=='TSO'|!sys=='MVS';!vse=!sys=='VSE';!dos=pos('DOS',!sys)\==0|pos('WIN',!sys)\==0|!sys=='CMD';call !rex;return
!var: call !fid;if !kexx then return space(dosenv(arg(1)));return space(value(arg(1),,!env))
$t: !call=']$T';call "$T" arg(1);!call=;return result
abb: arg abbu;parse arg abb;return abbrev(abbu,_,abbl(abb))
abbl: return verify(arg(1)'a',@abc,'M')-1
abbn: parse arg abbn;return abb(abbn)|abb('NO'abbn)
blinksec: if \blinksecs then return;bsec=' ';ss2=right(ss,2);if sw<=80 then bsec=copies(' ',2+ss2) ss2;call scrwrite 1+right(mn,1),1,bsec,,,1;call cursor sd-right(mn,1),sw-length(bsec);return
er: parse arg _1,_2;call '$ERR' "14"p(_1) p(word(_1,2) !fid(1)) _2;if _1<0 then return _1;exit result
err: call er '-'arg(1),arg(2);return ''
erx: call er '-'arg(1),arg(2);exit ''
halt: call er .1
no: if arg(1)\=='' then call er 01,arg(2);return left(_,2)\=='NO'
novalue:!sigl=sigl;call er 17,!fid(2) !fid(3) !sigl condition('D') sourceline(!sigl)
p: return word(arg(1),1)
syntax:!sigl=sigl;call er 13,!fid(2) !fid(3) !sigl !cal() condition('D') sourceline(!sigl)
output
░░░░░░░░ ░░░░░░░░ ░░ ░░░░░░░░░░ ░░░ ░░░░░░░░░░
░░░░░░░░░░ ░░░░░░░░░░ ░░░ ░░░░░░░░░░ ░░░░ ░░░░░░░░░░
░░ ░░ ░░ ░░ ░░░░ ░░ ░░ ░░ ░░
░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░
░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░
░░ ░░░ ░░ ░░░░░░░ ░░░░░░░░░░ ░░░░░░░
░░ ░░░ ░░ ░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░
░░ ░░ ░░ ░░ ░░ ░░
░░ ░░ ░░ ░░ ░░ ░░
░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░
░░░░░░░░░░ ░░░░░░░░░░ ░░ ░░░░░░ ░░░░░░░░░░ ░░ ░░░░ ░░░░░░░░░░
░░░░░░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░░░░ ░░░░ ░░░░░░░░
output (when the terminal screen is less then 109 bytes)
░░░░░░░░ ░░░░░░░░ ░░░░░░░░ ░░░░░░░░░░
░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░
░░ ░░ ░░ ░░ ░░ ░░ ░░
░░ ░░ ░░ ░░ ░░ ░░ ░░
░░ ░░ ░░ ░░ ░░
░░ ░░░ ░░ ░░░░░░░
░░ ░░░ ░░ ░░░░░░░░
░░ ░░ ░░ ░░
░░ ░░ ░░ ░░
░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░░
░░░░░░░░░░ ░░░░░░░░░░ ░░ ░░░░░░░░░░ ░░░░░░░░░░
░░░░░░░░░░ ░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░
[edit] Ruby
Shoes.app(:width=>205, :height => 228, :title => "A Clock") do
def draw_ray(width, start, stop, ratio)
angle = Math::PI * 2 * ratio - Math::PI/2
strokewidth width
cos = Math::cos(angle)
sin = Math::sin(angle)
line 101+cos*start, 101+sin*start, 101+cos*stop, 101+sin*stop
end
def update
t = Time.now
@time.text = t.strftime("%H:%M:%S")
h, m, s = (t.hour % 12).to_f, t.min.to_f, t.sec.to_f
s += t.to_f - t.to_i # add the fractional seconds
@hands.clear do
draw_ray(3, 0, 70, (h + m/60)/12)
draw_ray(2, 0, 90, (m + s/60)/60)
draw_ray(1, 0, 95, s/60)
end
end
# a place for the text display
@time = para(:align=>"center", :family => "monospace")
# draw the clock face
stack(:width=>203, :height=>203) do
strokewidth 1
fill gradient(deepskyblue, aqua)
oval 1, 1, 200
fill black
oval 98, 98, 6
# draw the minute indicators
0.upto(59) {|m| draw_ray(1, (m % 5 == 0 ? 96 : 98), 100, m.to_f/60)}
end.move(0,23)
# the drawing area for the hands
@hands = stack(:width=>203, :height=>203) {}.move(0,23)
animate(5) {update}
end
Inspired by the PicoLisp solution, here's an implementation of the Berlin-Uhr clock.
Shoes.app(:title => "Berlin-Uhr Clock", :width => 209, :height => 300) do
background lightgrey
Red = rgb(255, 20, 20)
Yellow = rgb(173, 255, 47)
Green = rgb(154, 205, 50)
Gray = rgb(128, 128, 128)
@time = para(:align => "center")
stack do
fill Gray
stroke black
strokewidth 2
@seconds = oval 75, 3, 50
@hrs_a = 4.times.collect {|i| rect 51*i, 56, 48, 30, 4}
@hrs_b = 4.times.collect {|i| rect 51*i, 89, 48, 30, 4}
@mins_a = 11.times.collect {|i| rect 2+18*i, 122, 15, 30, 4}
@mins_b = 4.times.collect {|i| rect 51*i, 155, 48, 30, 4}
# some decoration
fill white
stroke darkslategray
rect -10, -30, 75, 70, 10
rect 140, -30, 75, 70, 10
rect -13, 192, 105, 100, 10
rect 110, 192, 105, 100, 10
end.move(3,20)
animate(1) do
now = Time.now
@time.text = now.strftime("%H:%M:%S")
@seconds.style(:fill => now.sec.even? ? Green : Gray)
a, b = now.hour.divmod(5)
4.times {|i| @hrs_a[i].style(:fill => i < a ? Red : Gray)}
4.times {|i| @hrs_b[i].style(:fill => i < b ? Red : Gray)}
a, b = now.min.divmod(5)
11.times {|i| @mins_a[i].style(:fill => i < a ? (i%3==2 ? Red : Yellow) : Gray)}
4.times {|i| @mins_b[i].style(:fill => i < b ? Yellow : Gray)}
end
keypress do |key|
case key
when :control_q, "\x11" then exit
end
end
end
[edit] Run BASIC
' --------------------------------------------
' clock. I got nothing but time
' ---------------------------------------------
n = 12 ' num of points
r = 95 ' radius
pi = 22/7
alpha = pi * 2 / n
dim points(n)
graphic #g2, 200, 200
' --------------------------------------
' Draw the clock
' --------------------------------------
#g2 size(2) 'pen size
#g2 down()
#g2 font("arial", 20, "bold")
#g2 place(85,30)
#g2 "\12"
#g2 place(170,105)
#g2 "\3"
#g2 place(10,105)
#g2 "\9"
#g2 place(90,185)
#g2 "\6"
for i = 0 to n - 1
theta = alpha * i
px = cos( theta ) * r
py = sin( theta ) * r
px = px + 100
py = py + 100
#g2 place(px,py)
#g2 circle(2)
next i
[shoTime]
' -------------------------
' clear previous sec,min,hr
' -------------------------
r = 63
p = se
#g2 color("white")
gosub [h2Dot]
r = 50
p = mi
#g2 color("white")
gosub [h2Dot]
r = 30 ' radius
p = hr * 5
#g2 color("white")
gosub [h2Dot]
' -------------------------
' Show new time
' -------------------------
a$ = time$()
hr = val(word$(a$,1,":"))
mi = val(word$(a$,2,":"))
se = val(word$(a$,3,":"))
' put time on the clock - gimme a hand
#g2 size(4)
' second hand
n = 60
r = 63
p = se
#g2 color("blue")
gosub [h2Dot]
' minute hand
r = 50
p = mi
#g2 color("green")
gosub [h2Dot]
' hour hand
r = 30 ' radius
p = hr * 5
#g2 color("red")
gosub [h2Dot]
render #g2
end
' a one liner
[h2Dot]
alpha = pi * 2 / n
i = p - 15
theta = alpha * i
px = cos( theta ) * r
py = sin( theta ) * r
px = px + 100
py = py + 100
#g2 place(px,py)
#g2 circle(2)
#g2 line(100,100,px,py)
RETURN
[edit] 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
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.


