24 Dec 2009 ruoso   » (Journeyer)

Analog ASCII Clock

Today I started the day looking at this post that inspired me to write an ascii analog clock in Perl.

Unlike the linked post, I didn't intend to golf, but rather writing a clean and readable code.

Follows the code:

#!/usr/bin/perl

# besides DateTime, the standard pragmas
use 5.010;
use strict;
use warnings;
use utf8;
use DateTime;

# initialize configuration variables
my $width = 32;
my $height = 32;
my $mark_at = 15;
my $length_m = 12;
my $length_h = 8;
my $length_s = 15;
my $pi = 3.14159265;
my $center_x = int(0.5 + ($width / 2));
my $center_y = int(0.5 + ($height / 2));
my $loop = 1;

# clear the screen and position the cursor in the left top corner
print "\x1b[0;0H\x1b[2J";

# loop the rendering of the clock
while ($loop && sleep 1) {

  # create a matrix of the configured height and width with blanks
  # I use two spaces because most console fonts have the height with
  # twice the width
 my $data = [map {[ map { '  ' } 1..$width ]} 1..$height];

  # get the date and time in the local time zone
  my $now = DateTime->now(time_zone => 'local');
  my $hour = $now->hour_12;
  my $min = $now->minute;
  my $sec = $now->second;

  # convert that to degrees for the hands of that clock
  my $degree_m = ($min * 6) + 270;
  my $degree_h = ($hour * 30) + 270;
  my $degree_s = ($sec * 6) + 270;

  # see the place sub down there
  # place the numbers 1 to 12 around the clock
  place($data,($_ * 30) + 270,$mark_at,sprintf("%02d",$_)) for 1..12;

  # now the seconds hand
  place($data,$degree_m, $_ , '<>') for 0..$length_m;
  
  # the hours hand
  place($data,$degree_h, $_ , '::') for 0..$length_h;

  # the seconds hand
  place($data,$degree_s, $_ , '..') for 0..$length_s;

  # mark the center o the clock
  $data->[$center_y][$center_x] = 'OO';

  # position the cursor in the left top corner
  print "\x1b[0;0H\n";
  
  # covert the matrix into a single string and print it
  say join "\n", map { join '', @$_ } @$data;
};

# covert degrees and distance into X and Y coords using basic trigonometry
sub vec2xy {
  my ($deg, $len) = @_;
  return $center_x + int(0.5 + ($len * cos($deg * ($pi/180)))),
    $center_y + int(0.5 + ($len * sin($deg * ($pi/180))));
}

# place a string into some degree and distance
sub place {
  my ($data, $deg, $len, $txt) = @_;
  my ($x,$y) = vec2xy($deg,$len);
  $data->[$y][$x] = $txt;
}

Syndicated 2009-12-14 13:37:02 from Daniel Ruoso

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!