#!/bin/perl package SVGraph::2D::lines; use SVGraph::2D; use strict; our @ISA=("SVGraph::2D"); sub new { my $class=shift; my %env=@_; my $self={}; %{$self->{ENV}}=%env; $self->{'ENV'}{'show_legend_label'}=1 unless exists $self->{'ENV'}{'show_legend_label'}; $self->{grid_y_scale_minimum}=$env{grid_y_scale_minimum}; $self->{grid_y_scale_maximum}=$env{grid_y_scale_maximum}; $self->{grid_y_main_spacing}=$env{grid_y_main_spacing}; $self->{type}="lines"; return bless $self,$class; } sub prepare { my $self=shift; $self->{SVG}=SVG->new(width=>$self->{ENV}{x},height=>$self->{ENV}{y}); $self->{SVG}->script()->CDATA("setTimeout('reload()', ".($self->{ENV}{reload}*1000).");") if $self->{ENV}{reload}; # draw the block $self->prepare_block(); # add the text $self->prepare_title(); # prepare the columns $self->prepare_columns(); # # VYPOCITANIE POCTU ROWSOV - $self->{grid_x_main_lines} # VYPOCITANIE MAX VALUE - $self->{value_max} # $self->prepare_axis_calculate(); $self->prepare_axis_x_markArea(front=>0); $self->prepare_axis_x(); $self->prepare_axis_x_mark(front=>0); $self->prepare_axis_y_markArea(front=>0); $self->prepare_axis_y(); $self->prepare_axis_y_mark(front=>0); $self->prepare_legend(); foreach my $color(keys %SVGraph::colors::table) { my $g = $self->{SVG}->gradient ( '-type' => "linear", 'id' => "gr_".$color."_0", 'x1'=>"0%",'y1'=>"0%",'x2'=>"0%",'y2'=>"100%", ); $g->stop(offset=>"0%",style=>"stop-color:rgb(".$SVGraph::colors::table{$color}{'B0'}.");stop-opacity:1"); $g->stop(offset=>"100%",style=>"stop-color:rgb(".$SVGraph::colors::table{$color}{'L1'}.");stop-opacity:1"); } # drawing the lines my @data_stacked; foreach my $column(@{$self->{columnsA}}) { my $color=$self->{columns}->{$column}{ENV}{color}; my %colors=%{$SVGraph::colors::table{$color}}; my $count; my $points; my ($point_x,$point_y); my (@points_x,@points_y,@points_data); # foreach my $data($self->{columns}{$column}->GetValues()) foreach my $row(@{$self->{row}{label}}) { my $data=$self->{columns}{$column}->{data}{$row}; my $data_o=$data; # data original my $data_w=$data; # data for display; $count++; # main::_log("drawing label '$row' ($self->{ENV}{type})"); if ($self->{ENV}{type}=~/percentage/) { if ($self->GetRowSum($row)) { $data=$data/($self->GetRowSum($row)/100); } else { # $data } } $data_stacked[$count]+=$data; if ($self->{ENV}{type}=~/stacked/) { if ($self->{ENV}{type}=~/percentage/) { $data_w=((int($data*100))/100)."%"; $data=100-$data_stacked[$count]+$data; } else { $data=$self->GetRowSum($row)-$data_stacked[$count]+$data; } } my $height=(($data-$self->{grid_y_scale_minimum})/($self->{grid_y_scale}/100))*($self->{block_height}/100); $height=int($height*100)/100; $height=0 if $height < 0; $height=$self->{block_height} if $height > $self->{block_height}; my $width=($count-1)*($self->{block_width}/($self->{grid_x_main_lines}-1)); $width=int($width*100)/100; $points.=($self->{block_left}+$width).",".($self->{block_down}-$height)." "; push @points_x,($self->{block_left}+$width); push @points_y,($self->{block_down}-$height); push @points_data, $data_w; } # create path sets (skip null values) my @sets; my $set_id; for my $i (0..@points_x-1) { if ($points_data[$i] == undef && not $self->{'ENV'}{'type'}=~/(stacked|percentage)/) { $set_id++;next; } push @{$sets[$set_id]{'x'}},$points_x[$i]; push @{$sets[$set_id]{'y'}},$points_y[$i]; } if ($self->{ENV}{show_lines_smooth_range}) { for my $sin_koef(3,4,6,10) { my $points_sin; $points_sin.="M$points_x[0],$points_y[0] "; for (1..@points_x-2) { $points_sin.="S".($points_x[$_]+($points_x[$_-1]-$points_x[$_+1])/$sin_koef).",".($points_y[$_]+($points_y[$_-1]-$points_y[$_+1])/$sin_koef)." $points_x[$_],$points_y[$_] "; } $points_sin.="S$points_x[-1],$points_y[-1] $points_x[-1],$points_y[-1]"; $self->{SVG}->path( d=>$points_sin, 'stroke-width' =>"0.5pt" , 'stroke' =>"rgb(".$colors{G0}.")", style => { 'fill' =>"white", 'fill-opacity' =>"0", } ); } } my $area_displayed=0; my $sin_koef=6; if ( $self->{'ENV'}{'show_areas'} || $self->{'columns'}{$column}->{'ENV'}{'show_area'} ) { my $min=$self->{grid_y_scale_minimum};$min=0 if $min<0; my $height= ( ($min-$self->{grid_y_scale_minimum})/($self->{grid_y_scale}/100) )*($self->{block_height}/100); my $opacity="0"; my $points_sin; my $set_id; foreach (@sets) { if (!$sets[$set_id]){$set_id++;next;} if ($sets[$set_id]{'x'}[1]) # minimal 2 points to draw line { my @points_x=@{$sets[$set_id]{'x'}}; my @points_y=@{$sets[$set_id]{'y'}}; my $first=-1; my $last; for (0..@points_x-2) { $last=$_; if ($first == -1) { $points_sin.="M$points_x[$_],$points_y[$_] "; $first=$_; next; } my $x=int($points_x[$_]+($points_x[$_-1]-$points_x[$_+1])/$sin_koef); my $y=int($points_y[$_]+($points_y[$_-1]-$points_y[$_+1])/$sin_koef); if ($y>$self->{'block_down'}){$y=$self->{'block_down'}} $points_sin.="S". ($x).",". ($y)." $points_x[$_],$points_y[$_] "; } $last=@points_x-1; $points_sin.="S$points_x[$last],$points_y[$last] $points_x[$last],$points_y[$last]"; my %plus; $plus{'stroke-dasharray'}=$self->{'ENV'}{'show_line_dasharray'} if $self->{'ENV'}{'show_line_dasharray'}; $plus{'stroke-dasharray'}=$self->{'columns'}{$column}->{'ENV'}{'show_line_dasharray'} if $self->{'columns'}{$column}->{'ENV'}{'show_line_dasharray'}; my $opacity=$self->{'columns'}{$column}->{'ENV'}{'show_area_opacity'} || $self->{'ENV'}{'show_areas_opacity'} || "1"; $points_sin.= " S".$points_x[$last].",".($self->{block_down}-$height)." ".$points_x[$last].",".($self->{block_down}-$height). " S".$points_x[$first].",".($self->{block_down}-$height)." ".$points_x[$first].",".($self->{block_down}-$height); $self->{SVG}->path( 'd' => $points_sin, 'stroke' => "rgb(0,0,0)", 'stroke-width' =>"0pt" , 'fill' =>"url(#gr_".$color."_0)", 'fill-opacity' =>$opacity, %plus ); } $set_id++; } $area_displayed=1; } if ( $self->{ENV}{show_lines_smooth} || $self->{columns}{$column}->{ENV}{show_line_smooth} ) { my $min=$self->{grid_y_scale_minimum};$min=0 if $min<0; my $height= ( ($min-$self->{grid_y_scale_minimum})/($self->{grid_y_scale}/100) )*($self->{block_height}/100); my $opacity="0"; my $points_sin; my $set_id; foreach (@sets) { if (!$sets[$set_id]){$set_id++;next;} if ($sets[$set_id]{'x'}[1]) # minimal 2 points to draw line { my @points_x=@{$sets[$set_id]{'x'}}; my @points_y=@{$sets[$set_id]{'y'}}; my $first=-1; my $last; for (0..@points_x-2) { $last=$_; if ($first == -1) { $points_sin.="M$points_x[$_],$points_y[$_] "; $first=$_; next; } my $x=int($points_x[$_]+($points_x[$_-1]-$points_x[$_+1])/$sin_koef); my $y=int($points_y[$_]+($points_y[$_-1]-$points_y[$_+1])/$sin_koef); if ($y>$self->{'block_down'}){$y=$self->{'block_down'}} $points_sin.="S". ($x).",". ($y)." $points_x[$_],$points_y[$_] "; } $last=@points_x-1; $points_sin.="S$points_x[$last],$points_y[$last] $points_x[$last],$points_y[$last]"; my %plus; $plus{'stroke-dasharray'}=$self->{'ENV'}{'show_lines_dasharray'} if $self->{'ENV'}{'show_lines_dasharray'}; $plus{'stroke-dasharray'}=$self->{'columns'}{$column}->{'ENV'}{'show_line_dasharray'} if $self->{'columns'}{$column}->{'ENV'}{'show_line_dasharray'}; $self->{SVG}->path( 'd' => $points_sin, 'stroke-width' => $self->{'columns'}{$column}->{'ENV'}{'show_line_width'} || $self->{'ENV'}{'show_lines_width'} || "1.5pt" , 'stroke' => "rgb(".$colors{N0}.")", 'stroke-linejoin' => "round", 'fill-opacity' => "0", %plus ); } # draw missing lines if ( ($self->{'ENV'}{'show_lines_missing'} || $self->{'columns'}{$column}->{'ENV'}{'show_line_missing'}) ) { # finding next valid set my $set_id_; for ($set_id+1..@sets-1) { if ($sets[$_]{'x'}[0]) { $set_id_=$_; last; } } if ($sets[$set_id_]{'x'}[0] && $set_id_ && $sets[$set_id]{'x'}[-1]) { my $points=$self->{SVG}->get_path( x => [$sets[$set_id]{'x'}[-1],$sets[$set_id_]{'x'}[0]], y => [$sets[$set_id]{'y'}[-1],$sets[$set_id_]{'y'}[0]], -type => 'path', ); $self->{SVG}->path( %$points, 'stroke-width' => "1pt", 'stroke-opacity' => "0.5", 'stroke' => "rgb(".$colors{N0}.")", 'stroke-linecap' => "round", 'stroke-linejoin' => "round", 'stroke-dasharray' => "2,4", 'fill-opacity' => "0", ); } } $set_id++; } } if ( ($self->{'ENV'}{'show_lines'} && not exists $self->{'columns'}{$column}->{'ENV'}{'show_line'}) || $self->{'columns'}{$column}->{'ENV'}{'show_line'} ) { my $min=$self->{grid_y_scale_minimum};$min=0 if $min<0; my $height=(($min-$self->{grid_y_scale_minimum})/($self->{grid_y_scale}/100))*($self->{block_height}/100); my $set_id; foreach (@sets) { if (!$sets[$set_id]) { $set_id++; next; } if ( ($self->{'ENV'}{'show_areas'} && not exists $self->{'columns'}{$column}->{'ENV'}{'show_area'}) || $self->{'columns'}{$column}->{'ENV'}{'show_area'}) { my $opacity=$self->{'columns'}{$column}->{'ENV'}{'show_area_opacity'} || $self->{'ENV'}{'show_areas_opacity'} || "1"; my $points=$self->{SVG}->get_path( x => [$sets[$set_id]{'x'}[0],@{$sets[$set_id]{'x'}},$sets[$set_id]{'x'}[-1]], y => [($self->{block_down}-$height),@{$sets[$set_id]{'y'}},($self->{block_down}-$height)], -type => 'path', ); $self->{SVG}->path( %$points, 'stroke-width' =>"0pt", 'fill' =>"url(#gr_".$color."_0)", 'fill-opacity' =>$opacity, ); $area_displayed=1; } my $points=$self->{SVG}->get_path( x => [@{$sets[$set_id]{'x'}}], y => [@{$sets[$set_id]{'y'}}], -type => 'path', ); $self->{SVG}->path( %$points, 'stroke-width' =>"1.5pt" , 'stroke' =>"rgb(".$colors{N0}.")", 'stroke-linecap' =>"round", 'stroke-linejoin' =>"round", # 'fill' =>"url(#gr_".$color."_0)", 'fill-opacity' =>"0", ); $set_id++; } } if ( !$area_displayed && ( ($self->{'ENV'}{'show_areas'} && not exists $self->{'columns'}{$column}->{'ENV'}{'show_area'}) || $self->{'columns'}{$column}->{'ENV'}{'show_area'}) ) { my $set_id; foreach (@sets) { if (!$sets[$set_id]) { $set_id++; next; } my $min=$self->{grid_y_scale_minimum};$min=0 if $min<0; my $height=(($min-$self->{grid_y_scale_minimum})/($self->{grid_y_scale}/100))*($self->{block_height}/100); my @points_x_=@{$sets[$set_id]{'x'}}; my @points_y_=@{$sets[$set_id]{'y'}}; unshift @points_x_,$points_x_[0]; unshift @points_y_,($self->{block_down}-$height); push @points_x_,$points_x_[-1]; push @points_y_,($self->{block_down}-$height); my $opacity=$self->{'columns'}{$column}->{'ENV'}{'show_area_opacity'} || $self->{'ENV'}{'show_areas_opacity'} || "1"; my $points=$self->{SVG}->get_path( x => [@points_x_], y => [@points_y_], -type => 'path', ); $self->{SVG}->path( %$points, 'stroke-width' =>"0pt", 'fill' =>"url(#gr_".$color."_0)", 'fill-opacity' =>$opacity, ); $set_id++; } $area_displayed=1; } for (0..@points_x-1) { if ($self->{ENV}{show_points} || $self->{columns}{$column}->{ENV}{show_points}) { my $circle=$self->{SVG}->circle ( cx => $points_x[$_], cy => $points_y[$_], r => 2.5, 'fill' => "white", 'fill-opacity' => "0.8", 'stroke' => "rgb(".$colors{N0}.")", 'stroke-width' => "1pt", # 'fill' => "rgb(".$colors{N0}.")", # 'fill-opacity' => "1", # 'stroke' => "white", # 'stroke-width' => "1pt", ) if $points_y[$_] ne $self->{'block_down'}; =head1 if ($self->{ENV}{show_points_animate} || $self->{columns}{$column}->{ENV}{show_points_animate}) { my $circle2=$self->{SVG}->circle ( cx => $points_x[$_], cy => $points_y[$_], r => 10, 'fill' => "white", 'stroke' => "rgb(".$colors{N0}.")", 'stroke-width' => "1pt", 'fill-opacity' => "0", 'stroke-opacity' => "0", ); $circle2->animate ( 'attributeName'=>"r", 'begin'=>"mouseover", 'end'=>"mouseout", 'from'=>10, 'to'=>30, # 'values'=>"30", 'dur'=>"2s", # 'repeatDur'=>"freeze" 'restart'=>"whenNotActive" ); $circle2->animate ( 'attributeName'=>"fill-opacity", 'begin'=>"mouseover", 'end'=>"mouseout", 'from'=>0, 'to'=>0.5, # 'values'=>"30", 'dur'=>"2s", # 'repeatDur'=>"freeze" 'restart'=>"whenNotActive" ); $circle2->animate ( 'attributeName'=>"stroke-opacity", 'begin'=>"mouseover", 'end'=>"mouseout", 'from'=>0, 'to'=>0.9, # 'values'=>"30", 'dur'=>"2s", # 'repeatDur'=>"freeze" 'restart'=>"whenNotActive" ); } =cut } $self->{SVG}->circle( cx=>(($points_x[$_]+$points_x[$_-1])/2), cy=>(($points_y[$_]+$points_y[$_-1])/2),r=>2, 'fill' => "rgb(".$colors{B1}.")", 'stroke' => "rgb(".$colors{L0}.")", 'stroke-width' => "1pt", ) if $self->{ENV}{show_points_middle}; if ($points_data[$_]) { if ( $self->{ENV}{show_data_background} || $self->{columns}{$column}->{ENV}{show_data_background} ) { my $width=length($points_data[$_])*5; my $rect=$self->{SVG}->rect( 'x' => ($points_x[$_]+2), 'y' => ($points_y[$_]-11), 'width' => $width+3, 'height' => 10, 'fill' =>"rgb(255,255,255)", 'fill-opacity' =>"1", 'stroke' =>"rgb(125,125,125)", 'stroke-width' =>"1pt", 'stroke-linecap' =>"round", 'stroke-linejoin' =>"round", 'rx' => "2pt", 'ry' => "2pt", ); =head1 if ($self->{ENV}{show_data} || $self->{columns}{$column}->{ENV}{show_data}) { $rect->text ( x => 10, y => 10, style => { 'font-family' => 'Verdana', 'font-size' => 8, 'font-weight' => 400, # 'fill' => "rgb(".$colors{B3}.")", 'fill' => "rgb(0,0,0)", # 'stroke' => "rgb(0,0,0)", # 'stroke-width' => "1pt", 'stroke-linecap' =>"round", 'stroke-linejoin' =>"round", }, )->cdata($points_data[$_]); } =cut } if ($self->{ENV}{show_data} || $self->{columns}{$column}->{ENV}{show_data}) { $self->{SVG}->text ( x => $points_x[$_]+3, y => $points_y[$_]-3, style => { 'font-family' => 'Verdana', 'font-size' => '8px', 'font-weight' => 400, # 'fill' => "rgb(".$colors{B3}.")", 'fill' => "rgb(0,0,0)", # 'stroke' => "rgb(0,0,0)", # 'stroke-width' => "1pt", 'stroke-linecap' =>"round", 'stroke-linejoin' =>"round", }, )->cdata($points_data[$_]); } } #=head1 # animated if ( ($self->{'ENV'}{'show_data_SMIL'} || $self->{columns}{$column}->{'ENV'}{'show_data_SMIL'}) &&($points_data[$_]) ) { my $width=length($points_data[$_])*5; my $box=$self->{SVG}->polyline( 'points' => ($points_x[$_]-3).",".($points_y[$_]-5)." ". ($points_x[$_]+$width).",".($points_y[$_]-5)." ". ($points_x[$_]+$width).",".($points_y[$_]+5)." ". ($points_x[$_]-3).",".($points_y[$_]+5)." ". ($points_x[$_]-3).",".($points_y[$_]-5)." ", 'fill' =>"rgb(255,255,255)", 'fill-opacity' =>"0.7", 'stroke' =>"rgb(0,0,0)", 'stroke-width' =>"1", 'stroke-opacity' => "0", 'fill-opacity' => "0", 'stroke-linecap' =>"round", 'stroke-linejoin' =>"round", ); $box->animate ( 'attributeName'=>"fill-opacity", 'begin'=>"mouseover", 'end'=>"mouseout", # 'from'=>"0", 'values'=>"1", # 'dur'=>"0.5s", # 'repeatDur'=>"freeze" 'restart'=>"whenNotActive" ); } #=cut } =head1 $self->{SVG}->polyline( points => $points # .$self->{block_right}.",".$self->{block_down}." " # .$self->{block_left}.",".$self->{block_down} , 'stroke-width' =>"1pt" , 'stroke' =>"black", style => { 'fill' =>"white", ' fill-opacity' =>"0", } ); =cut } $self->prepare_axis_x_markArea(front=>1); $self->prepare_axis_x_mark(front=>1); $self->prepare_axis_y_markArea(front=>1); $self->prepare_axis_y_mark(front=>1); $self->prepare_axis(); $self->prepare_legend_label(); # output $self->{SVG_out}=$self->{SVG}->xmlify ( # -namespace => "svg", # -pubid => "-//W3C//DTD SVG 1.0//EN", -inline => 1 ); return $self->{SVG_out}; }