#!/usr/local/bin/perl -w
#
# This most entertaining program was written, in Tcl/Tk, by Scott Hess
# (shess@winternet.com).  It's a clock that uses a bezier curve anchored
# at four points -- the hour position, the minute position, the second
# position and the center of the clock -- to show the time.
#
# <Button-1> switches between display modes, and <Button-3> switches
# between line thicknesses.
#
# Perl/Tk version by me, Stephen.O.Lidie@Lehigh.EDU, 2000/02/05.

use POSIX qw/asin/;
use Tk;
use subs qw/buildclock hands updateclock/;
use vars qw/$clock %hand $mw $pi180 $resize/;
use strict;

%hand = (
    'hour'    => 0.40,
    'minute'  => 0.75,
    'second'  => 0.85,
    '0'       => 0.00,
    'intick'  => 0.95,
    'outtick' => 1.00,
    'width'   => 0.05,
    'scale'   => 100,

    'type'    => 'bezier',
    'types'   => [qw/normal curve angle bezier/],
    'typei'   => 3,
    'normal'  => [qw/minute 0 0 second 0 0 hour 0 0 minute/],
    'curve'   => [qw/minute 0 second 0 hour 0 minute/],
    'bezier'  => [qw/minute second 0 hour/],
    'angle'   => [qw/minute second second hour/],
    
    'tick'    => [qw/intick outtick/],
);
$pi180 = asin(1) / 90.0;
$resize = 0;

$mw = MainWindow->new;
$clock = $mw->Canvas(qw/-width 200 -height 200/);
$clock->pack(qw/-expand 1 -fill both/);
$mw->bind('<Configure>' => \&buildclock);
$mw->bind('<Button-1>'  => \&incrtype);
$mw->bind('<Button-3>'  => \&incrwidth);
buildclock;
$mw->repeat(1000 => \&updateclock);
MainLoop;

sub buildclock  {

    # Build the clock.  Puts tickmarks every 30 degrees, tagged
    # "ticks", and prefills the "hands" line.

    my $pi180 = asin(1)/90.0;
    
    Tk::catch {$clock->delete('marks')};
    $clock->update;
    my $w = $clock->width;
    $mw->geometry("${w}x${w}") if $resize; # ensure clock is square
    $resize++;
    $hand{'scale'} = $w / 2.0;
    
    # This is a horrid hack.  Use the hands() procedure to
    # calculate the tickmark positions by temporarily changing
    # the clock type.
    
    my $type = $hand{'type'};
    $hand{'type'} = 'tick';
    my %angles;
    for (my $ii = 0; $ii < 12; $ii++) {
	$angles{'intick'} = $angles{'outtick'} = $ii * 30 * $pi180;
	$clock->createLine(hands(\%angles), -tags => [qw/ticks marks/]);
    }
    $hand{'type'} =  $type;
    
    $clock->createLine(qw/0 0 0 0 -smooth 1 -tags/ => [qw/hands marks/]);
    $clock->itemconfigure(qw/marks -capstyle round -width/ =>
			  $hand{'width'} * $hand{'scale'});
}

sub hands {

    # Calculate the set of points for the current hand type and
    # the angles in the passed array.

    my($aa) = @_;

    my $ss = $hand{'scale'};
    my @points;
    foreach my $desc ( @{ $hand{$hand{'type'}} } ) {
	push @points, sin($aa->{$desc}) * $hand{$desc} * $ss + $ss;
	push @points, $ss - cos($aa->{$desc}) * $hand{$desc} * $ss;
    }
    #print join(', ', @points), "\n";
    return @points;
}

sub incrtype {
    $hand{'type'} = $hand{'types'}->[++$hand{'typei'} % @{$hand{'types'}}];
}

sub incrwidth {
    my $w = $hand{'width'} + .05;
    $hand{'width'} = $w > .25 ? 0 : $w;
    $clock->itemconfigure('marks', -width => $hand{'width'} * $hand{'scale'});
}

sub setclock {

    # Calculate the angles for the second, minute, and hour hands,
    # and then update the clock hands to match.

    my($hour, $minute, $second) = @_;

    my %angles;
    $angles{'0'} =        0;
    $angles{'second'} = $second *  6 * $pi180;
    $angles{'minute'} = $minute *  6 * $pi180;
    $angles{'hour'}   = $hour   * 30 * $pi180 + $angles{'minute'} / 12;

    my $sector  = int( $angles{'second'} + 0.5 );
    my(@colors) = qw/cyan green blue purple red yellow orange/;
    $clock->itemconfigure(qw/hands -fill/ => $colors[$sector]);

    $clock->coords('hands',  hands \%angles);
}

sub updateclock {

    # Draw the clock for the current time.

    my(@t) = localtime;
    setclock @t[2,1,0];
}
