Project-Gantt
view release on metacpan or search on metacpan
Gantt/Gantt/TimeSpan.pm view on Meta::CPAN
##########################################################################
#
# File: Project/Gantt/TimeSpan.pm
#
# Author: Alexander Westholm
#
# Purpose: This class is a visual representation of a timespan on
# a Gantt chart. It is used to display both sub-projects,
# and the tasks they contain.
#
# Client: CPAN
#
# CVS: $Id: TimeSpan.pm,v 1.4 2004/08/03 17:56:52 awestholm Exp $
#
##########################################################################
package Project::Gantt::TimeSpan;
use strict;
use warnings;
use Project::Gantt::Globals;
##########################################################################
#
# Method: new(%opts)
#
# Purpose: Constructor. Takes as parameters the Task or Gantt object
# it is going to display, as well as the Class::Date object
# representing the beginning of the chart. In addition, the
# Image::Magick canvas is passed in, along with a skin object
# to customize the colors of this TimeSpan.
#
##########################################################################
sub new {
my $cls = shift;
my %ops = @_;
die "Must provide proper args to TimeSpan!" if(not($ops{task} and $ops{rootStr}));
return bless {
task => $ops{task},
rootStr => $ops{rootStr},
canvas => $ops{canvas},
skin => $ops{skin},
beginX => 205,
}, $cls;
}
##########################################################################
#
# Method: Display(mode, height)
#
# Purpose: Calls _writeBar passing its parameters along. Simply a
# placeholder at this point, but exists incase some
# preprocessing is necessary at a later date.
#
##########################################################################
sub display {
my $me = shift;
my $mod = shift;
my $hgt = shift;
$me->_writeBar($mod, $hgt);
}
##########################################################################
#
# Method: _writeBar(mode, height)
#
# Purpose: This method calculates the distance from the beginning of
# the graph at which to begin drawing this TimeSpan, as well
# as how many pixels in width it should be. It then calls
# either _drawSubProj or _drawTask depending on whether
# the task object passed to the constructor is a
# Project::Gantt instance or a Project::Gantt::Task
# instance.
#
##########################################################################
sub _writeBar {
my $me = shift;
my $mode = shift;
my $height = shift;
my $tsk = $me->{task};
my $rootStart = $me->{rootStr};
my $taskStart = $tsk->getStartDate();
my $taskEnd = $tsk->getEndDate();
my $startX = $me->{beginX};
my $dif = $taskStart-$rootStart;
# calculate starting X coordinate based on number of units away from start it is
if($mode eq 'hours'){
$startX += $dif->hour * $DAYSIZE;
$startX += (($rootStart->min / 59) * $DAYSIZE);
Gantt/Gantt/TimeSpan.pm view on Meta::CPAN
}else{
$startX += $dif->day * $DAYSIZE;
$startX += (($rootStart->hour / 23) * $DAYSIZE);
}
my $endX = $startX;
my $edif = $taskEnd-$taskStart;
# range variable indicates whether or not space filled by this bar is less than 15 pixels or not
# this is because 15 pixels are required for the diamond shape... if less than, a rectangle
# is used
my $range = 0;
# calculate ending X coordinate based on number of units within this bar
if($mode eq 'hours'){
$endX += $edif->hour * $DAYSIZE;
$range = $edif->hour;
}elsif($mode eq 'months'){
my $tmp = $me->_getMonthPixels($taskStart, $taskEnd);
$endX += $tmp;
$range = $tmp / $DAYSIZE;
}else{
$endX += $edif->day * $DAYSIZE;
$range = $edif->day;
}
if($startX == $endX){
die "Incorrect date range!";
}
$me->_drawSubProj($startX, $height, $endX, $range) if $tsk->isa("Project::Gantt");
$me->_drawTask($startX, $height, $endX, $range) if $tsk->isa("Project::Gantt::Task");
}
##########################################################################
#
# Method: _getMonthPixels(start, end)
#
# Purpose: Given the start and end of a TimeSpan, as passed in, this
# method approximately calculates the number of pixels
# that it should take up on the Gantt chart. There are some
# minor errors occasionally, as this calculation is based on
# the number of seconds in the task divided by the number of
# seconds in the year. Since not every month has the same
# number of seconds, minor miscalculations will occur.
#
##########################################################################
sub _getMonthPixels {
my $me = shift;
my $birth = shift;
my $death = shift;
my $pixelsPerYr = 12 * 60;
my $secsInYear = (((60*60)*24)*365);
my $secsInSpan = ($death - $birth)->sec;
my $percentage = $secsInSpan / $secsInYear;
return $percentage * $pixelsPerYr;
}
##########################################################################
#
# Method: _drawTask(startX, startY, endX, range)
#
# Purpose: Given the starting coordinates, and ending X coordinate
# of a TimeSpan, uses Image::Magick to draw the span on the
# chart using whatever Skin scheme is in effect. Range is
# an indication of whether the span takes up more than
# 15 pixels or not. If so, the span is drawn as a diamond,
# if not, as a rectangle.
#
##########################################################################
sub _drawTask {
my $me = shift;
my $startX = shift;
my $startY = shift;
my $endX = shift;
my $range = shift;
my $canvas = $me->{canvas};
my $leadY = $startY+8.5;
my $bottom = $startY+13.5;
$startY += 3.5;
my $leadX = $startX+7.5;
my $trailX = $endX-7.5;
# if has space for full diamond
if($range >= 1){
$canvas->Draw(
fill => $me->{skin}->itemFill(),
stroke => $me->{skin}->itemFill(),
primitive => 'polygon',
points => "${startX}, $leadY ${leadX}, $bottom ${leadX}, $startY");
$canvas->Draw(
fill => $me->{skin}->itemFill(),
stroke => $me->{skin}->itemFill(),
primitive => 'polygon',
points => "${trailX}, $bottom ${trailX}, $startY ${endX}, $leadY");
# if space between diamond edges, fill in
if($leadX != $trailX){
$canvas->Draw(
fill => $me->{skin}->itemFill(),
stroke => $me->{skin}->itemFill(),
primitive => 'rectangle',
points => "${leadX}, $startY ${trailX}, $bottom");
}
# not enough space for full diamond, use rectangle
}else{
$canvas->Draw(
fill => $me->{skin}->itemFill(),
stroke => $me->{skin}->itemFill(),
primitive => 'rectangle',
points => "${startX}, $startY ${endX}, $bottom");
}
}
##########################################################################
#
# Method: _drawSubProj(startX, startY, endX, range)
#
# Purpose: Same as above, except draws a bracket instead of a
# diamond, indicating a containment relationship.
#
##########################################################################
sub _drawSubProj {
my $me = shift;
my $startX = shift;
my $startY = shift;
( run in 0.942 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )