File Coverage

blib/lib/Chart/Lines.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             ## @file
2             # Implementation of Chart::Lines
3             #
4             # written by david bonner
5             # dbonner@cs.bu.edu
6             #
7             # maintained by the Chart Group at Geodetic Fundamental Station Wettzell
8             # Chart@fs.wettzell.de
9             # @author Chart Group (Chart@fs.wettzell.de)
10             # @date 2015-03-01
11             # @version 2.4.10
12              
13             ## @class Chart::Lines
14             # Lines class derived from class Base.
15             #
16             # This class provides all functions which are specific to
17             # lines
18             #
19             package Chart::Lines;
20              
21 13     13   26859 use Chart::Base '2.4.10';
  0            
  0            
22             use GD;
23             use Carp;
24             use strict;
25              
26             @Chart::Lines::ISA = qw(Chart::Base);
27             $Chart::Lines::VERSION = '2.4.10';
28              
29             #>>>>>>>>>>>>>>>>>>>>>>>>>>#
30             # public methods go here #
31             #<<<<<<<<<<<<<<<<<<<<<<<<<<#
32              
33             #>>>>>>>>>>>>>>>>>>>>>>>>>>>#
34             # private methods go here #
35             #<<<<<<<<<<<<<<<<<<<<<<<<<<<#
36              
37             ## @method private _draw_data
38             # finally get around to plotting the data for lines
39             sub _draw_data
40             {
41             my $self = shift;
42             my $data = $self->{'dataref'};
43             my $misccolor = $self->_color_role_to_index('misc');
44             my ( $x1, $x2, $x3, $y1, $y2, $y3, $mod, $abs_x_max, $abs_y_max, $tan_alpha );
45             my ( $width, $height, $delta, $delta_num, $map, $t_x_min, $t_x_max, $t_y_min, $t_y_max );
46             my ( $i, $j, $color, $brush, $zero_offset );
47             my $repair_top_flag = 0;
48             my $repair_bottom_flag = 0;
49              
50             # init the imagemap data field if they asked for it
51             if ( $self->true( $self->{'imagemap'} ) )
52             {
53             $self->{'imagemap_data'} = [];
54             }
55              
56             # find the delta value between data points, as well
57             # as the mapping constant
58             $width = $self->{'curr_x_max'} - $self->{'curr_x_min'};
59             $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
60             $delta = $width / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 );
61             $map = $height / ( $self->{'max_val'} - $self->{'min_val'} );
62              
63             #for a xy-plot, use this delta and maybe an offset for the zero-axes
64             if ( $self->true( $self->{'xy_plot'} ) )
65             {
66             $delta_num = $width / ( $self->{'x_max_val'} - $self->{'x_min_val'} );
67              
68             if ( $self->{'x_min_val'} <= 0 && $self->{'x_max_val'} >= 0 )
69             {
70             $zero_offset = abs( $self->{'x_min_val'} ) * abs($delta_num);
71             }
72             elsif ( $self->{'x_min_val'} > 0 || $self->{'x_max_val'} < 0 )
73             {
74             $zero_offset = -$self->{'x_min_val'} * $delta_num;
75             }
76             else
77             {
78             $zero_offset = 0;
79             }
80             }
81              
82             # get the base x-y values
83             if ( $self->true( $self->{'xy_plot'} ) )
84             {
85             $x1 = $self->{'curr_x_min'};
86             }
87             else
88             {
89             $x1 = $self->{'curr_x_min'} + ( $delta / 2 );
90             }
91             if ( $self->{'min_val'} >= 0 )
92             {
93             $y1 = $self->{'curr_y_max'};
94             $mod = $self->{'min_val'};
95             }
96             elsif ( $self->{'max_val'} <= 0 )
97             {
98             $y1 = $self->{'curr_y_min'};
99             $mod = $self->{'max_val'};
100             }
101             else
102             {
103             $y1 = $self->{'curr_y_min'} + ( $map * $self->{'max_val'} );
104             $mod = 0;
105             $self->{'gd_obj'}->line( $self->{'curr_x_min'}, $y1, $self->{'curr_x_max'}, $y1, $misccolor );
106             }
107              
108             # draw the lines
109              
110             for $i ( 1 .. $self->{'num_datasets'} )
111             {
112              
113             # get the color for this dataset, and set the brush
114             $color = $self->_color_role_to_index( 'dataset' . ( $i - 1 ) );
115             $brush = $self->_prepare_brush($color);
116             $self->{'gd_obj'}->setBrush($brush);
117              
118             # draw every line for this dataset
119             for $j ( 1 .. $self->{'num_datapoints'} - 1 )
120             {
121              
122             # don't try to draw anything if there's no data
123             if ( defined( $data->[$i][$j] ) and defined( $data->[$i][ $j - 1 ] ) )
124             {
125             if ( $self->true( $self->{'xy_plot'} ) )
126             {
127             $x2 = $x1 + $delta_num * $data->[0][ $j - 1 ] + $zero_offset;
128             $x3 = $x1 + $delta_num * $data->[0][$j] + $zero_offset;
129             }
130             else
131             {
132             $x2 = $x1 + ( $delta * ( $j - 1 ) );
133             $x3 = $x1 + ( $delta * $j );
134             }
135             $y2 = $y1 - ( ( $data->[$i][ $j - 1 ] - $mod ) * $map );
136             $y3 = $y1 - ( ( $data->[$i][$j] - $mod ) * $map );
137              
138             # now draw the line
139             # ----------------
140             # stepline option added by G.ST. 2005/02
141             #----------------
142             if ( $self->true( $self->{'stepline'} ) )
143             {
144             if ( $self->{'stepline_mode'} =~ /^begin$/i )
145             {
146             $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, gdBrushed );
147             $self->{'gd_obj'}->line( $x3, $y2, $x3, $y3, gdBrushed );
148             }
149             else
150             {
151             $self->{'gd_obj'}->line( $x2, $y2, $x2, $y3, gdBrushed );
152             $self->{'gd_obj'}->line( $x2, $y3, $x3, $y3, gdBrushed );
153             }
154             }
155              
156             # -----------------------------------
157             # end stepline option
158             #------------------------------------
159             else
160             {
161             $self->{'gd_obj'}->line( $x2, $y2, $x3, $y3, gdBrushed );
162             }
163              
164             # set the flags, if the lines are out of the borders of the chart
165             if ( ( $data->[$i][$j] > $self->{'max_val'} ) || ( $data->[$i][ $j - 1 ] > $self->{'max_val'} ) )
166             {
167             $repair_top_flag = 1;
168             }
169              
170             if ( ( $self->{'max_val'} <= 0 )
171             && ( ( $data->[$i][$j] > $self->{'max_val'} ) || ( $data->[$i][ $j - 1 ] > $self->{'max_val'} ) ) )
172             {
173             $repair_top_flag = 1;
174             }
175             if ( ( $data->[$i][$j] < $self->{'min_val'} ) || ( $data->[$i][ $j - 1 ] < $self->{'min_val'} ) )
176             {
177             $repair_bottom_flag = 1;
178             }
179              
180             # store the imagemap data if they asked for it
181             if ( $self->true( $self->{'imagemap'} ) )
182             {
183             $self->{'imagemap_data'}->[$i][ $j - 1 ] = [ $x2, $y2 ];
184             $self->{'imagemap_data'}->[$i][$j] = [ $x3, $y3 ];
185             }
186             }
187             else
188             {
189             if ( $self->true( $self->{'imagemap'} ) )
190             {
191             $self->{'imagemap_data'}->[$i][ $j - 1 ] = [ undef(), undef() ];
192             $self->{'imagemap_data'}->[$i][$j] = [ undef(), undef() ];
193             }
194             }
195             }
196             }
197              
198             # and finaly box it off
199             $self->{'gd_obj'}
200             ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor );
201              
202             #get the width and the heigth of the complete picture
203             ( $abs_x_max, $abs_y_max ) = $self->{'gd_obj'}->getBounds();
204              
205             #repair the chart, if the lines are out of the borders of the chart
206             if ($repair_top_flag)
207             {
208              
209             #overwrite the ugly mistakes
210             $self->{'gd_obj'}->filledRectangle(
211             $self->{'curr_x_min'} - ( $self->{'brush_size'} / 2 ),
212             0, $self->{'curr_x_max'},
213             $self->{'curr_y_min'} - 1,
214             $self->_color_role_to_index('background')
215             );
216              
217             #save the actual x and y values
218             $t_x_min = $self->{'curr_x_min'};
219             $t_x_max = $self->{'curr_x_max'};
220             $t_y_min = $self->{'curr_y_min'};
221             $t_y_max = $self->{'curr_y_max'};
222              
223             #get back to the point, where everything began
224             $self->{'curr_x_min'} = 0;
225             $self->{'curr_y_min'} = 0;
226             $self->{'curr_x_max'} = $abs_x_max;
227             $self->{'curr_y_max'} = $abs_y_max;
228              
229             #draw the title again
230             if ( $self->{'title'} )
231             {
232             $self->_draw_title;
233             }
234              
235             #draw the sub title again
236             if ( $self->{'sub_title'} )
237             {
238             $self->_draw_sub_title;
239             }
240              
241             #draw the top legend again
242             if ( $self->{'legend'} =~ /^top$/i )
243             {
244             $self->_draw_top_legend;
245             }
246              
247             #reset the actual values
248             $self->{'curr_x_min'} = $t_x_min;
249             $self->{'curr_x_max'} = $t_x_max;
250             $self->{'curr_y_min'} = $t_y_min;
251             $self->{'curr_y_max'} = $t_y_max;
252             }
253              
254             if ($repair_bottom_flag)
255             {
256              
257             #overwrite the ugly mistakes
258             $self->{'gd_obj'}->filledRectangle(
259             $self->{'curr_x_min'} - ( $self->{'brush_size'} / 2 ),
260             $self->{'curr_y_max'} + 1,
261             $self->{'curr_x_max'}, $abs_y_max, $self->_color_role_to_index('background')
262             );
263              
264             #save the actual x and y values
265             $t_x_min = $self->{'curr_x_min'};
266             $t_x_max = $self->{'curr_x_max'};
267             $t_y_min = $self->{'curr_y_min'};
268             $t_y_max = $self->{'curr_y_max'};
269              
270             #get back to the point, where everything began
271             $self->{'curr_x_min'} = 0;
272             $self->{'curr_y_min'} = 0;
273             $self->{'curr_x_max'} = $abs_x_max;
274             $self->{'curr_y_max'} = $abs_y_max - 1;
275              
276             # mark off the graph_border space
277             $self->{'curr_y_max'} -= 2 * $self->{'graph_border'};
278              
279             #draw the bottom legend again
280             if ( $self->{'legend'} =~ /^bottom$/i )
281             {
282             $self->_draw_bottom_legend;
283             }
284              
285             #draw the x label again
286             if ( $self->{'x_label'} )
287             {
288             $self->_draw_x_label;
289             }
290              
291             #get back to the start point for the ticks
292             $self->{'curr_x_min'} = $self->{'temp_x_min'};
293             $self->{'curr_y_min'} = $self->{'temp_y_min'};
294             $self->{'curr_x_max'} = $self->{'temp_x_max'};
295             $self->{'curr_y_max'} = $self->{'temp_y_max'};
296              
297             #draw the x ticks again
298             if ( $self->true( $self->{'xy_plot'} ) )
299             {
300             $self->_draw_x_number_ticks;
301             }
302             else
303             {
304             $self->_draw_x_ticks;
305             }
306              
307             #reset the actual values
308             $self->{'curr_x_min'} = $t_x_min;
309             $self->{'curr_x_max'} = $t_x_max;
310             $self->{'curr_y_min'} = $t_y_min;
311             $self->{'curr_y_max'} = $t_y_max;
312             }
313              
314             return;
315              
316             }
317              
318             ## @fn private int _prepare_brush($color)
319             # set the gdBrush object to trick GD into drawing fat lines
320             #
321             sub _prepare_brush
322             {
323             my $self = shift;
324             my $color = shift;
325             my $radius = $self->{'brush_size'} / 2;
326             my ( @rgb, $brush, $white, $newcolor );
327              
328             # get the rgb values for the desired color
329             @rgb = $self->{'gd_obj'}->rgb($color);
330              
331             # create the new image
332             $brush = GD::Image->new( $radius * 2, $radius * 2 );
333              
334             # get the colors, make the background transparent
335             $white = $brush->colorAllocate( 255, 255, 255 );
336             $newcolor = $brush->colorAllocate(@rgb);
337             $brush->transparent($white);
338              
339             # draw the circle
340             $brush->arc( $radius - 1, $radius - 1, $radius, $radius, 0, 360, $newcolor );
341              
342             # set the new image as the main object's brush
343             return $brush;
344             }
345              
346             ## be a good module and return 1
347             1;