Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

ASCII analog clock

by polettix (Vicar)
on Mar 18, 2005 at 18:33 UTC ( [id://440771]=sourcecode: print w/replies, xml ) Need Help??
Category: Fun Stuff
Author/Contact Info Flavio Poletti <flavio@NO-SPAM-PLEASEpolettix.it>
Description: Something that any terminal should not miss in their spare time...

A quite simple analog clock in ASCII. I made it for fun in little time, so there's no optimisation (for example, I know better algos to write a line, but I was too lazy to get to my copy of Foley/Van Dam...).

You can set the clock size (x and y) on the command line; defaults to 45 x 23.

Updated: added screen clearing using one advice from here and fairer input usage. I tested clearing in xterm and cygwin window and it seems to work well.

#!/usr/bin/perl

use strict;
use warnings;

#This will let me handle the output as a simple matrix.
# A simple screen abstraction, actually a long array
package Screen;
sub new { # Get a Screen of given sizes
   my ($class, $wx, $wy) = @_;
   my $self = {_wx => $wx,
               _wy => $wy,
               _data => [ ((' ') x $wx , "\n") x $wy ]};
   bless $self, $class;
}

sub _index { # Find index of given coordinate into vector
    my ($self, $x, $y) = @_;
    return $x + ($y * (1 + $self->{_wx}));
}

# Public methods
sub inbound { # Test if coordinates are within Screen bounds
   my ($self, $x, $y) = @_;
   return ($x >= 0 && $y >= 0 && $x < $self->{_wx} && $y < $self->{_wy
+});
}

sub set { # Set value $v at given coordinates, with clipping
   my ($self, $x, $y, $v) = @_;
   return undef unless $self->inbound($x, $y);
   $self->{_data}->[$self->_index($x, $y)] = $v;
}

sub to_string { # Dump Screen onto string
    return join('', @{$_[0]->{_data}});
}

#Back to reality. The main cycle gets a new ASCII clock representation
+ every second and prints it.

package main;
use POSIX 'ceil';
my $pi2 = 2 * atan2(0, -1); # Useful to have around

my ($wx, $wy) = @ARGV; # Accept sizes from command line

while (1) { # Cycle forever, updating each second
   print "\e[2J", anaclock($wx, $wy);
   sleep(1);
}

#Accessor functions. The adjust function rounds numbers to have intege
+r matrix indexes;
# in the meanwhile, it also performs a translation by 1 cell - please 
+don't ask me why,
# it works :) 
#The two drawing functions do what they declare.

# Round and translate to better fit inside the screen
sub adjust {return ceil($_[0] - 1.5);}

sub draw_circle {
   my ($screen, $cx, $cy, $rx, $ry, $c) = @_;
   my $radius = ($rx > $ry) ? $rx : $ry;
   my $step = abs(1 / ($radius * $pi2));
   for (my $a = 0.0; $a < 2 * $pi2; $a += $step) {
      $screen->set(adjust($cx + $rx * cos($a)), 
                   adjust($cy + $ry * sin($a)), $c);
   }
}

sub draw_line {
   my ($screen, $ax, $ay, $bx, $by, $c) = @_;
   my ($dx, $dy) = ($bx - $ax, $by - $ay);
   my ($adx, $ady) = (abs($dx), abs($dy));
   my $delta = ($adx > $ady) ? $adx : $ady;
   $dx /= $delta;
   $dy /= $delta;
   for (; $delta > 0; $ax += $dx, $ay += $dy, --$delta) {
      $screen->set(adjust($ax), adjust($ay), $c);
   }
}

#The actual ASCII clock building function. It accepts width and heigth
+ even 
# if it increases them by 1 when building the screen- I hope you're re
+ally not
# going to be annoyed because of this.

sub anaclock {
   my $width  = shift || 45;
   my $height = shift || 23;

   --$width; --$height; # Too lazy to change code after...

   my ($rx, $ry) = ($width / 2, $height / 2);
   my ($cx, $cy) = (1 + $rx, 1 + $ry);

   # Get a virtual screen to write onto; get it a little larger
    # to cope with roundups
   my $screen = Screen->new($width + 1, $height + 1);

   # Draw the surronding circle
   draw_circle($screen, $cx, $cy, $rx, $ry, '.');

   # Decrease radius to be strictly inside
   $rx *= 6/8;
   $ry *= 6/8;

   # What time is it?
   my ($sec, $min, $hour) = (localtime(time))[0 .. 2];

   # Scale values to get angles. Note that $min is shifted by the
   # seconds, and $hour is shifted by the minutes.
   $sec  *= $pi2 / 60;
   $min  = ($min * $pi2 + $sec) / 60;
   $hour = (($hour % 12) * $pi2 + $min) / 12;
   
   # Draw lines. According to most clocks, hours are in background and
    # seconds in foreground.
   draw_line($screen, $cx, $cy, $cx + $rx * sin($hour) * 2 / 3,
      $cy - $ry * cos($hour) * 2 / 3, '#');
   draw_line($screen, $cx, $cy, $cx + $rx * sin($min), 
      $cy - $ry * cos($min), '+');
   draw_line($screen, $cx, $cy, $cx + $rx * sin($sec), 
      $cy - $ry * cos($sec), '.');

   # Return a string representation
   return $screen->to_string();
}
Replies are listed 'Best First'.
Re: ASCII analog clock
by zentara (Archbishop) on Mar 19, 2005 at 12:59 UTC
    Pretty Neat. but I experience 2 problems with it on linux. I am using the framebuffer console. I wonder if it's the framebuffer?

    1. On my monitor, in console mode, it dosn't jitter and looks nice, except the "bottom" of the clock is reflected above the clock. I see 1 and a half clocks.

    2. Most annoying. When run in an xterm, every second, the whole clock jitters up and down 1 line. Very hard to look at. I also see 1 and a half clock in the xterm, if I am at nearly fullscreen size.


    I'm not really a human, but I play one on earth. flash japh
      That's pretty strange to me, because I actually tested it inside an xterm.

      Anyway, the real core function produces a text string with newlines that simply gets printed in the main while loop. I actually don't know how to clear the terminal screen from inside perl, so the best I could afford was to simply scroll. To solve the console mode problem, the best you can do is probably to increase the clock height (which is the second parameter you provide to the script).

      I've developed a version that uses Tk to pop up an ad-hoc window for the clock, but I actually find it quite unuseful to have to pop up a graph window for an ascii-based clock!

      If I find some solution for the screen clear which does not require strange modules I'll change the implementation and warn you, be sure. In the meantime, thanks for your feedback.

      Flavio

      -- Don't fool yourself.
Re: ASCII analog clock
by chanio (Priest) on Mar 22, 2005 at 04:52 UTC
Re: ASCII analog clock
by aubell (Novice) on Mar 30, 2005 at 10:47 UTC
    It's perfect!En,looks good. (Run on ActivePerl && Win32)
    Aubell:Sky is blue

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://440771]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-25 15:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found