File Coverage

lib/Graph/ChartSVG.pm
Criterion Covered Total %
statement 168 527 31.8
branch 20 172 11.6
condition 22 291 7.5
subroutine 24 29 82.7
pod 5 8 62.5
total 239 1027 23.2


line stmt bran cond sub pod time code
1             package Graph::ChartSVG::Layer;
2 1     1   14242 use Moose;
  1         465081  
  1         8  
3              
4             has 'data' => ( isa => 'Graph::ChartSVG::Data', is => 'rw', required => 0 );
5             has 'glyph' => ( isa => 'Graph::ChartSVG::Glyph', is => 'rw', required => 0 );
6             has 'overlay' => ( isa => 'Graph::ChartSVG::Overlay', is => 'rw', required => 0 );
7              
8             1;
9              
10             package Graph::ChartSVG::Data;
11 1     1   6998 use Moose;
  1         3  
  1         4  
12              
13             has 'data_set' => ( isa => 'ArrayRef', is => 'rw', required => 0 );
14             has 'type' => ( isa => 'Str', is => 'rw', required => 0, default => 'line' );
15             has 'thickness' => ( isa => 'Num', is => 'rw', required => 0, default => 1 );
16             has 'color' => ( isa => 'Str | ArrayRef', is => 'rw', required => 0, default => '00000000' );
17             has 'opacity' => ( isa => 'Num', is => 'rw', required => 0, default => 1 );
18             has 'max' => ( isa => 'Int', is => 'rw', required => 0 );
19             has 'last' => ( isa => 'Int', is => 'rw', required => 0 );
20             has 'label' => ( isa => 'Str', is => 'rw', required => 0 );
21             has 'offset' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
22             has 'scale' => ( isa => 'Num | ArrayRef', is => 'rw', required => 0, default => 1 );
23              
24             1;
25              
26             package Graph::ChartSVG::Frame;
27 1     1   5966 use Moose;
  1         7  
  1         5  
28              
29             has 'type' => ( isa => 'Str', is => 'rw', required => 0, default => 'line' );
30             has 'thickness' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
31             has 'color' => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );
32              
33             1;
34              
35             package Graph::ChartSVG::Glyph;
36 1     1   5942 use Moose;
  1         2  
  1         17  
37              
38             has 'x' => ( isa => 'Str', is => 'rw', required => 1, default => 0 );
39             has 'y' => ( isa => 'Str', is => 'rw', required => 1, default => 0 );
40             has 'type' => ( isa => 'Str', is => 'rw', required => 0, default => 'line' );
41             has 'filled' => ( isa => 'Bool', is => 'rw', required => 0 );
42             has 'color' => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );
43             has 'anchor' => ( isa => 'Str', is => 'rw', required => 0, default => 'start' );
44             has 'data_set' => ( isa => 'ArrayRef', is => 'rw', required => 0 );
45             has 'thickness' => ( isa => 'Num', is => 'rw', required => 0, default => 1 );
46             has 'font' => ( isa => 'Str', is => 'rw' );
47             has 'size' => ( isa => 'Num', is => 'rw' );
48             has 'font_weight' => ( isa => 'Str', is => 'rw' );
49             has 'stretch' => ( isa => 'Str', is => 'rw' );
50             has 'letter_spacing' => ( isa => 'Num', is => 'rw' );
51             has 'word_spacing' => ( isa => 'Num', is => 'rw' );
52             has 'label' => ( isa => 'Str', is => 'rw', required => 0 );
53              
54             1;
55              
56             package Graph::ChartSVG::Border;
57 1     1   6041 use Moose;
  1         2  
  1         4  
58              
59             has 'left' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
60             has 'right' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
61             has 'top' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
62             has 'bottom' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
63              
64             1;
65              
66             package Graph::ChartSVG::Label;
67 1     1   5978 use Moose;
  1         2  
  1         5  
68              
69             has 'color' => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );
70             has 'text' => ( isa => 'ArrayRef', is => 'rw', required => 0 );
71             has 'font' => ( isa => 'Str', is => 'rw' );
72             has 'font_scaling' => ( isa => 'Num', is => 'rw', required => 0, default => 1 );
73             has 'style' => ( isa => 'Str', is => 'rw', required => 0 );
74             has 'space' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
75             has 'size' => ( isa => 'Num', is => 'rw', required => 0, default => 10 );
76             has 'align' => ( isa => 'Str', is => 'rw', required => 0, default => 'left' );
77             has 'rotation' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
78             has 'kerning_correction' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
79              
80             1;
81              
82             package Graph::ChartSVG::Grid_def;
83 1     1   5969 use Moose;
  1         2  
  1         3  
84              
85             has 'color' => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );
86             has 'number' => ( isa => 'Num', is => 'rw', required => 1 );
87             has 'thickness' => ( isa => 'Num', is => 'rw', required => 0, default => 1 );
88             has 'label' => ( isa => 'Graph::ChartSVG::Label', is => 'rw', required => 0 );
89             has 'label2' => ( isa => 'Graph::ChartSVG::Label', is => 'rw', required => 0 );
90              
91             1;
92              
93             package Graph::ChartSVG::Grid;
94 1     1   5975 use Moose;
  1         2  
  1         13  
95              
96             has 'x' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
97             has 'y' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
98             has 'y_up' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
99             has 'y_down' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
100             has 'x_up' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
101             has 'x_down' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
102             has 'debord' => ( isa => 'Graph::ChartSVG::Border', is => 'rw', required => 0, default => sub { Graph::ChartSVG::Border->new } );
103              
104             1;
105              
106             package Graph::ChartSVG::Overlay;
107 1     1   5999 use Moose;
  1         1  
  1         5  
108              
109             has 'type' => ( isa => 'Str', is => 'rw', required => 0, default => 'v' );
110             has 'debord_1' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
111             has 'debord_2' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
112             has 'data_set' => ( isa => 'HashRef', is => 'rw', required => 0 );
113             has 'color' => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );
114              
115             1;
116              
117             package Graph::ChartSVG;
118 1     1   5923 use Moose;
  1         2  
  1         6  
119              
120 1     1   14719 use constant PI => 4 * atan2( 1, 1 );
  1         5  
  1         171  
121              
122 1     1   2168 use SVG;
  1         18079  
  1         8  
123 1     1   2214 use SVG::Parser qw(SAX=XML::LibXML::SAX::Parser);
  1         655  
  1         7  
124              
125 1     1   37961 use List::Util qw( max min sum );
  1         2  
  1         108  
126             # use Math::Complex;
127             # use Compress::Zlib;
128 1     1   828 use Hash::Merge qw( merge );
  1         2610  
  1         59  
129 1     1   890 use Data::Serializer;
  1         3090  
  1         34  
130              
131             # use Carp::Clan;
132 1     1   7 use Carp;
  1         2  
  1         56  
133             #use Data::Dumper;
134             # use Devel::Size qw(size total_size);
135             # use Clone qw(clone);
136              
137 1     1   838 use MIME::Base64;
  1         742  
  1         63  
138              
139 1     1   6 use vars qw( $VERSION );
  1         2  
  1         8705  
140              
141             $VERSION = '2.07';
142              
143             has 'active_size' => ( isa => 'ArrayRef', is => 'rw', required => 0 );
144             has 'total_size' => ( isa => 'ArrayRef', is => 'rw', required => 0 );
145             has 'bg_color' => ( isa => 'Str', is => 'rw', required => 0, default => 'ffffffff' );
146             has 'frame' => ( isa => 'Graph::ChartSVG::Frame', is => 'rw', required => 0, default => sub { Graph::ChartSVG::Frame->new } );
147             has 'grid' => ( isa => 'Graph::ChartSVG::Grid', is => 'rw', required => 0 );
148             #has 'reticle' => ( isa => 'HashRef', is => 'rw', required => 0 );
149             has 'overlay' => ( isa => 'Graph::ChartSVG::Overlay', is => 'rw', required => 0 );
150             has 'glyph' => ( isa => 'ArrayRef', is => 'rw', required => 0 );
151             #has 'layer' => ( isa => 'ArrayRef', is => 'rw' ,default => sub { [ Layer->new]} );
152             has 'layer' => ( isa => 'ArrayRef[Layer]', is => 'rw' );
153             has 'image' => ( isa => 'Str', is => 'rw' );
154             has 'svg_raw' => ( isa => 'Str', is => 'rw' );
155             has 'border' => ( isa => 'Graph::ChartSVG::Border', is => 'rw', required => 0, default => sub { Graph::ChartSVG::Border->new } );
156             has 'tag' => ( isa => 'Bool', is => 'rw', required => 0 );
157             #has 'tag' => ( isa => 'Tag', is => 'rw', required => 0 );
158             #has 'tag' => ( isa => 'HashRef', is => 'rw', required => 0 );
159              
160             sub Tag
161             {
162 0     0 0 0 my $self = shift;
163 0         0 my $args = shift;
164 0 0       0 if ( exists $self->{ tag } )
165             {
166 0         0 $self->{ tag } = merge( $self->{ tag }, $args );
167             }
168             else
169             {
170 0         0 $self->{ tag } = $args;
171             }
172 0         0 bless $self;
173             }
174              
175             sub label
176             {
177 0     0 1 0 my $self = shift;
178 0         0 my $label = shift;
179 0         0 foreach my $l ( 1 .. ( scalar @{ $self->{ Layer } } ) )
  0         0  
180             {
181              
182 0         0 my $m = $l - 1;
183              
184 0 0 0     0 if ( defined $self->{ Layer }->[$m]
      0        
185             && exists $self->{ Layer }->[$m]->{ label }
186             && $self->{ Layer }->[$m]->{ label } eq $label )
187             {
188             # carp "$l => ".Dumper($self->{ Layer }->[$m]->{ data_set });
189             return wantarray
190             ? ( $self->{ Layer }->[$m]->{ data_set }, $m )
191 0 0       0 : $self->{ Layer }->[$m]->{ data_set };
192             }
193             }
194 0         0 return ();
195             }
196              
197             sub move
198             {
199 0     0 1 0 my $self = shift;
200 0         0 my $from = shift;
201 0         0 my $to = shift;
202 0         0 my $type = 'Layer';
203 0         0 my @tmp;
204 0 0       0 if ( exists $self->{ $type } )
205             {
206 0         0 my $elem = splice @{ $self->{ $type } }, $from, 1;
  0         0  
207 0         0 @tmp = ( @{ $self->{ $type } }[ 0 .. ( $to - 1 ) ], $elem, @{ $self->{ $type } }[ ( $to ) .. $#{ $self->{ $type } } ] );
  0         0  
  0         0  
  0         0  
208             }
209              
210 0         0 $self->{ $type } = \@tmp;
211             }
212              
213             sub add
214             {
215 3     3 1 7749 my $self = shift;
216 3         5 my $what = shift;
217 3         6 my $where = shift;
218 3         4 my $insert = shift;
219              
220 3         4 my $type = 'Layer';
221 3         4 my @tmp;
222 3 50       10 if ( exists $self->{ $type } )
223             {
224 3         4 @tmp = @{ $self->{ $type } };
  3         8  
225             }
226 3 50       9 if ( defined $where )
227             {
228 0 0       0 if ( $where >= 0 )
229             {
230 0 0       0 if ( !exists $what->{ label } )
231             {
232 0         0 $what->{ label } = $where;
233             }
234 0 0       0 if ( defined $insert )
235             {
236 0         0 my @t = ( @tmp[ 0 .. ( $where - 1 ) ], $what, @tmp[ $where .. $#tmp ] );
237 0         0 @tmp = @t;
238             }
239             else
240             {
241 0         0 $tmp[$where] = $what;
242             }
243             }
244             else
245             {
246 0 0       0 if ( !exists $what->{ label } )
247             {
248 0         0 $what->{ label } = 0;
249             }
250 0         0 unshift @tmp, $what;
251             }
252             }
253             else
254             {
255 3         5 push @tmp, $what;
256             }
257              
258 3         11 $self->{ $type } = \@tmp;
259             }
260              
261             sub render
262             {
263 1     1 1 13538 my $self = shift;
264 1         28 my @tmp = ( $self->active_size->[0] + $self->border->right + $self->border->left, $self->active_size->[1] + $self->border->top + $self->border->bottom );
265 1         24 $self->total_size( \@tmp );
266              
267 1         23 my $svg = SVG->new(
268             width => $self->total_size->[0],
269             height => $self->total_size->[1],
270             standalone => 'yes',
271             # border => 1,
272             );
273              
274 1 50       357 if ( $self->bg_color )
275             {
276 1   50     24 my $tag = $svg->rectangle(
277             x => 0,
278             y => 0,
279             width => $self->total_size->[0],
280             height => $self->total_size->[1],
281             fill => '#' . ( unpack "a6", $self->bg_color ) || 0,
282             id => 'background'
283             );
284             }
285              
286 1         89 my $layer_ind = 0;
287 1         23 my $data_goup = $svg->group(
288             id => "data",
289             transform => "matrix(1,0,0,-1," . ( $self->border->left ) . "," . ( $self->border->top + $self->active_size->[1] ) . ")"
290             );
291             # my $layer_goup = $svg->group(
292             # id => "layer",
293             # transform => "matrix(1,0,0,-1," . ( $self->border->left ) . "," . ( $self->border->top + $self->active_size->[1] ) . ")"
294             # );
295              
296 1         65 my @list_data;
297 1         3 foreach my $layer ( @{ $self->{ Layer } } )
  1         4  
298             {
299 0         0 my $layer_goup;
300 0         0 push @list_data, "data_$layer_ind";
301 0 0       0 if ( ( ref $layer ) eq 'Graph::ChartSVG::Data' )
302             {
303 0 0       0 if ( defined $layer->{ data_set } )
304             {
305 0         0 my $scale = $layer->{ scale };
306              
307 0 0       0 if ( $layer->{ type } =~ /_up$/ )
    0          
308             {
309 0         0 $scale *= 0.5;
310 0         0 $layer_goup = $data_goup->group( id => 'data_' . $layer_ind, transform => "matrix(1,0,0,$scale,0," . ( ( $self->active_size->[1] / 2 ) + $layer->{ offset } ) . " )" );
311             }
312             elsif ( $layer->{ type } =~ /_down$/ )
313             {
314 0         0 $scale *= -0.5;
315             # carp "scale=$scale act=".$self->active_size->[1]." off=" .$layer->{ offset };
316 0         0 $layer_goup = $data_goup->group( id => 'data_' . $layer_ind, transform => "matrix(1,0,0,$scale,0," . ( ( ( $self->active_size->[1] / 2 ) - $layer->{ offset } ) ) . " )" );
317             }
318             else
319             {
320 0 0       0 if ( $layer->{ offset } )
321             {
322 0         0 $layer_goup = $data_goup->group( id => 'data_' . $layer_ind, transform => "matrix(1,0,0,$scale,0," . ( $layer->{ offset } ) . " )" );
323             }
324             else
325             {
326 0         0 $layer_goup = $data_goup->group( id => 'data_' . $layer_ind, transform => "matrix(1,0,0,$scale,0,0)" );
327             }
328             }
329              
330 0 0       0 if ( ref( $layer->{ data_set }->[0] ) eq 'ARRAY' )
331             {
332              
333 0         0 my @all_xv;
334             my @all_yv;
335 0         0 my @all_style;
336 0         0 my $stack_size = scalar @{ $layer->{ data_set } };
  0         0  
337 0 0       0 if ( $layer->{ type } =~ /_stack/ )
338             {
339 0 0       0 if ( $layer->{ type } =~ /line|bar/ )
340             {
341 0         0 my $max = 0;
342 0         0 for my $stack_idx ( 0 .. $#{ $layer->{ data_set } } )
  0         0  
343             {
344 0         0 $max = max( $max, $#{ $layer->{ data_set }->[$stack_idx] } );
  0         0  
345             }
346              
347 0         0 for my $stack_idx ( 0 .. $#{ $layer->{ data_set } } )
  0         0  
348             {
349 0         0 my @xv;
350             my @yv;
351 0         0 my $dot = -1;
352 0         0 for my $raw_val_idx ( 0 .. $max )
353             {
354 0         0 my $raw_val = $layer->{ data_set }->[$stack_idx][$raw_val_idx];
355 0         0 $dot++;
356 0 0       0 last if $dot >= $self->active_size->[0];
357 0   0     0 my $plot_val = ( $raw_val || 0 );
358 0 0       0 $plot_val =
359             $plot_val * $scale > $self->active_size->[1]
360             ? $self->active_size->[1]
361             : $plot_val;
362              
363 0         0 $xv[$raw_val_idx] = $dot;
364 0         0 $yv[$raw_val_idx] = sum_array( $stack_idx, $raw_val_idx, $layer->{ data_set } );
365              
366             }
367 0         0 my %style;
368 0 0       0 if ( $layer->{ type } =~ /bar/ )
369             {
370              
371 0         0 push @xv, $dot;
372 0         0 push @yv, 0;
373 0         0 push @xv, 0;
374 0         0 push @yv, 0;
375              
376             %style = (
377             'opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color }->[$stack_idx] ) ) / 255 ) || 1,
378             'stroke' => '#' . ( unpack "a6", $layer->{ color }->[$stack_idx] ) || 0,
379             'stroke-width' => $layer->{ thickness },
380             'fill' => '#' . ( unpack "a6", $layer->{ color }->[$stack_idx] ) || 0,
381 0   0     0 'fill-opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color }->[$stack_idx] )[1] ) / 255 ) || 1,
      0        
      0        
      0        
382             'fill-rule' => 'nonzero'
383             );
384             }
385             else
386             {
387             %style = (
388             'opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color }->[$stack_idx] )[1] ) / 255 ) || 1,
389             'stroke' => '#' . ( unpack "a6", $layer->{ color }->[$stack_idx] ) || 0,
390             'stroke-width' => $layer->{ thickness },
391 0   0     0 'fill' => '#ff0000',
      0        
392             'fill-opacity' => 0,
393             'fill-rule' => 'nonzero'
394             );
395             }
396 0         0 push @all_xv, \@xv;
397 0         0 push @all_yv, \@yv;
398 0         0 push @all_style, \%style;
399              
400             }
401              
402 0         0 foreach ( my $idx = $#all_xv ; $idx >= 0 ; $idx-- )
403             {
404 0         0 my $points = $layer_goup->get_path(
405             x => $all_xv[$idx],
406             y => $all_yv[$idx],
407             -type => 'polyline',
408             -closed => 'true' #specify that the polyline is NOT closed.
409             );
410              
411 0         0 my $id_data;
412 0 0       0 if ( exists $layer->{ label } )
413             {
414 0         0 $id_data = $layer->{ label } . '_' . $idx;
415             }
416             else
417             {
418 0         0 $id_data = 'layerdata_' . $layer_ind . '_' . $idx;
419             }
420 0         0 my $tag = $layer_goup->polyline(
421             %$points,
422             id => $id_data,
423             style => $all_style[$idx]
424             );
425             }
426             }
427             }
428             else
429             {
430 0         0 carp( "if data_set not arayref_of_arrayref it should be stack type " );
431             }
432             }
433             else
434             {
435 0 0       0 if ( $layer->{ type } =~ /line|bar/ )
436             {
437 0         0 my @xv;
438             my @yv;
439 0         0 my $dot = -1;
440              
441 0         0 foreach my $raw_val ( @{ $layer->{ data_set } } )
  0         0  
442             {
443 0         0 $dot++;
444              
445 0 0       0 last if $dot >= $self->active_size->[0];
446 0   0     0 my $plot_val = ( $raw_val || 0 );
447 0 0       0 $plot_val =
448             $plot_val * $scale > $self->active_size->[1]
449             ? $self->active_size->[1]
450             : $plot_val;
451              
452 0         0 push @xv, $dot;
453 0         0 push @yv, $plot_val;
454             }
455 0         0 my %style;
456 0 0       0 if ( $layer->{ type } =~ /bar/ )
457             {
458              
459 0         0 push @xv, $dot;
460 0         0 push @yv, 0;
461 0         0 push @xv, 0;
462 0         0 push @yv, 0;
463              
464             %style = (
465             'opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color } )[1] ) / 255 ) || 1,
466             'stroke' => '#' . ( unpack "a6", $layer->{ color } ) || 0,
467             'stroke-width' => $layer->{ thickness },
468             'fill' => '#' . ( unpack "a6", $layer->{ color } ) || 0,
469 0   0     0 'fill-opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color } )[1] ) / 255 ) || 1,
      0        
      0        
      0        
470             'fill-rule' => 'nonzero'
471             );
472             }
473             else
474             {
475             %style = (
476             'opacity' => eval( hex( ( unpack "a6 a2", $layer->{ color } )[1] ) / 255 ) || 1,
477             'stroke' => '#' . ( unpack "a6", $layer->{ color } ) || 0,
478             'stroke-width' => $layer->{ thickness },
479 0   0     0 'fill' => '#ff0000',
      0        
480             'fill-opacity' => 0,
481             'fill-rule' => 'nonzero'
482             );
483             }
484 0         0 my $points = $layer_goup->get_path(
485             x => \@xv,
486             y => \@yv,
487             -type => 'polyline',
488             -closed => 'true' #specify that the polyline is NOT closed.
489             );
490              
491 0         0 my $id_data;
492 0 0       0 if ( exists $layer->{ label } )
493             {
494 0         0 $id_data = $layer->{ label };
495             }
496             else
497             {
498 0         0 $id_data = 'layerdata_' . $layer_ind;
499             }
500 0         0 my $tag = $layer_goup->polyline(
501             %$points,
502             id => $id_data,
503             style => \%style
504             );
505             }
506             }
507             }
508             }
509              
510             #######################################
511             ################ Glyph ################
512             #######################################
513 0 0       0 if ( ( ref $layer ) eq 'Graph::ChartSVG::Glyph' )
514             {
515 0         0 $layer_goup = $data_goup->group( id => "data_$layer_ind" );
516 0         0 my $X = 0;
517 0         0 my $Y = 0;
518             # if ( $layer->{ x } eq 'active_min' )
519             # {
520             # # $X += $self->border->left;
521             # }
522             # elsif ( $layer->{ x } eq 'active_max' )
523             # {
524             # $X += $self->active_size->[0];
525             # }
526             # else
527             # {
528             # if ( exists $layer->{ type } && $layer->{ type } eq 'image' )
529             # {
530             # $X = $layer->{ x };
531             # }
532             # else
533             # {
534             # $X += $layer->{ x } - $self->border->left;
535             # }
536             # }
537             # if ( $layer->{ y } eq 'active_max' )
538             # {
539             # $Y += $self->active_size->[1];
540             # }
541             # elsif ( $layer->{ y } eq 'active_min' )
542             # {
543             # # $Y += $self->border->bottom;
544             # }
545             # else
546             # {
547             # if ( exists $layer->{ type } && $layer->{ type } eq 'image' )
548             # {
549             # $Y = $layer->y;
550             # }
551             # else
552             # {
553             # $Y += $layer->y - $self->border->bottom;
554             # }
555             # }
556              
557 0 0 0     0 if ( exists $layer->{ type } && $layer->{ type } eq 'image' )
558             {
559 0         0 $X = $layer->{ x };
560 0         0 $Y = $layer->y;
561             }
562             else
563             {
564 0         0 $X += $layer->{ x } - $self->border->left;
565 0         0 $Y += $layer->y - $self->border->bottom;
566             }
567              
568 0 0 0     0 if ( exists $layer->{ type } && $layer->{ type } eq 'text' )
    0 0        
    0 0        
    0 0        
569             {
570 0         0 foreach my $set ( @{ $layer->data_set } )
  0         0  
571             {
572 0   0     0 my $text_angle = exists( $set->{ rotation } ) && $set->{ rotation } || 0;
573 0         0 my $font_style = 'normal';
574 0   0     0 my $font_weight = $set->{ font_weight } || $layer->font_weight || 'normal';
575              
576 0   0     0 my $f_style = $set->{ style } || $layer->style;
577 0 0       0 if ( $f_style =~ /(italic)|(oblique)/ )
578             {
579 0         0 $font_style = 'italic';
580             }
581 0 0       0 if ( $f_style =~ /bold/ )
582             {
583 0         0 $font_weight = 'bold';
584             }
585              
586 0   0     0 my $letter_spacing = $set->{ letter_spacing } || $layer->letter_spacing || 'normal';
587 0   0     0 my $word_spacing = $set->{ word_spacing } || $layer->word_spacing || 'normal';
588 0   0     0 my $font_stretch = $set->{ stretch } || $layer->stretch || 'normal';
589              
590             my $txt = $layer_goup->text(
591             x => $set->{ x } || 0,
592             y => -$set->{ y } || 0,
593             style => {
594             'font-family' => $set->{ font } || $layer->font,
595             'font-size' => $set->{ size } || $layer->size,
596             'font-style' => $font_style,
597             'font-weight' => $font_weight,
598             'font-stretch' => $font_stretch,
599             'letter-spacing' => $letter_spacing,
600             'word-spacing' => $word_spacing,
601             'fill' => '#' . ( $set->{ color } || $layer->color || 'ffffff' ),
602             'stroke' => '#' . ( $set->{ stroke } || $set->{ color } || $layer->color || '000000' ),
603             'writing-mode' => 'lr',
604 0   0     0 'text-anchor' => $set->{ anchor } || $layer->anchor
      0        
      0        
      0        
      0        
      0        
      0        
605             },
606             transform => "matrix(1,0,0,-1," . ( $X ) . "," . ( $Y ) . ") rotate($text_angle)"
607             );
608 0         0 $txt->tspan( dy => "0" )->cdata( $set->{ text } );
609              
610             }
611             }
612             elsif ( exists $layer->{ type } && $layer->{ type } eq 'line' )
613             {
614 0         0 my $ind = 0;
615 0         0 foreach my $set ( @{ $layer->data_set } )
  0         0  
616             {
617 0         0 my @xv;
618             my @yv;
619 0         0 my $dot = -1;
620              
621 0         0 foreach my $point ( @{ $set->{ data } } )
  0         0  
622             {
623 0 0       0 next unless ( ref $point eq 'ARRAY' );
624 0         0 push @xv, $point->[0] + $X;
625 0         0 push @yv, $point->[1] + $Y;
626 0         0 $dot++;
627             }
628 0         0 my %style;
629 0   0     0 my $color_hex = $set->{ color } || $layer->{ color };
630 0 0 0     0 if ( exists $layer->{ filled } && $layer->{ filled } == 1 )
631             {
632 0         0 push @xv, $xv[0];
633 0         0 push @yv, $yv[0];
634             %style = (
635             'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
636             'stroke' => '#' . ( unpack "a6", $color_hex ) || 0,
637 0   0     0 'stroke-width' => ( $set->{ thickness } || $layer->{ thickness } ),
      0        
      0        
      0        
      0        
638             'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
639             'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
640             'fill-rule' => 'nonzero'
641             );
642             }
643             else
644             {
645             %style = (
646             'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 )
647             || 1,
648             'stroke' => '#' . ( unpack "a6", $color_hex )
649             || 0,
650 0   0     0 'stroke-width' => ( $set->{ thickness } || $layer->{ thickness } ),
      0        
      0        
651             # 'fill' => '#ff0000',
652             'fill-opacity' => 0,
653             'fill-rule' => 'nonzero'
654             );
655             }
656 0         0 my $points = $layer_goup->get_path(
657             x => \@xv,
658             y => \@yv,
659             -type => 'polyline',
660             -closed => 'true',
661             );
662 0         0 my $id_data;
663 0 0       0 if ( exists $layer->{ label } )
664             {
665 0         0 $id_data = $layer->{ label };
666             }
667             else
668             {
669 0         0 $id_data = 'glyph_' . $layer_ind . '_' . $ind;
670             }
671 0         0 my $tag = $layer_goup->polyline(
672             %$points,
673             id => $id_data,
674             style => \%style,
675             );
676 0         0 $ind++;
677             }
678             }
679             elsif ( exists $layer->{ type } && $layer->{ type } eq 'ellipse' )
680             {
681 0         0 my $ind = 0;
682 0         0 foreach my $set ( @{ $layer->data_set } )
  0         0  
683             {
684 0   0     0 my $cx = ( $set->{ cx } + $X ) || 0;
685 0   0     0 my $cy = ( $set->{ cy } ) || 0;
686 0   0     0 my $rx = $set->{ rx } || 0;
687 0   0     0 my $ry = $set->{ ry } || 0;
688 0         0 my %style;
689              
690 0   0     0 my $color_hex = $set->{ color } || $layer->{ color };
691 0 0 0     0 if ( exists $layer->{ filled } && $layer->{ filled } == 1 )
692             {
693             %style = (
694             'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
695             'stroke' => '#' . ( unpack "a6", $color_hex ) || 0,
696 0   0     0 'stroke-width' => ( $set->{ thickness } || $layer->{ thickness } ),
      0        
      0        
      0        
      0        
697             'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
698             'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
699             'fill-rule' => 'nonzero'
700             );
701             }
702             else
703             {
704             %style = (
705             'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 )
706             || 1,
707             'stroke' => '#' . ( unpack "a6", $color_hex )
708             || 0,
709 0   0     0 'stroke-width' => ( $set->{ thickness } || $layer->{ thickness } ),
      0        
      0        
710             # 'fill' => '#ff0000',
711             'fill-opacity' => 0,
712             'fill-rule' => 'nonzero'
713             );
714             }
715              
716 0         0 my $tag = $layer_goup->ellipse(
717             cx => $cx,
718             cy => $cy,
719             rx => $rx,
720             ry => $ry,
721             id => 'ellipse_' . $layer_ind . '_' . $ind,
722             style => \%style,
723             # transform => "matrix(1,0,0,-1," . $self->border->left . "," . ( $self->total_size->[1] - $Y - $self->border->bottom ) . ")",
724             );
725 0         0 $ind++;
726             }
727             }
728             elsif ( exists $layer->{ type } && $layer->{ type } eq 'image' )
729             {
730 0         0 my $image_nbr = 1;
731 0         0 foreach my $set ( @{ $layer->data_set } )
  0         0  
732             {
733 0         0 my $raw_img = $set->{ image };
734 0         0 my $img64 = encode_base64( $raw_img, "\n" );
735             my $tag = $svg->image(
736             x => $set->{ x } || 0,
737             y => -$set->{ y } || 0,
738             width => $set->{ width },
739             height => $set->{ height },
740 0   0     0 id => 'image_' . $layer_ind . '_' . $image_nbr,
      0        
741             '-href' => "data:image/png;base64," . $img64,
742             transform => "matrix(1,0,0,1," . $X . "," . $Y . ")",
743             );
744 0         0 $image_nbr++;
745             }
746             # $tag = $svg->image(
747             # x=>100, y=>100,
748             # width=>300, height=>200,
749             # '-href'=>"image.png", #may also embed SVG, e.g. "image.svg"
750             # id=>'image_1'
751             # );
752              
753             }
754             }
755              
756             #######################################
757             ############## Overlay ################
758             #######################################
759 0 0       0 if ( ( ref $layer ) eq 'Graph::ChartSVG::Overlay' )
760             {
761 0         0 $layer_goup = $data_goup->group( id => "data_$layer_ind" );
762 0         0 my $ind = 0;
763 0         0 my $color_hex = $layer->{ color };
764 0 0       0 if ( $layer->{ type } eq 'v' )
765             {
766 0         0 foreach my $start ( keys %{ $layer->{ data_set } } )
  0         0  
767             {
768 0         0 my $stop = $layer->{ data_set }->{ $start };
769             my $k = $layer_goup->rectangle(
770             x => $start,
771             y => -$layer->{ debord_1 },
772             width => $stop - $start,
773             height => $self->{ active_size }->[1] + $layer->{ debord_1 } + $layer->{ debord_2 },
774 0   0     0 style => {
      0        
      0        
775              
776             'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
777             'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
778             'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
779             'fill-rule' => 'nonzero'
780             },
781             id => 'v_overlay_' . $layer_ind . '_' . $ind
782             );
783 0         0 $ind++;
784             }
785             }
786 0 0       0 if ( $layer->{ type } eq 'h' )
787             {
788 0         0 foreach my $start ( keys %{ $layer->{ data_set } } )
  0         0  
789             {
790 0         0 my $stop = $layer->{ data_set }->{ $start };
791             my $k = $layer_goup->rectangle(
792             x => -$layer->{ debord_1 },
793             y => $start,
794             width => $self->{ active_size }->[0] + $layer->{ debord_1 } + $layer->{ debord_2 },
795 0   0     0 height => $stop - $start,
      0        
      0        
796             style => {
797             'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
798             'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
799             'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
800             'fill-rule' => 'nonzero'
801             },
802             id => 'h_overlay_' . $layer_ind . '_' . $ind
803             );
804 0         0 $ind++;
805             }
806             }
807             }
808 0         0 $layer_ind++;
809             }
810              
811 1         5 my $info_data_group = $svg->group( id => "info_data" );
812 1         63 my $obj = Data::Serializer->new( 'compress' => 1 );
813 1         1486 my $tag = $obj->serialize( \@list_data );
814 1         75641 $info_data_group->comment( $tag );
815              
816             #######################################
817             ## grid
818             #######################################
819 1 50       119 if ( defined $self->grid )
820             {
821 1 50       25 if ( defined $self->grid->x )
822             {
823 1         6 my $x_grid_group = $svg->group( id => "x_grid" );
824 1         102 my $x_grid_text = $x_grid_group->group(
825             id => "x_grid_text",
826             transform => "matrix(1,0,0,1, 0," . ( $self->border->bottom ) . " )"
827             );
828 1   50     81 my $thickness = $self->grid->x->thickness || 1;
829 1         23 my $color_hex = $self->grid->x->color;
830 1         3 my $max_length_x;
831 1 50 33     22 $max_length_x = max( map( length, @{ $self->grid->x->label->text } ) )
  1         22  
832             if ( defined( $self->grid->x->label )
833             && ( ref( $self->grid->x->label->text ) eq 'ARRAY' ) );
834 1         3 my $max_length_x2;
835 1 50 33     23 $max_length_x2 = max( map( length, @{ $self->grid->x->label2->text } ) )
  0         0  
836             if ( defined( $self->grid->x->label2 )
837             && ( ref( $self->grid->x->label2->text ) eq 'ARRAY' ) );
838              
839 1         23 for ( my $nbr = $self->grid->x->number - 1 ; $nbr >= 0 ; $nbr-- )
840             {
841 5         109 my $val = $nbr * ( ( ( $self->active_size->[1] ) / ( $self->grid->x->number - 1 ) ) );
842 5         101 my $text_indx = $self->grid->x->number - $nbr - 1;
843              
844 5   50     107 my $tag = $x_grid_group->line(
      50        
845             id => 'x_grid_' . $nbr,
846             x1 => $self->border->left - $self->grid->debord->left,
847             y1 => $self->border->bottom + $val,
848             x2 => $self->border->left + $self->active_size->[0] + $self->grid->debord->right,
849             y2 => $self->border->bottom + $val,
850             style => {
851             'fill' => 'none',
852             'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
853             'stroke' => '#' . ( unpack "a6", $color_hex ) || 0,
854             'stroke-width' => $thickness,
855             'stroke-linecap' => 'butt',
856             'stroke-linejoin' => 'miter',
857             'stroke-opacity' => 1
858             },
859             transform => "matrix(1,0,0,-1," . ( 0 ) . "," . ( $self->total_size->[1] ) . ")",
860             );
861              
862 5 50 33     586 if ( defined $self->grid->x->label && defined $self->grid->x->label->text->[$text_indx] )
863             {
864 5   33     103 my $text_color = ( $self->grid->x->label->color || $color_hex );
865 5   50     107 my $radian = ( $self->grid->x->label->rotation / 180 ) * PI || 0;
866 5         25 my $cos = cos( $radian );
867 5         9 my $sin = sin( $radian );
868 5         108 my $len = length( $self->grid->x->label->text->[$text_indx] );
869              
870 5         8 my $font_style = 'normal';
871 5         10 my $font_weight = 'normal';
872 5   50     104 my $f_style = $self->grid->x->label->style || '';
873 5 50       13 if ( $f_style =~ /(italic)|(oblique)/ )
874             {
875 0         0 $font_style = 'italic';
876             }
877 5 50       11 if ( $f_style =~ /bold/ )
878             {
879 0         0 $font_weight = 'bold';
880             }
881              
882 5         6 my $x_offset = 0;
883 5         7 my $y_offset = 0;
884              
885 5         6 my %style;
886 5 50       102 if ( $self->grid->x->label->align =~ /left/i )
887             {
888 0   0     0 %style = (
      0        
889             'font-family' => $self->grid->x->label->font,
890             'font-size' => $self->grid->x->label->size,
891             'font-style' => $font_style,
892             'font-weight' => $font_weight,
893             'fill' => '#' . ( $text_color || 'ffffff' ),
894             'stroke' => '#' . ( $text_color || '000000' ),
895             'writing-mode' => 'lr',
896             'text-anchor' => 'start',
897             # 'baseline-shift'=> '-10%',
898             );
899 0         0 $x_offset = $self->grid->x->label->size * ( $max_length_x - 1 );
900 0 0       0 if ( $self->grid->x->label->rotation )
901             {
902 0         0 $y_offset = ( $sin * $len * $self->grid->x->label->size ) - ( $sin * $self->grid->x->label->size );
903             }
904             }
905             else
906             {
907 5   50     107 %style = (
      50        
908             'font-family' => $self->grid->x->label->font,
909             'font-size' => $self->grid->x->label->size,
910             'font-style' => $font_style,
911             'font-weight' => $font_weight,
912             'fill' => '#' . ( $text_color || 'ffffff' ),
913             'stroke' => '#' . ( $text_color || '000000' ),
914             'writing-mode' => 'lr',
915             'text-anchor' => 'end',
916             # 'baseline-shift'=> '-10%',
917             );
918             }
919              
920 5         114 my $txt = $x_grid_text->text(
921             x => $self->border->left - $self->grid->debord->left - $self->grid->x->label->space - $x_offset,
922             y => $self->border->top - $self->border->bottom + $val + ( $self->grid->x->label->size * 0.3 ) - $y_offset,
923             style => \%style,
924             # transform => " rotate( " . $self->grid->x->label->rotation . "," . ( $self->border->left - $self->grid->debord->left - $self->grid->x->label->space - $x_offset ) . "," . ( $self->border->bottom + $val - $y_offset ) . " ) ",
925             transform => " rotate( " . $self->grid->x->label->rotation . "," . ( $self->border->left - $self->grid->debord->left - $self->grid->x->label->space - $x_offset ) . "," . ( $self->border->bottom + $val - $y_offset ) . " ) ",
926              
927             );
928 5         383 $txt->tspan( dy => "0" )->cdata( $self->grid->x->label->text->[$text_indx] );
929             }
930              
931             ##########################################
932             # second x label ( right side )
933             ##########################################
934 5 50 33     143 if ( defined $self->grid->x->label2 && defined $self->grid->x->label2->text->[$text_indx] )
935             {
936 0   0     0 my $text_color = ( $self->grid->x->label2->color || $color_hex );
937 0   0     0 my $radian = ( $self->grid->x->label2->rotation / 180 ) * PI || 0;
938 0         0 my $cos = cos( $radian );
939 0         0 my $sin = sin( $radian );
940 0         0 my $len = length( $self->grid->x->label2->text->[$text_indx] );
941 0         0 my $font_style = 'normal';
942 0         0 my $font_weight = 'normal';
943 0   0     0 my $f_style = $self->grid->x->label2->style || '';
944              
945 0 0       0 if ( $f_style =~ /(italic)|(oblique)/ )
946             {
947 0         0 $font_style = 'italic';
948             }
949 0 0       0 if ( $f_style =~ /bold/ )
950             {
951 0         0 $font_weight = 'bold';
952             }
953 0         0 my $x_offset = 0;
954 0         0 my $y_offset = 0;
955              
956 0         0 my %style;
957 0 0       0 if ( $self->grid->x->label2->align =~ /right/i )
958             {
959 0   0     0 %style = (
      0        
960             'font-family' => $self->grid->x->label2->font,
961             'font-size' => $self->grid->x->label2->size,
962             'font-style' => $font_style,
963             'font-weight' => $font_weight,
964             'fill' => '#' . ( $text_color || 'ffffff' ),
965             'stroke' => '#' . ( $text_color || '000000' ),
966             'writing-mode' => 'lr',
967             'text-anchor' => 'end',
968             # 'baseline-shift'=> '-10%',
969             );
970 0         0 $x_offset = $self->grid->x->label2->size * ( $max_length_x2 - 1 );
971 0 0       0 if ( $self->grid->x->label2->rotation )
972             {
973 0         0 $y_offset = ( $sin * ( $max_length_x2 - 1 ) * $self->grid->x->label2->size ) - ( $sin * $self->grid->x->label2->size );
974             }
975             }
976             else
977             {
978 0   0     0 %style = (
      0        
979             'font-family' => $self->grid->x->label2->font,
980             'font-size' => $self->grid->x->label2->size,
981             'font-style' => $font_style,
982             'font-weight' => $font_weight,
983             'fill' => '#' . ( $text_color || 'ffffff' ),
984             'stroke' => '#' . ( $text_color || '000000' ),
985             'writing-mode' => 'lr',
986             'text-anchor' => 'start',
987             # 'baseline-shift'=> '-10%',
988             );
989             }
990 0         0 my $txt = $x_grid_text->text(
991             x => $self->border->left + $self->grid->debord->right + $self->grid->x->label2->space + $x_offset + $self->active_size->[0],
992             # y => $self->border->bottom + $val + ( $self->grid->x->label2->size * 0.3 ) + $y_offset,
993             y => $self->border->top - $self->border->bottom + $val + ( $self->grid->x->label2->size * 0.3 ) - $y_offset,
994              
995             style => \%style,
996             transform => " rotate( " . $self->grid->x->label2->rotation . "," . ( $self->border->left + $self->grid->debord->right + $self->grid->x->label2->space + $x_offset + $self->active_size->[0] ) . "," . ( $self->border->bottom + $val + $y_offset ) . " ) ",
997             );
998 0         0 $txt->tspan( dy => "0" )->cdata( $self->grid->x->label2->text->[$text_indx] );
999             }
1000             }
1001             }
1002              
1003             ####################################
1004             ## grid Y ( vertical )
1005             ####################################
1006 1 50       23 if ( defined $self->grid->y )
1007             {
1008 0         0 my $y_grid_group = $svg->group( id => "y_grid", transform => "matrix(1,0,0,-1, 0," . ( $self->total_size->[1] ) . " )" );
1009 0         0 my $y_grid_text = $y_grid_group->group(
1010             id => "y_grid_text",
1011             transform => "matrix(1,0,0,-1, 0," . ( $self->border->bottom ) . " )"
1012             );
1013 0   0     0 my $thickness = $self->grid->y->thickness || 1;
1014 0         0 my $color_hex = $self->grid->y->color;
1015 0         0 my $max_length_y;
1016 0 0 0     0 $max_length_y = max( map( length, @{ $self->grid->y->label->text } ) )
  0         0  
1017             if ( defined( $self->grid->y->label )
1018             && ( ref( $self->grid->y->label->text ) eq 'ARRAY' ) );
1019 0         0 my $max_length_y2;
1020 0 0 0     0 $max_length_y2 = max( map( length, @{ $self->grid->y->label2->text } ) )
  0         0  
1021             if ( defined( $self->grid->y->label2 )
1022             && ( ref( $self->grid->y->label2->text ) eq 'ARRAY' ) );
1023              
1024 0         0 for my $nbr ( 0 .. ( $self->grid->y->number - 1 ) )
1025             {
1026 0         0 my $val = ( ( $nbr ) * ( $self->active_size->[0] / ( $self->grid->y->number - 1 ) ) );
1027              
1028 0   0     0 my $tag = $y_grid_group->line(
      0        
1029             id => 'y_grid_' . $nbr,
1030             x1 => $self->border->left + $val,
1031             y1 => $self->border->bottom - $self->grid->debord->bottom,
1032             x2 => $self->border->left + $val,
1033             y2 => $self->border->bottom + $self->active_size->[1] + $self->grid->debord->top,
1034             style => {
1035             'fill' => 'none',
1036             'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
1037             'stroke' => '#' . ( unpack "a6", $color_hex ) || 0,
1038             'stroke-width' => $thickness,
1039             'stroke-linecap' => 'butt',
1040             'stroke-linejoin' => 'miter',
1041             'stroke-opacity' => 1
1042             },
1043             );
1044              
1045 0 0 0     0 if ( defined $self->grid->y->label && defined $self->grid->y->label->text->[$nbr] )
1046             {
1047 0   0     0 my $text_color = $self->grid->y->label->color || $color_hex;
1048 0         0 my $font_style = 'normal';
1049 0         0 my $font_weight = 'normal';
1050 0   0     0 my $f_style = $self->grid->y->label->style || '';
1051              
1052 0 0       0 if ( $f_style =~ /(italic)|(oblique)/ )
1053             {
1054 0         0 $font_style = 'italic';
1055             }
1056 0 0       0 if ( $f_style =~ /bold/ )
1057             {
1058 0         0 $font_weight = 'bold';
1059             }
1060              
1061 0   0     0 my $radian = ( $self->grid->y->label->rotation / 180 ) * PI || 0;
1062 0         0 my $cos = cos( $radian );
1063 0         0 my $sin = sin( $radian );
1064 0         0 my $len = length( $self->grid->y->label->text->[$nbr] );
1065 0         0 my $x_offset = 0;
1066 0         0 my $y_offset = 0;
1067              
1068 0         0 my $l = ( 0.628 * $self->grid->y->label->size * $max_length_y ) - 5.052;
1069 0         0 my %style;
1070 0 0       0 if ( $self->grid->y->label->align =~ /left/i )
1071             {
1072 0   0     0 %style = (
      0        
1073             'font-family' => $self->grid->y->label->font,
1074             'font-size' => $self->grid->y->label->size,
1075             'font-style' => $font_style,
1076             'font-weight' => $font_weight,
1077             'fill' => '#' . ( $text_color || 'ffffff' ),
1078             'stroke' => '#' . ( $text_color || '000000' ),
1079             'writing-mode' => 'lr',
1080             'text-anchor' => 'end',
1081             # 'baseline-shift'=> '-10%',
1082             );
1083 0 0       0 if ( $self->grid->y->label->rotation )
1084             {
1085 0         0 $y_offset = ( $self->grid->debord->bottom + $self->grid->y->label->space ) * 2;
1086             }
1087             }
1088             else
1089             {
1090 0   0     0 %style = (
      0        
1091             'font-family' => $self->grid->y->label->font,
1092             'font-size' => $self->grid->y->label->size,
1093             'font-style' => $font_style,
1094             'font-weight' => $font_weight,
1095             'fill' => '#' . ( $text_color || 'ffffff' ),
1096             'stroke' => '#' . ( $text_color || '000000' ),
1097             'writing-mode' => 'lr',
1098             'text-anchor' => 'start',
1099             # 'baseline-shift'=> '-10%',
1100             );
1101 0         0 $x_offset = $self->grid->y->label->size * ( ( 0.7025 * $max_length_y ) - 1.601 ) * $cos;
1102 0         0 $y_offset = ( $l * $sin ) + ( $self->grid->debord->bottom + $self->grid->y->label->space ) + ( $l / $cos );
1103              
1104             }
1105              
1106 0         0 my $txt = $y_grid_text->text(
1107             x => $self->border->left + $val - $x_offset,
1108             y => $y_offset,
1109             style => \%style,
1110             transform => " rotate( " . $self->grid->y->label->rotation . "," . ( $self->border->left + $val - $x_offset ) . ", " . $y_offset . " ) ",
1111             );
1112 0         0 $txt->tspan( dy => "0" )->cdata( $self->grid->y->label->text->[$nbr] );
1113              
1114             }
1115              
1116 0 0 0     0 if ( defined $self->grid->y->label2 && defined $self->grid->y->label2->text->[$nbr] )
1117             {
1118 0   0     0 my $text_color = $self->grid->y->label2->color || $color_hex;
1119 0         0 my $font_style = 'normal';
1120 0         0 my $font_weight = 'normal';
1121 0   0     0 my $f_style = $self->grid->y->label2->style || '';
1122              
1123 0 0       0 if ( $f_style =~ /(italic)|(oblique)/ )
1124             {
1125 0         0 $font_style = 'italic';
1126             }
1127 0 0       0 if ( $f_style =~ /bold/ )
1128             {
1129 0         0 $font_weight = 'bold';
1130             }
1131              
1132 0   0     0 my $radian = ( $self->grid->y->label2->rotation / 180 ) * PI || 0;
1133 0         0 my $cos = cos( $radian );
1134 0         0 my $sin = sin( $radian );
1135 0         0 my $len = length( $self->grid->y->label2->text->[$nbr] );
1136 0         0 my $x_offset = 0;
1137 0         0 my $y_offset = 0;
1138             # my $l = ( 0.628 * $self->grid->y->label2->size * $max_length_y2 ) - 5.052;
1139 0         0 my $l = $self->grid->y->label2->size * $max_length_y2 * $self->grid->y->label2->font_scaling;
1140 0         0 my %style;
1141              
1142 0 0       0 if ( $self->grid->y->label2->align =~ /left/i )
1143             {
1144 0   0     0 %style = (
      0        
1145             'font-family' => $self->grid->y->label2->font,
1146             'font-size' => $self->grid->y->label2->size,
1147             'font-style' => $font_style,
1148             'font-weight' => $font_weight,
1149             'fill' => '#' . ( $text_color || 'ffffff' ),
1150             'stroke' => '#' . ( $text_color || '000000' ),
1151             'writing-mode' => 'lr',
1152             'text-anchor' => 'start',
1153             # 'baseline-shift'=> '-10%',
1154             );
1155 0 0       0 if ( $self->grid->y->label2->rotation )
1156             {
1157 0         0 $y_offset = -( $self->active_size->[1] + $self->grid->debord->top + $self->grid->y->label2->space );
1158             }
1159             }
1160             else
1161             {
1162 0   0     0 %style = (
      0        
1163             'font-family' => $self->grid->y->label2->font,
1164             'font-size' => $self->grid->y->label2->size,
1165             'font-style' => $font_style,
1166             'font-weight' => $font_weight,
1167             'fill' => '#' . ( $text_color || 'ffffff' ),
1168             'stroke' => '#' . ( $text_color || '000000' ),
1169             'writing-mode' => 'lr',
1170             'text-anchor' => 'end',
1171             # 'baseline-shift'=> '-10%',
1172             );
1173 0         0 $x_offset = ( $l * $cos ) + $val + $self->border->left;
1174 0         0 $y_offset = ( $l * $sin ) - $self->grid->debord->bottom - $self->active_size->[1] - $self->grid->debord->top - $self->grid->y->label2->space;
1175             }
1176 0         0 my $txt = $y_grid_text->text(
1177             x => $x_offset,
1178             y => $y_offset,
1179             style => \%style,
1180             transform => " rotate( " . $self->grid->y->label2->rotation . "," . ( $x_offset ) . ", " . $y_offset . " ) ",
1181             );
1182 0         0 $txt->tspan( dy => "0" )->cdata( $self->grid->y->label2->text->[$nbr] );
1183             }
1184             }
1185             }
1186             }
1187              
1188             #######################################
1189             ####### Frame #######
1190             #######################################
1191 1 50       25 if ( defined $self->frame )
1192             {
1193             my $k = $svg->rectangle(
1194             x => $self->{ border }->left,
1195             y => $self->{ border }->top,
1196             width => $self->{ active_size }->[0],
1197             height => $self->{ active_size }->[1],
1198             # rx => 10, ry => 5,
1199             style => {
1200             stroke => '#' . ( ( $self->frame )->{ color } ),
1201             fill => 'none',
1202 1   50     25 'stroke-width' => ( $self->frame )->{ thickness } || 0,
1203             },
1204             id => 'frame'
1205             );
1206             }
1207              
1208             #######################################
1209             ####### TAG #######
1210             #######################################
1211 1 0 33     87 if ( exists $self->{ tag } && $self->{ tag } )
1212             {
1213 0         0 my $tag_group = $svg->group( id => "serial_tag" );
1214 0         0 my $obj = Data::Serializer->new( 'compress' => 1 );
1215 0         0 my $tag = $obj->serialize( $self->{ tag } );
1216 0         0 $tag_group->comment( $tag );
1217             }
1218              
1219             $self->image(
1220 1         10 $svg->xmlify(
1221             -namespace => "svg",
1222             -pubid => "-//W3C//DTD SVG 1.0//EN",
1223             -standalone => "no",
1224             -inline => 1
1225             )
1226             );
1227 1         28 $self->{ svg_raw } = $svg;
1228             }
1229              
1230             sub sum_array
1231             {
1232 0     0 0 0 my $col = shift;
1233 0         0 my $line = shift;
1234 0         0 my $data = shift;
1235 0         0 my $res = 0;
1236 0         0 for my $c_idx ( 0 .. $col )
1237             {
1238 0   0     0 $res += $data->[$c_idx][$line] || 0;
1239             }
1240 0         0 return $res;
1241             }
1242              
1243             sub reduce
1244             {
1245 1     1 1 886 my $self = shift;
1246 1         6 my %object_hash = ( @_ );
1247            
1248 1         2 my $object = \%object_hash;
1249              
1250 1         4 my $width_out = $self->{ active_size }->[0];
1251 1   50     5 my $start = $object->{ start } || 0;
1252 1   50     7 my $percentile_value = $object->{ percentile } || 0.95;
1253 1   33     5 my $end = $object->{ end } || $width_out;
1254 1         2 my @data_in = @{ $object->{ data } };
  1         72  
1255 1         3 my $data_in_size = scalar @data_in;
1256            
1257 1     1   8 no warnings "all";
  1         1  
  1         144  
1258 1         25 my @perc = sort { $a <=> $b } @data_in[ $start .. $end ];
  310         341  
1259 1         10 my $prec_ind = int( scalar( @perc ) * $percentile_value );
1260 1         2 my @data_out;
1261             my %STATS;
1262 1   50     5 $STATS{ perc } = $perc[$prec_ind] || 0;
1263 1   50     17 $STATS{ min } = ( min @data_in ) || 0;
1264 1         13 $STATS{ max } = max @data_in;
1265 1         20 $STATS{ sum } = sum @data_in;
1266 1         96 $STATS{ avg } = $STATS{ sum } / scalar( @data_in );
1267 1     1   5 use warnings "all";
  1         2  
  1         1166  
1268            
1269 1         4 my $width_in = $end - $start + 1;
1270              
1271 1         2 my $data_dot = ( scalar @data_in ) / $width_in;
1272 1         3 my $data_dot_int = int( $data_dot + 0.5 );
1273 1         1 my @chars;
1274              
1275 1 50       4 if ( exists $object->{ init } )
1276             {
1277             # @data_out = map( $object->{ init }, @data_out );
1278 1   50     47 @data_out = ( $object->{ init } || 0 ) x $width_out;
1279             }
1280              
1281 1 50       4 if ( $#data_out <= $#data_in )
1282             {
1283 1         2 my $old_val = 0;
1284 1         5 for ( my $dot = $start ; $dot <= $end ; $dot++ )
1285             {
1286 311         385 my $s = ( $dot - $start ) * $data_dot;
1287 311         365 my $e = $s + $data_dot - 1;
1288 311         512 my @slice = @data_in[ $s .. $e ];
1289              
1290 311 50       534 if ( scalar( @slice ) )
1291             {
1292 311 50 33     748 if ( exists $object->{ type } && $object->{ type } =~ /^nrz$/i )
1293             {
1294 0         0 foreach my $idx ( 0 .. $#slice )
1295             {
1296 0 0       0 if ( $slice[$idx] == 0 )
1297             {
1298 0         0 $slice[$idx] = $old_val;
1299             }
1300             else
1301             {
1302 0         0 $old_val = $slice[$idx];
1303             }
1304             }
1305             }
1306 311 50       501 if ( scalar( @slice ) > 1 )
1307             {
1308 311         724 $data_out[$dot] = sum( @slice ) / scalar( @slice );
1309             }
1310             else
1311             {
1312 0         0 $data_out[$dot] = 0;
1313             }
1314             }
1315             else
1316             {
1317 0         0 $data_out[$dot] = 0;
1318             }
1319 311         424 $STATS{ last } = $dot;
1320 311         791 $STATS{ last_val } = $data_in[ $end - 1 ];
1321             }
1322             }
1323             else
1324             {
1325 0 0 0     0 if ( exists $object->{ type } && $object->{ type } =~ /^line|nrz$/i )
1326             {
1327 0         0 my $dot = 0;
1328 0         0 my $old_val = 0;
1329 0         0 W: while ( $dot <= $width_in )
1330             {
1331 0         0 my $ind = ( int( ( $dot / ( $width_in / $data_in_size ) ) ) );
1332 0   0     0 my $val1 = ( $ind > $#data_in ? $data_in[-1] : $data_in[$ind] ) || 0;
1333 0   0     0 my $val2 = ( ( $ind + 1 ) > $#data_in ? $data_in[-1] : $data_in[ ( $ind + 1 ) ] ) || 0;
1334              
1335 0         0 my $inc = ( $val2 - $val1 ) / ( ( $width_in / $data_in_size ) );
1336 0   0     0 my $val = $val1 || 0;
1337 0         0 for ( 0 .. ( $width_in / $data_in_size ) )
1338             {
1339 0         0 $STATS{ last } = $dot;
1340 0 0       0 last W if ( $dot >= $width_in );
1341 0 0 0     0 if ( $object->{ type } =~ /^nrz$/i && ( !$val2 || !$val ) )
      0        
1342             {
1343 0         0 carp "in nrz [ $dot + $start ] = $old_val";
1344 0         0 $data_out[ $dot + 1 ] = $old_val;
1345             # $data_out[ $dot ] = $val;
1346             # $start = $dot;
1347             }
1348             # else
1349             # {
1350 0         0 $data_out[ $dot + $start ] = $val;
1351 0         0 $old_val = $val;
1352 0         0 $val += $inc;
1353             # }
1354              
1355 0 0       0 if ( $inc > 0 )
1356             {
1357 0 0       0 $val = $val > $val2 ? $val2 : $val;
1358             }
1359             else
1360             {
1361 0 0       0 $val = $val < $val2 ? $val2 : $val;
1362             }
1363              
1364 0         0 $dot++;
1365             }
1366             }
1367             }
1368             else
1369             {
1370 0         0 $STATS{ last } = $width_in;
1371 0         0 for ( my $dot = 1 ; $dot <= $width_in ; $dot++ )
1372             {
1373 0         0 my $ind = ( int( ( $dot / ( $width_in / $data_in_size ) ) ) );
1374 0 0 0     0 $data_out[ $dot + $start - 1 ] = $ind > $#data_in ? $data_in[-1] || 0 : $data_in[$ind] || 0;
      0        
1375             }
1376             }
1377             }
1378 1 50       28 return wantarray ? ( \@data_out, \%STATS ) : \@data_out;
1379             }
1380              
1381             sub img_from
1382             {
1383 0     0 0   my $self = shift;
1384 0           my $file = shift;
1385 0           my $obj = Data::Serializer->new( 'compress' => 1 );
1386              
1387 0           my $svg = SVG::Parser->new()->parsefile( $file );
1388              
1389 0           my $info_data = $svg->getElementByID( 'info_data' );
1390             # my $element = $info_data->cloneNode( 1 );
1391             # my $c = ($element->getElements('comment'))[0];
1392 0           my $kid = $info_data->getFirstChild();
1393              
1394 0           my $com = $kid->xmlify();
1395              
1396 0           $com =~ s/^\s*<!--\s*//m;
1397 0           $com =~ s/\s*-->$//;
1398              
1399             # my @comments = $kid->getElements('comment');
1400             # carp "**" x 50;
1401             # carp Dumper(\@c);
1402             # my $comments = $c->{ '-comment' };
1403             # foreach my $comment ( @comments )
1404             # {
1405             # # $comment =~ s/^\s*//;
1406             # carp "<$comment>";
1407             #
1408 0           my $tag = $obj->deserialize( $com );
1409             # carp Dumper($comment->{ '-comment' });
1410 0           foreach my $data_tag ( @$tag )
1411             {
1412             # carp Dumper( $data_tag );
1413 0           my $data = $svg->getElementByID( $data_tag );
1414 0           my $kid_data = $data->getFirstChild();
1415             # carp Dumper($kid_data);
1416             # carp $kid_data->xmlify();
1417 0           my $data_element = $kid_data->cloneNode( 1 );
1418             # carp Dumper( $data_element );
1419             # my $parser=new SVG::Parser();
1420             #
1421             # my $svg1=$parser->parse_string($kid_data->xmlify());
1422             # carp Dumper($svg1);
1423              
1424             # # my $data_element = $data->cloneNode( 2 );
1425             # carp Dumper($data);
1426             }
1427             #
1428             # }
1429              
1430             # my @rectangles=$data->getElements("");
1431             # carp Dumper(\@rectangles);
1432             # foreach my $sib1 ( @$ref)
1433             # {
1434             # carp "**" x 50;
1435             # foreach my $sib2 ( @{$sib1->getSiblings()} )
1436             # {
1437             # carp Dumper($sib2);
1438             #
1439             # }
1440             #
1441             # }
1442 0           $self;
1443             }
1444              
1445             # __PACKAGE__->meta->make_immutable;
1446             #
1447             1;
1448              
1449             =head1 METHODS
1450            
1451             OO interface
1452              
1453             =head2 Graph::ChartSVG->new
1454              
1455             =over
1456              
1457             Create a new Chart
1458              
1459             possible parameters are :
1460              
1461             =back
1462              
1463             =head3 active_size
1464              
1465             =over
1466              
1467             =back
1468              
1469             an array ref with x,y size of the active graph ( without the reserved border for label )
1470            
1471            
1472             =head3 bg_color
1473            
1474             =over
1475              
1476             =back
1477              
1478             an hex color for the global background color
1479            
1480             =head3 frame
1481            
1482             =over
1483              
1484             =back
1485              
1486             a Frame object to surround the active part of the graph
1487              
1488             =head3 grid
1489            
1490             =over
1491              
1492             =back
1493              
1494             a Grid oject to add to the graph
1495            
1496            
1497             =head3 overlay
1498            
1499             =over
1500              
1501             =back
1502              
1503             a Overlay to add on top of the graph ( useful to enhance a period in alarm )
1504            
1505             =head3 layer
1506            
1507             =over
1508              
1509             =back
1510              
1511             a Layer object
1512            
1513             =head3 border
1514            
1515             =over
1516              
1517             =back
1518              
1519             a Border object ( = some extra space to fit aroubd the active graph to allow label.
1520             This increase the actual_size and create the total_size)
1521            
1522             =head3 tag
1523            
1524             =over
1525              
1526             =back
1527              
1528             a Tag objet ( if missing create a automatically incremented one )
1529            
1530             =head3 glyph
1531            
1532             =over
1533              
1534             =back
1535              
1536             a Glyph object to add on the graph ( like a arrow to point at the end of the current data )
1537            
1538              
1539              
1540             my $graph = Graph::ChartSVG->new( active_size => \@size, bg_color => 'FCF4C6', frame => $f, png_tag => 1 );
1541              
1542              
1543             =over
1544              
1545             =back
1546              
1547             =head2 Frame->new
1548              
1549             =over
1550              
1551             =back
1552            
1553             =head3 color
1554            
1555             =over
1556              
1557             =back
1558              
1559             a hex color of the frame
1560            
1561             =head3 thickness
1562            
1563             =over
1564              
1565             =back
1566              
1567             thickness of the frame
1568              
1569             my $f = Frame->new( color => 'ff0000', thickness => 3 );
1570              
1571             =over
1572              
1573             =back
1574              
1575             =head2 Border->new
1576              
1577             =over
1578              
1579             =back
1580            
1581             =head3 top
1582              
1583             space between the active part of the graph and the top of the image
1584            
1585             =over
1586              
1587             =back
1588            
1589             =head3 bottom
1590              
1591             space between the active part of the graph and the bottom of the image
1592            
1593             =over
1594              
1595             =back
1596            
1597             =head3 left
1598              
1599             space between the active part of the graph and the left of the image
1600              
1601             =over
1602              
1603             =back
1604            
1605             =head3 right
1606              
1607             space between the active part of the graph and the right of the image
1608            
1609             my $b = Border->new( top => 200, bottom => 100, left => 80, right => 200 );
1610              
1611             =over
1612              
1613             =back
1614              
1615             =head2 $graph->border
1616              
1617             method to add or change a border on the graph
1618            
1619             =over
1620              
1621             =back
1622              
1623             =head2 Data->new
1624              
1625             create a new set of data
1626            
1627             =over
1628              
1629             =back
1630            
1631             =head3 type
1632              
1633             the type of graph used for that data set.
1634             could be :
1635            
1636             =over
1637              
1638             =item
1639              
1640             line
1641              
1642             a normal line
1643              
1644             =item
1645              
1646             line_up
1647              
1648             a line with zero starting at the middle of the active graph with increasing value in the top direction
1649              
1650             =item
1651              
1652             line_down
1653              
1654             a line with zero starting at the middle of the active graph with increasing value in the bottom direction
1655              
1656             =item
1657              
1658             bar
1659              
1660             a filled graph
1661              
1662             =item
1663              
1664             bar_up
1665              
1666             a filled graph with zero starting at the middle of the active graph with increasing value in the top direction
1667              
1668             =item
1669              
1670             bar_down
1671              
1672             a filled graph with zero starting at the middle of the active graph with increasing value in the bottom direction
1673            
1674             =item
1675              
1676             line_stack
1677              
1678             a set of line stacked under each other
1679              
1680             =item
1681              
1682             line_stack_up
1683              
1684             a set of line stacked under each other with zero starting at the middle of the active graph with increasing value in the top direction
1685              
1686             =item
1687              
1688             line_stack_down
1689              
1690             a set of line stacked under each otherwith zero starting at the middle of the active graph with increasing value in the bottom direction
1691            
1692             =item
1693              
1694             bar_stack
1695              
1696             a set of filled graph stacked under each other
1697              
1698             =item
1699              
1700             bar_stack_up
1701              
1702             a set of filled graph stacked under each other with zero starting at the middle of the active graph with increasing value in the top direction
1703              
1704             =item
1705              
1706             bar_stack_down
1707              
1708             a set of filled graph stacked under each other with zero starting at the middle of the active graph with increasing value in the bottom direction
1709            
1710              
1711             =back
1712            
1713             =head3 color
1714              
1715             the hex color of the graph
1716              
1717             if Data is stack type, it should be a array ref with all the hex color
1718              
1719             =over
1720              
1721             =back
1722            
1723             =head3 thickness
1724              
1725             the thickness of the line used
1726              
1727             in bar the thickness of the border
1728              
1729              
1730             =over
1731              
1732             =back
1733            
1734             =head3 label
1735              
1736             a label to set to the SVG object
1737              
1738             =over
1739              
1740             =back
1741            
1742             =head3 offset
1743              
1744             a vertical offset ( where the zero start )
1745              
1746             =over
1747              
1748             =back
1749              
1750             =head4 example:
1751            
1752             my $l = Data->new( type => 'line', color => 'ff9800A0', thickness => 3, label => 'oblique' , offset => 50 );
1753              
1754             =over
1755              
1756             =back
1757              
1758             =head2 $l->data_set( ... )
1759              
1760             include a set of data to a Data object
1761              
1762             it is an array ref with all data
1763              
1764             or an array ref of array ref for the stack type of Graph
1765              
1766             =over
1767              
1768             =back
1769              
1770             =head4 example:
1771              
1772             $l->data_set( \@data1 );
1773            
1774            
1775             =over
1776              
1777             =back
1778              
1779             =head2 $graph->add( ... )
1780              
1781             add a element in the graph
1782              
1783             the element could be:
1784              
1785             =over
1786              
1787             =item
1788              
1789             data_set
1790              
1791             =item
1792              
1793             Glyph
1794              
1795             =item
1796              
1797             Overlay
1798              
1799              
1800             =back
1801              
1802             =head4 example:
1803              
1804             $graph->add( $l );
1805            
1806             method to add an object to a graph
1807              
1808             =over
1809              
1810             =back
1811              
1812             =head2 graph->grid(
1813             debord => ...,
1814             x => Grid_object,
1815             y = > Grid_object,
1816             )
1817              
1818             $graph->grid(
1819              
1820             Grid->new(
1821             debord => Border->new( # the debord size of the grid ( = the grid is greater than the active size )
1822             top => 20,
1823             bottom => 10,
1824             left => 10,
1825             right => 10 ),
1826              
1827             x => Grid_def->new( # label on the left border of the graph
1828             color => '1292FF',
1829             number => 10,
1830             thickness => 2,
1831             label => Label->new(
1832             font => 'verdana',
1833             color => '0000ff',
1834             size => 15,
1835             text => \@text, # a array ref with all the text to set on the left border of the graph
1836             space => 10, # space between the end of the label and the start of the grid
1837             align => 'right',
1838             rotation => -30,
1839             ),
1840             label2 => Label->new( # label on the right border of the graph
1841             font => 'times',
1842             color => '0000ff',
1843             s ize => 20,
1844             text => \@text2,
1845             space => 10,
1846             # align => 'right',
1847             # rotation => -45,
1848             ),
1849             ),
1850              
1851             y => Grid_def->new(
1852             color => '00fff0',
1853             number => $VERT_GRID,
1854             thickness => 1,
1855             label => Label->new( # label on the left bottom of the graph
1856             font => 'verdana',
1857             color => 'ff00ff',
1858             size => 14,
1859             text => \@DATE,
1860             space => 10,
1861             rotation => -30,
1862             align => 'right',
1863             ),
1864             label2 => Label->new( # label on the left top of the graph
1865             font => 'verdana',
1866             font_scaling => 0.558,
1867             color => 'B283FF',
1868             size => 16,
1869             text => \@DATE2,
1870             align => 'right',
1871             space => 0,
1872             rotation => -30,
1873             ),
1874             )
1875             )
1876             );
1877              
1878            
1879             =over
1880              
1881             =back
1882              
1883             =head2 $graph->label( label_name);
1884              
1885             search for the layer with the label = label_name
1886              
1887             in array context return ( ref_data, layer_level)
1888             in scalar context return ref_data
1889              
1890             (ref_data = an array ref with the data set )
1891              
1892              
1893             my ( $Mal, $Mlan ) = $graph->label( 'src_all' );
1894            
1895             =over
1896              
1897             =back
1898              
1899             =head2 $graph->move( from, to );
1900              
1901             Move a speciied layer to another level.
1902              
1903             the other layer are shifted to allow the insert
1904              
1905             =over
1906              
1907             =back
1908              
1909             =head2 Glyph->new
1910              
1911             3 type of glyph are available:
1912              
1913             'line' draw a polyline or polygon
1914             'text' draw a text
1915             'image' include a PNG image Embeded in the SVG
1916              
1917             To draw an arrow:
1918              
1919             my $g1 = Glyph->new(
1920             x => $graph->border->left ,
1921             y =>$graph->active_size->[1] +$graph->border->bottom ,
1922             type => 'line',
1923             filled => 1, # if 1 = fill the polygon ( be sure to correctly close the path )
1924             color => '0faFff',
1925             data_set => [
1926             {
1927             data => [ [ 0, 0 ], [ 8, 10 ], [ 0, 10 ], [ 0, 10 + 20 ], [ 0, 10 ], [ -8, 10 ], [ 0, 0 ] ], # the list of point to create the polyline
1928             thickness => 3
1929             }
1930             ]
1931             );
1932              
1933              
1934             To write 2 text label ( in one Glyph )
1935              
1936             $g = Glyph->new(
1937             label =>'label_max',
1938             x => 100 ,
1939             y =>200,
1940             type => 'text',
1941             color =>0xff0000,
1942             size => 9, # if the glyph's type is 'text', this is the font size
1943             font => 'Verdana', # the TrueType font to use
1944             data_set => [ # the data set contain an array with all the text to plot followed by the relative position + the optional rotation
1945             {
1946             text => "hello text 1",
1947             x => 0, # the relative position in x for that specific text
1948             y => 15, # the relative position in yfor that specific text
1949             anchor => 'end', # the text anchor ( could be start, middle or end )
1950             rotation => -45, # a rotation in ° in trigonometric direction ( anti-clock )
1951             style => 'oblique' # could be normal (default) ,| italic = oblique
1952             },
1953             {
1954             text =>"Bye text 2",
1955             x => 60, # the relative position in x for that specific text
1956             y => 15, # the relative position in yfor that specific text
1957             anchor => 'end',
1958             # rotation => -45,
1959             style => 'oblique'
1960             },
1961             ],
1962             );
1963            
1964            
1965             To inlude a PNG image ( the image is encoded with MIME::Base64 )
1966            
1967             $g = Glyph->new(
1968             label => 'port_label',
1969             x => $graph->active_size->[0] + $graph->border->left + 250,
1970             y => $graph->active_size->[1] + $graph->border->top+$graph->border->bottom -4,
1971             type => 'image',
1972             data_set => [
1973             {
1974             image => $img_bin,
1975             x => 0,
1976             y => -5,
1977             width => $buf_x, # the width of the image
1978             height => $buf_y, # the height of the image
1979             },
1980             ],
1981             );
1982            
1983             =over
1984              
1985             =back
1986              
1987             =head2 $graph>image
1988              
1989             return the SVG image ( to be writed in a file )
1990              
1991             open( my $IMG, '>', $file_svg ) or die $!;
1992             binmode $IMG;
1993             print $IMG $graph>image;
1994             close $IMG;
1995            
1996             !!!! This object is only available after the render method !!!!
1997              
1998             =over
1999              
2000             =back
2001              
2002             =head2 $graph->reduce( data => \@dot1,
2003             start => 50,
2004             end => 360,
2005             init => 300 );
2006            
2007             This method allow to create a set of data directly usable a a data_set.
2008             If there are more plotting value in the input data then the size of the graph, use some average to create the plotting dot
2009             If there are lower plotting value in the input data then the size of the graph, fill the gap to smooth the graph
2010             data = an array ref with the input data
2011             start = where the data start in the reduced data ( if missing =0 )
2012             end = where the data end in the reduced data ( f missing = end of the active size graph )
2013             init = a default value to set the element when there is no data to add, like before start or after end ( if missing use undef )
2014              
2015              
2016             return a array of 2 element
2017              
2018             The first one contains an array refwith the reduced set of data
2019             The second a hash ref with the statistic info
2020              
2021             $VAR1 = {
2022             'perc' => 345,
2023             'last_val' => 359,
2024             'avg' => '400',
2025             'min' => 0,
2026             'last' => 360,
2027             'max' => 800,
2028             'sum' => 320400
2029             };
2030              
2031              
2032              
2033             =over
2034              
2035             =back
2036              
2037             =head2 $graph->render
2038              
2039             create the SVG from all the objects
2040              
2041             =over
2042              
2043             =back