SVG-Timeline-Compact

 view release on metacpan or  search on metacpan

lib/SVG/Timeline/Compact.pm  view on Meta::CPAN

	foreach my $event (sort _byStart $self->ev_all){
		my $found=0;
		$event->resolution($self->_resolution);
		$event->origin($self->_min);
		for my $slot(0..$#slot){
			my $evslot=$slot[$slot];
			my $head=pop @{$evslot};if ($head){push @{$evslot}, $head;}
			if ($head->x0+$head->width <$event->x0){
				push @{$evslot},$event;
				$found=1;
				last;

			}
		}

		if ($found==0){
			my $newslot=[$event];
			unshift @slot,$newslot;
		}
	}
	return @slot;
}

sub to_ds {
	my $self=shift;
	my @slot=$self->layout;
	my @ds;
	my ($y0,$y1,$x0,$maxX)=(0,1.5,0,0);
	my $fmt="yyyy-MM-dd hh:mm";
	foreach my $slot (@slot){
		foreach my $ev (@{$slot}){
			push @ds,{idchangeset=>$ev->id,count=>$ev->name,name=>$ev->tooltip,created=>$ev->start->format_cldr($fmt),
				x=>$ev->x0,y=>$y0,width=>$ev->width,height=>$y1, status=>$ev->color};
			$maxX=$ev->x0+$ev->width	if ($maxX<$ev->x0+$ev->width);
		}
		$y0+=1.5*$y1;
	}
	return {resolution=>$self->_resolution,start=>$self->_min->format_cldr($fmt),end=>$self->_max->format_cldr($fmt),maxX=>$maxX,maxY=>$y0,ds=>\@ds}
}


sub to_svg{
	my $self=shift;
	my @slot=$self->layout;
	my $svg=SVG->new();
        my $d = DateTime::Format::Duration->new( pattern => '%Y years, %m months, %e days, '.  '%H hours, %M minutes, %S seconds');
	my $bbox=$svg->group(id=>"bbox");
	my $bars=$svg->group(id=>"bars");
        my $def=$svg->defs(id=>"arrow","stroke-linecap"=>"round","stroke-width"=>"1");
        $def->line(x1=>"-8",y1=>"-4",x2=>"1",y2=>"0");
        $def->line(x1=>"1",y1=>"0",x2=>"-8",y2=>"4");
	my ($y0,$y1,$x0,$maxX)=(0,$self->min_height,0,0);
	foreach my $slot (@slot){
		#p @slot;
		foreach my $ev (@{$slot}){
			#p $ev;
			$x0=$ev->x0;
			my $width=$ev->width;
			my $color=$ev->color;
			my $rect=$bars->rect(x=>$x0."px",y=>$y0."px",width=>$width."px",height=>$y1."px", fill=>$color,stroke=>"#000");
			$bars->text(x=>$x0+$width/2,y=>($y0-2+$self->min_height),"text-anchor"=>"middle")->cdata($ev->name);
				$rect->title->cdata($ev->tooltip.", Start: ". $ev->start.", End ". $ev->end);
			$maxX=$x0+$width	if ($maxX<$x0+$width);
		}
		$y0+=$y1*1.5;

	}
	#$y0+=$y1*1.5;
        $maxX=$self->units if($maxX<$self->units);
	my $border=$bbox->rect(x=>0,"fill-opacity"=>"0.1",y=>0,width=>$maxX."px",height=>$y0."px");
	my $x=0 ;
        my$dim=$bbox->group(id=>"dim");
	$dim->line(x1=>0,y1=>($y0+$self->min_height),x2=>800,y2=>($y0+$self->min_height));
	$dim->use("xlink:href"=>"#arrow",x=>0,y=>($y0+$self->min_height));
	$dim->use("xlink:href"=>"#arrow",x=>800,y=>($y0+$self->min_height));
	#$dim->text(x=>$maxX/2,y=>($y0+$self->min_height),"text-anchor"=>"middle")->cdata("<-------------- ". $self->_resolution. " --------------->");
        #<use stroke="#000000" xlink:href="#ah" transform="translate(354.4 119.4)rotate(90)"/>
	$bbox->text(x=>$maxX+10,y=>$self->min_height)->cdata("Min:\t".$self->_min->format_cldr("yyyy/MM/dd h:m"));
	$bbox->text(x=>$maxX+10,y=>2*$self->min_height)->cdata("Max:\t".$self->_max->format_cldr("yyyy/MM/dd h:m"));

	my $unit;
	$unit= $self->_resolution->in_units('months')." Months" if ($self->_resolution->in_units('months')>0);
	$unit= $self->_resolution->in_units('days')." Days" if ($self->_resolution->in_units('days')>0);
	$unit= $self->_resolution->in_units('minutes')." Minutes" if ($self->_resolution->in_units('minutes')>0);
	$bbox->text(x=>$maxX+10,y=>3*$self->min_height)->cdata("Scale:\t ".$unit);
	while($x<=$maxX){
		if($x%100 == 0){
		$bbox->line(x1=>$x,x2=>$x,y1=>$y0,y2=>0 ,style=>"stroke:#000;stroke-width:1px" );
		$dim->text(x=>$x,y=>($y0+$self->min_height),"text-anchor"=>"middle")->cdata("$x");
	}else{
		$bbox->line(x1=>$x,x2=>$x,y1=>$y0,y2=>0 ,style=>"stroke:#fff;stroke-width:1px" );
	}
		$x=$x+10;

	}
	return $svg->xmlify;
}
sub _set_resolution{
	#We desire that atleast 50% of the graph is utilized. and we do not overflow.
	#minutes up to 800 minutes
	my $self=shift;
	my $scale=get_scale($self->_min,$self->_max);
	$self->_resolution($scale);
}
sub get_scale{
	my ($min,$max)=@_;
	my $duration=$max-$min;
	my $days=$duration->in_units('months')*30 +$duration->in_units('days');
	my $hours=$days*24+$duration->in_units('hours');
	my $minutes=$hours*60+$duration->in_units('minutes');
	my $scale;
	#p $minutes;
	if ($duration->in_units('months')>300){
		$scale=DateTime::Duration->new(months=>POSIX::ceil($duration->in_units('months')/800));
	}elsif ($days>300){
		$scale=DateTime::Duration->new(days=>POSIX::ceil($days/800));
	}elsif ($hours>300){
		$scale=DateTime::Duration->new(hours=>POSIX::ceil($hours/800));
	}else{
		$scale=DateTime::Duration->new(minutes=>POSIX::ceil($minutes/800));
	}
	return $scale;
}
sub _byStart {
	return DateTime->compare($a->start,$b->start);
}

__END__

=pod

=encoding UTF-8

=head1 NAME

SVG::Timeline::Compact - A Moose based SVG Timeline drawing class.

=head1 VERSION

version 0.003

=head1 SYNOPSIS

 use SVG::Timeline::Compact;
 use DateTime::Format::Natural;

 my $svg=SVG::Timeline::Compact->new();

 my $parser = DateTime::Format::Natural->new;



( run in 1.250 second using v1.01-cache-2.11-cpan-22024b96cdf )