File Coverage

blib/lib/SVG/Graph/Kit.pm
Criterion Covered Total %
statement 83 99 83.8
branch 15 26 57.6
condition 26 56 46.4
subroutine 12 14 85.7
pod 2 2 100.0
total 138 197 70.0


line stmt bran cond sub pod time code
1             package SVG::Graph::Kit;
2             BEGIN {
3 2     2   4566 $SVG::Graph::Kit::AUTHORITY = 'cpan:GENE';
4             }
5             # ABSTRACT: Simplified data plotting with SVG
6              
7 2     2   21 use strict;
  2         4  
  2         77  
8 2     2   13 use warnings;
  2         4  
  2         110  
9              
10             our $VERSION = '0.0401';
11              
12 2     2   13 use base qw(SVG::Graph);
  2         6  
  2         2031  
13 2     2   159079 use SVG::Graph::Data;
  2         7  
  2         57  
14 2     2   2303 use SVG::Graph::Data::Datum;
  2         799  
  2         60  
15 2     2   6617 use Math::Trig;
  2         76162  
  2         3651  
16              
17              
18             sub new {
19 2     2 1 1845 my $class = shift;
20 2         7 my %args = @_;
21              
22             # Move non-parent arguments to the kit.
23 2         5 my %kit = ();
24 2         7 for my $arg (qw(axis data plot polar)) {
25 8 100       29 next unless exists $args{$arg};
26 1         4 $kit{$arg} = $args{$arg};
27 1         2 delete $args{$arg};
28             }
29              
30             # Construct the SVG::Graph object with the remaining arguments.
31 2   50     16 $args{width} ||= 600;
32 2   50     15 $args{height} ||= 600;
33 2   50     11 $args{margin} ||= 35;
34 2         19 my $self = $class->SUPER::new(%args);
35              
36             # Re-bless as a Graph::Kit object.
37 2         865 bless $self, $class;
38 2         9 $self->_setup(%kit);
39 2         5622 return $self;
40             }
41              
42             sub _setup {
43 2     2   5 my $self = shift;
44 2         6 my %args = @_;
45              
46             # Start with an initial frame...
47 2         12 my $frame = $self->add_frame;
48              
49             # Plot the data.
50 2 100       651 if ($args{data}) {
51             # Load the graph data and use the SVG::Graph::Data object for label making.
52 1         7 $self->{graph_data} = _load_data($args{data}, $frame, $args{polar});
53             # Add the data to the graph.
54 1   50     22 my %plot = (
      50        
      50        
55             stroke => $args{plot}{stroke} || 'red',
56             fill => $args{plot}{fill} || 'red',
57             'fill-opacity' => $args{plot}{'fill-opacity'} || 0.5,
58             );
59 1   50     7 $args{plot}{type} ||= 'scatter';
60 1         6 $frame->add_glyph($args{plot}{type}, %plot);
61             }
62              
63             # Handle the axis unless it's set to 0.
64 2 50 0     1931 if (not(exists $args{axis}) or exists $args{axis} and $args{axis}) {
      33        
65 2         13 my %axis = $self->_load_axis($args{data}, $args{axis});
66 2         17 $frame->add_glyph('axis', %axis);
67             }
68             }
69              
70             sub _load_axis {
71 2     2   7 my($self, $data, $axis) = @_;
72              
73             # Initialize an empty axis unless given a hashref.
74 2 50       11 $axis = {} if not ref $axis eq 'HASH';
75              
76             # Set the default properties and user override.
77 2         24 my %axis = (
78             x_intercept => 0,
79             y_intercept => 0,
80             stroke => 'gray',
81             'stroke-width' => 2,
82             ticks => 30, # Max data per axis
83             log => 0,
84             %$axis, # User override
85             );
86              
87             # Set the number of ticks to show on each axis.
88 2   33     14 $axis{xticks} ||= $axis{ticks};
89 2   33     12 $axis{yticks} ||= $axis{ticks};
90              
91             # Set the logarithmic scaling factor.
92 2   33     13 $axis{xlog} ||= $axis{log};
93 2   33     12 $axis{ylog} ||= $axis{log};
94              
95             # Compute scale factors.
96 2         4 my ($xscale, $yscale) = (1, 1);
97 2 50 66     17 if ($data and $self->{graph_data}->xmax - $self->{graph_data}->xmin > $axis{xticks}) {
98             # Round to the integer, i.e. 0 decimal places.
99 0         0 $xscale = sprintf '%.0f', $self->{graph_data}->xmax / $axis{xticks};
100             }
101 2 50 66     921 if ($data and $self->{graph_data}->ymax - $self->{graph_data}->ymin > $axis{yticks}) {
102             # Round to the integer, i.e. 0 decimal places.
103 0         0 $yscale = sprintf '%.0f', $self->{graph_data}->ymax / $axis{yticks};
104             }
105              
106             # Use absolute_ticks if no tick mark setting is provided.
107 2 50 33     62 unless (defined $axis{x_absolute_ticks} or defined $axis{x_fractional_ticks}) {
108 2         4 $axis{x_absolute_ticks} = $xscale;
109             }
110 2 50 33     18 unless (defined $axis{y_absolute_ticks} or defined $axis{y_fractional_ticks}) {
111 2         5 $axis{y_absolute_ticks} = $yscale;
112             }
113              
114             # Use increments of 1 to data-max for ticks if none are provided.
115 2 50 66     23 if ($data and !defined $axis{x_tick_labels} and !defined $axis{x_intertick_labels}) {
      66        
116 1 50       5 if ($xscale > 1) {
117 0         0 $axis{x_tick_labels} = [ $self->{graph_data}->xmin ];
118 0         0 push @{ $axis{x_tick_labels} }, $_ * $xscale for 1 .. $axis{ticks};
  0         0  
119             }
120             else {
121 1         6 $axis{x_tick_labels} = [ $self->{graph_data}->xmin .. $self->{graph_data}->xmax ];
122             }
123             }
124 2 50 66     54 if ($data and !defined $axis{y_tick_labels} and !defined $axis{y_intertick_labels}) {
      66        
125 1 50       3 if ($yscale > 1) {
126 0         0 $axis{y_tick_labels} = [ $self->{graph_data}->ymin ];
127 0         0 push @{ $axis{y_tick_labels} }, $_ * $yscale for 1 .. $axis{ticks};
  0         0  
128             }
129             else {
130 1         6 $axis{y_tick_labels} = [ $self->{graph_data}->ymin .. $self->{graph_data}->ymax ];
131             }
132             }
133              
134             # Remove keys not used by parent module.
135 2         50 delete $axis{ticks};
136 2         5 delete $axis{xticks};
137 2         5 delete $axis{yticks};
138 2         5 delete $axis{log};
139 2         26 delete $axis{xlog};
140 2         4 delete $axis{ylog};
141              
142 2         19 return %axis;
143             }
144              
145             sub _load_data {
146 1     1   3 my ($data, $frame, $polar) = @_;
147             # Create individual data points.
148 1         3 my @data = ();
149 1         3 for my $datum (@$data) {
150             # Set the coordinate.
151 9         269 my @coord = @$datum;
152 9 50       21 @coord = _to_polar($datum) if $polar;
153              
154             # Add our 3D data point.
155 9         60 push @data, SVG::Graph::Data::Datum->new(
156             x => $coord[0],
157             y => $coord[1],
158             z => $coord[2],
159             );
160             }
161             # Instantiate a new SVG::Graph::Data object;
162 1         41 my $obj = SVG::Graph::Data->new(data => \@data);
163             # Populate our graph with data.
164 1         84 $frame->add_data($obj);
165 1         22 return $obj;
166             }
167              
168             sub _theta {
169 0     0   0 my $point = shift;
170             # return int(rand 359);
171 0         0 return atan2($point->[1], $point->[0]);
172             }
173              
174             sub _to_polar {
175 0     0   0 my $point = shift;
176 0         0 my $r = 0;
177 0         0 $r += $_ ** 2 for @$point;
178 0         0 $r = sqrt $r;
179 0         0 my $t = _theta($point);
180 0         0 return $r, $t;
181             }
182              
183              
184             sub stat {
185 21     21 1 14556 my ($self, $dimension, $name, @args) = @_;
186 21         49 my $method = $dimension . $name;
187 21         135 return $self->{graph_data}->$method(@args);
188             }
189              
190             1;
191              
192             __END__