File Coverage

blib/lib/SVG/Graph/Frame.pm
Criterion Covered Total %
statement 137 198 69.1
branch 48 94 51.0
condition 8 23 34.7
subroutine 33 36 91.6
pod 23 30 76.6
total 249 381 65.3


line stmt bran cond sub pod time code
1             package SVG::Graph::Frame;
2              
3 7     7   42 use base SVG::Graph::Data;
  7         14  
  7         14077  
4 7     7   80 use strict;
  7         19  
  7         266  
5 7     7   47 use Data::Dumper;
  7         16  
  7         26601  
6              
7             =head2 new
8              
9             Title : new
10             Usage : you should not be calling new, see add_frame
11             Function:
12             Example :
13             Returns :
14             Args :
15              
16              
17             =cut
18              
19             sub new {
20 10     10 1 62 my($class, %args) = @_;
21 10         27 my $self = bless {}, $class;
22 10         71 $self->init(%args);
23 10         39 return $self;
24             }
25              
26             =head2 init
27              
28             Title : init
29             Usage :
30             Function:
31             Example :
32             Returns :
33             Args :
34              
35              
36             =cut
37              
38             sub init {
39 10     10 1 43 my($self, %args) = @_;
40              
41             # die "you must provide a 'data' arg to new()" unless $args{data};
42 10         40 $self->_parent_svg($args{svg});
43              
44 10         34 foreach my $arg (keys %args){
45 60         102 my $meth = 'add_'.$arg;
46 60         227 $self->$meth($args{$arg});
47             }
48              
49 10         445 my $id = 'n'.sprintf("%07d",int(rand(9999999)));
50 10         17 my $group;
51              
52 10 50       921 if($self->frame_transform eq "top") {
    50          
    50          
    50          
53 0         0 $group=$self->_parent_svg->svg->group(id=> $id);
54             }
55             elsif($self->frame_transform eq "left") {
56              
57 0         0 my $scaley = $self->xsize/$self->ysize;
58 0         0 my $scalex = $self->ysize/$self->xsize;
59            
60 0         0 my $translateoffx = -$self->xoffset;
61 0         0 my $translateoffy = -$self->yoffset;
62              
63 0         0 my $translatey = $self->ysize + $self->yoffset;
64 0         0 my $translatex = $self->xoffset;
65            
66 0         0 $group = $self->_parent_svg->svg->group(id => $id, transform =>"translate($translatex, $translatey) scale($scaley, $scalex) rotate(-90) translate($translateoffx, $translateoffy)");
67             }
68             elsif($self->frame_transform eq "right") {
69            
70 0         0 my $scalex = $self->xsize/$self->ysize;
71 0         0 my $scaley = $self->ysize/$self->xsize;
72              
73 0         0 my $translateoffx = -$self->xoffset;
74 0         0 my $translateoffy = -$self->yoffset;
75              
76 0         0 my $translatey = $self->yoffset;
77 0         0 my $translatex = $self->xsize + $self->xoffset;
78              
79 0         0 $group=$self->_parent_svg->svg->group(id => $id, transform => "translate($translatex, $translatey) scale($scalex, $scaley) rotate(90) translate($translateoffx, $translateoffy)");
80              
81             }
82             elsif($self->frame_transform eq "bottom") {
83              
84 0         0 my $translateoffx = -$self->xoffset;
85 0         0 my $translateoffy = -$self->yoffset;
86              
87 0         0 my $translatex = $self->xsize + $self->xoffset;
88 0         0 my $translatey = $self->ysize + $self->yoffset;
89 0         0 $group=$self->_parent_svg->svg->group(id => $id, transform => "translate($translatex, $translatey) rotate(180) translate($translateoffx, $translateoffy)");
90              
91             }
92             else {
93 10         36 $group=$self->_parent_svg->svg->group(id=> $id);
94             }
95            
96 10         823 $self->svg($group);
97 10         94 $self->is_changed(1);
98             }
99              
100             =head2 add_glyph
101              
102             Title : add_glyph
103             Usage : $frame->add_glyph( 'glyph_name', glyph_args=>arg)
104             Function: adds a glyph to the Frame object
105             Returns : a SVG::Graph::Glyph::glyph_name object
106             Args : glyph dependent
107              
108              
109             =cut
110              
111             sub add_glyph {
112 14     14 1 1018 my($self, $glyphtype, %args) = @_;
113              
114 14   50     59 my $class = 'SVG::Graph::Glyph::'.$glyphtype || 'generic';
115 14 50       1070 eval "require $class"; if($@){ die "couldn't load $class: $@" };
  14         75  
  0         0  
116              
117 14         109 my $glyph = $class->new(%args, svg => $self->svg, group => $self,
118             xsize=>$self->xsize,
119             ysize=>$self->ysize,
120             xoffset=>$self->xoffset,
121             yoffset=>$self->yoffset,
122             );
123 14         26 push @{$self->{glyphs}}, $glyph;
  14         39  
124 14         42 return $glyph;
125             }
126              
127             =head2 add_frame
128              
129             Title : add_frame
130             Usage : my $frame = $graph->add_frame
131             Function: adds a Frame to the current Frame
132             Returns : a SVG::Graph::Frame object
133             Args : (optional) frame_transform => 'top' default orientation
134             'bottom' rotates graph 180 deg (about the center)
135             'right' points top position towards right
136             'left' points top position towards left
137            
138              
139              
140             =cut
141              
142             sub add_frame {
143 0     0 1 0 my($self,$frames) = @_;
144              
145 0         0 my $epitaph = "only SVG::Graph::Frame objects accepted";
146              
147             #die $epitaph unless ref $frames->{frame} eq 'SVG::Graph::Frame';
148            
149 0         0 my $frame_arg = $frames->{frame};
150              
151 0 0       0 if (ref($frame_arg) eq 'ARRAY') {
    0          
152 0         0 foreach my $frame (@$frame_arg) {
153 0 0       0 die $epitaph unless ref $frame eq 'SVG::Graph::Frame';
154 0         0 push @{$self->{frames}}, $frame;
  0         0  
155             }
156            
157             }
158             elsif(ref($frame_arg) eq __PACKAGE__) {
159 0         0 push @{$self->{frames}}, $frame_arg;
  0         0  
160             }
161             else {
162 0         0 my $frame = SVG::Graph::Frame->new(svg=>$self->_parent_svg,
163             _parent_frame=>$self,
164             xoffset=>$self->_parent_svg->margin,
165             yoffset=>$self->_parent_svg->margin,
166             xsize=>$self->_parent_svg->width - (2 * $self->_parent_svg->margin),
167             ysize=>$self->_parent_svg->height - (2 * $self->_parent_svg->margin),
168             frame_transform=>$frames->{frame_transform}
169             );
170            
171 0 0       0 $frame->stack($self->stack) if $self->stack;
172 0 0       0 $frame->ystat($self->ystat) if $self->stack;
173            
174 0         0 push @{$self->{frames}}, $frame;
  0         0  
175 0         0 return $frame;
176             }
177              
178 0         0 $self->is_changed(1);
179             }
180              
181             =head2 frames
182              
183             Title : frames
184             Usage :
185             Function:
186             Example :
187             Returns :
188             Args :
189              
190              
191             =cut
192              
193             sub frames {
194 69     69 1 97 my $self = shift;
195 69 50       277 return $self->{frames} ? @{$self->{frames}} : ();
  0         0  
196             }
197              
198             =head2 add_data
199              
200             Title : add_data
201             Usage : $frame->add_data($data)
202             Function: adds a SVG::Graph::Data object to the current Frame
203             Returns : none
204             Args : SVG::Graph::Data object
205              
206              
207             =cut
208              
209             sub add_data {
210 10     10 1 860 my($self,@datas) = @_;
211              
212 10         22 my $epitaph = "only SVG::Graph::Data objects accepted";
213              
214 10         20 foreach my $data (@datas){
215 10 50       46 if(ref $data eq 'ARRAY'){
216 0         0 foreach my $d (@$data){
217 0 0 0     0 die $epitaph unless ref $d eq 'SVG::Graph::Data' || ref $data eq 'SVG::Graph::Data::Tree';
218 0         0 push @{$self->{data}}, $d;
  0         0  
219             }
220             } else {
221 10 50 66     55 die $epitaph unless ref $data eq 'SVG::Graph::Data' || ref $data eq 'SVG::Graph::Data::Tree';
222 10         14 push @{$self->{data}}, $data;
  10         62  
223             }
224             }
225              
226 10         48 $self->is_changed(1);
227             }
228              
229             =head2 all_data
230              
231             Title : all_data
232             Usage :
233             Function:
234             Example :
235             Returns :
236             Args :
237              
238              
239             =cut
240              
241             sub all_data {
242 24     24 1 33 my $self = shift;
243 24         38 my $flag = shift;
244              
245 24 50 33     68 if(($self->_parent_frame && $flag) || !$self->_parent_frame){
    0 33        
246 24         57 my @data = $self->data;
247              
248             #recurse down into subframes...
249 24         75 foreach my $subframe ($self->frames){
250 0         0 push @data, $subframe->all_data(1);
251             }
252              
253 24 50       40 return map {$_->can('data') ? $_->all_data(1) : $_ } @data;
  480         1315  
254             } elsif($self->_parent_frame) {
255 0         0 return $self->_parent_frame->all_data;
256             }
257             }
258              
259             =head2 data
260              
261             Title : data
262             Usage :
263             Function:
264             Example :
265             Returns :
266             Args :
267              
268              
269             =cut
270              
271             sub data {
272 35     35 1 48 my $self = shift;
273              
274             #these are SVG::Graph::Data objects
275 35 50       105 my @data = $self->{data} ? @{$self->{data}} : ();
  35         85  
276              
277             #recurse down into subframes...
278 35         87 foreach my $subframe ($self->frames){
279 0         0 push @data, $subframe->data;
280             }
281              
282 35 100       68 return map {$_->can('data') ? $_->data : $_ } @data;
  35         6511  
283             }
284              
285             =head2 glyphs
286              
287             Title : glyphs
288             Usage :
289             Function:
290             Example :
291             Returns :
292             Args :
293              
294              
295             =cut
296              
297             sub glyphs {
298 10     10 1 20 my $self = shift;
299 10 50       39 return $self->{glyphs} ? @{$self->{glyphs}} : ();
  10         37  
300             }
301              
302             =head2 data_chunks
303              
304             Title : data_chunks
305             Usage :
306             Function:
307             Example :
308             Returns :
309             Args :
310              
311              
312             =cut
313              
314             sub data_chunks {
315 0     0 1 0 my $self = shift;
316              
317 0 0       0 my @data = $self->{data} ? @{$self->{data}} : ();
  0         0  
318              
319             #recurse down into subframes...
320 0         0 foreach my $subframe ($self->frames){
321 0         0 push @data, $subframe->data_chunks;
322             }
323              
324 0         0 return @data;
325             }
326              
327             =head2 draw
328              
329             Title : draw
330             Usage : should not directly call this method, see SVG::Graph->draw
331             Function: depends on child glyph implementations
332             Example :
333             Returns :
334             Args :
335              
336              
337             =cut
338              
339             sub draw {
340 10     10 1 32 my($self, $svg) = @_;
341              
342 10         46 foreach my $frame ($self->frames){
343             #warn $frame;
344 0         0 $frame->draw($self);
345             }
346              
347 10         49 foreach my $glyph ($self->glyphs){
348             #warn $glyph;
349 14         301 $glyph->draw($self);
350             }
351             }
352              
353             =head2 _recalculate_stats
354              
355             Title : _recalculate_stats
356             Usage :
357             Function:
358             Example :
359             Returns :
360             Args :
361              
362              
363             =cut
364              
365             sub _recalculate_stats{
366 487     487   666 my ($self,@args) = @_;
367 487 100       1151 return undef unless $self->is_changed;
368              
369 8         80 my $xstat = Statistics::Descriptive::Full->new();
370 8         702 my $ystat = Statistics::Descriptive::Full->new();
371 8         404 my $zstat = Statistics::Descriptive::Full->new();
372              
373             #right now we only support y-stacking. this may need to be extended in the future
374 8 50       396 if($self->stack){
375 0         0 my @ystack;
376 0         0 foreach my $data ($self->data_chunks){
377 0         0 my $i = 0;
378 0         0 foreach my $datum ($data->data){
379 0         0 $ystack[$i] += $datum->y;
380 0         0 $i++;
381             }
382             }
383              
384 0         0 $ystat->add_data($_) foreach @ystack;
385             } else {
386 8 50 33     39 $ystat->add_data(map {ref($_) && $_->can('y') ? $_->y : $_} map {$_->can('data') ? $_->data : $_->y} $self->all_data);
  160 50       462  
  160         577  
387              
388             }
389              
390 8 50 33     1052 $xstat->add_data(map {ref($_) && $_->can('x') ? $_->x : $_} map {$_->can('data') ? $_->data : $_->x} $self->all_data);
  160 50       466  
  160         612  
391 8 50 33     678 $zstat->add_data(map {ref($_) && $_->can('z') ? $_->z : $_} map {$_->can('data') ? $_->data : $_->z} $self->all_data);
  160 50       414  
  160         596  
392              
393 8         719 $self->xstat($xstat);
394 8         52 $self->ystat($ystat);
395 8         48 $self->zstat($zstat);
396              
397 8         26 $self->is_changed(0);
398             }
399              
400             =head2 _parent_svg
401              
402             Title : _parent_svg
403             Usage : $obj->_parent_svg($newval)
404             Function:
405             Example :
406             Returns : value of _parent_svg (a scalar)
407             Args : on set, new value (a scalar or undef, optional)
408              
409              
410             =cut
411              
412             sub _parent_svg{
413 20     20   30 my $self = shift;
414              
415 20 100       118 return $self->{'_parent_svg'} = shift if @_;
416 10         43 return $self->{'_parent_svg'};
417             }
418              
419             =head2 _parent_frame
420              
421             Title : _parent_frame
422             Usage : $obj->_parent_frame($newval)
423             Function:
424             Example :
425             Returns : value of _parent_frame (a scalar)
426             Args : on set, new value (a scalar or undef, optional)
427              
428              
429             =cut
430              
431 0     0 0 0 sub add__parent_frame{return shift->_parent_frame(@_)}
432             sub _parent_frame{
433 48     48   69 my $self = shift;
434              
435 48 50       105 return $self->{'_parent_frame'} = shift if @_;
436 48         234 return $self->{'_parent_frame'};
437             }
438              
439             =head2 svg
440              
441             Title : svg
442             Usage : $obj->svg($newval)
443             Function:
444             Example :
445             Returns : value of svg (a scalar)
446             Args : on set, new value (a scalar or undef, optional)
447              
448              
449             =cut
450              
451             sub svg{
452 34     34 1 54 my $self = shift;
453              
454 34 100       125 return $self->{'svg'} = shift if @_;
455 14         67 return $self->{'svg'};
456             }
457              
458             =head2 xsize
459              
460             Title : xsize
461             Usage : $obj->xsize($newval)
462             Function:
463             Example :
464             Returns : value of xsize (a scalar)
465             Args : on set, new value (a scalar or undef, optional)
466              
467              
468             =cut
469              
470 10     10 0 30 sub add_xsize {return shift->xsize(@_)}
471             sub xsize{
472 24     24 1 37 my $self = shift;
473              
474 24 100       84 return $self->{'xsize'} = shift if @_;
475 14         53 return $self->{'xsize'};
476             }
477              
478             =head2 ysize
479              
480             Title : ysize
481             Usage : $obj->ysize($newval)
482             Function:
483             Example :
484             Returns : value of ysize (a scalar)
485             Args : on set, new value (a scalar or undef, optional)
486              
487              
488             =cut
489              
490 10     10 0 32 sub add_ysize {return shift->ysize(@_)}
491             sub ysize{
492 24     24 1 34 my $self = shift;
493              
494 24 100       78 return $self->{'ysize'} = shift if @_;
495 14         63 return $self->{'ysize'};
496             }
497              
498             =head2 xoffset
499              
500             Title : xoffset
501             Usage : $obj->xoffset($newval)
502             Function:
503             Example :
504             Returns : value of xoffset (a scalar)
505             Args : on set, new value (a scalar or undef, optional)
506              
507              
508             =cut
509              
510 10     10 0 38 sub add_xoffset {return shift->xoffset(@_)}
511             sub xoffset{
512 24     24 1 35 my $self = shift;
513              
514 24 100       77 return $self->{'xoffset'} = shift if @_;
515 14         52 return $self->{'xoffset'};
516             }
517              
518             =head2 yoffset
519              
520             Title : yoffset
521             Usage : $obj->yoffset($newval)
522             Function:
523             Example :
524             Returns : value of yoffset (a scalar)
525             Args : on set, new value (a scalar or undef, optional)
526              
527              
528             =cut
529              
530 10     10 0 42 sub add_yoffset {return shift->yoffset(@_)}
531             sub yoffset{
532 24     24 1 34 my $self = shift;
533              
534 24 100       90 return $self->{'yoffset'} = shift if @_;
535 14         111 return $self->{'yoffset'};
536             }
537              
538             =head2 xmin
539              
540             Title : xmin
541             Usage : $obj->xmin($newval)
542             Function:
543             Example :
544             Returns : value of xmin (a scalar)
545             Args : on set, new value (a scalar or undef, optional)
546              
547              
548             =cut
549              
550             sub xmin{
551 210     210 1 377 my $self = shift;
552              
553 210 50       395 return $self->{'xmin'} = shift if @_;
554 210 50       444 return $self->{'xmin'} if defined $self->{'xmin'};
555 210         368 $self->_recalculate_stats();
556 210         532 return $self->xstat->min;
557              
558             }
559              
560             =head2 xmax
561              
562             Title : xmax
563             Usage : $obj->xmax($newval)
564             Function:
565             Example :
566             Returns : value of xmax (a scalar)
567             Args : on set, new value (a scalar or undef, optional)
568              
569              
570             =cut
571              
572             sub xmax{
573 31     31 1 43 my $self = shift;
574              
575 31 50       149 return $self->{'xmax'} = shift if @_;
576 31 50       93 return $self->{'xmax'} if defined $self->{'xmax'};
577 31         64 $self->_recalculate_stats();
578 31         91 return $self->xstat->max;
579              
580             }
581              
582             =head2 ymin
583              
584             Title : ymin
585             Usage : $obj->ymin($newval)
586             Function:
587             Example :
588             Returns : value of ymin (a scalar)
589             Args : on set, new value (a scalar or undef, optional)
590              
591              
592             =cut
593              
594             sub ymin{
595 210     210 1 382 my $self = shift;
596              
597 210 50       343 return $self->{'ymin'} = shift if @_;
598 210 50       423 return $self->{'ymin'} if defined $self->{'ymin'};
599 210         403 $self->_recalculate_stats();
600 210         509 return $self->ystat->min;
601              
602             }
603              
604             =head2 ymax
605              
606             Title : ymax
607             Usage : $obj->ymax($newval)
608             Function:
609             Example :
610             Returns : value of ymax (a scalar)
611             Args : on set, new value (a scalar or undef, optional)
612              
613              
614             =cut
615              
616             sub ymax{
617 31     31 1 34 my $self = shift;
618              
619 31 50       62 return $self->{'ymax'} = shift if @_;
620 31 50       97 return $self->{'ymax'} if defined $self->{'ymax'};
621 31         64 $self->_recalculate_stats();
622 31         83 return $self->ystat->max;
623              
624             }
625              
626             =head2 xrange
627              
628             Title : xrange
629             Usage : $obj->xrange($newval)
630             Function:
631             Example :
632             Returns : value of xrange (a scalar)
633              
634              
635             =cut
636              
637             sub xrange{
638 31     31 1 42 my $self = shift;
639              
640 31         71 return $self->xmax - $self->xmin;
641             }
642              
643             =head2 yrange
644              
645             Title : yrange
646             Usage : $obj->yrange($newval)
647             Function:
648             Example :
649             Returns : value of yrange (a scalar)
650              
651              
652             =cut
653              
654             sub yrange{
655 31     31 1 50 my $self = shift;
656              
657 31         79 return $self->ymax - $self->ymin;
658             }
659              
660             =head2 stack
661              
662             Title : stack
663             Usage : $obj->stack($newval)
664             Function:
665             Example :
666             Returns : value of stack (a scalar)
667             Args : on set, new value (a scalar or undef, optional)
668              
669              
670             =cut
671              
672             sub stack{
673 8     8 1 20 my $self = shift;
674              
675 8 50       32 return $self->{'stack'} = shift if @_;
676 8         29 return $self->{'stack'};
677             }
678              
679 10     10 0 32 sub add_frame_transform{return shift->frame_transform(@_)}
680             sub frame_transform {
681 50     50 0 71 my $self = shift;
682              
683 50 100       128 return $self->{'frame_transform'} = shift if @_;
684 40 50       233 return $self->{'frame_transform'} if defined $self->{'frame_transform'};
685             }
686              
687             1;