File Coverage

blib/lib/GD/Graph/xylines.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             #==========================================================================
2             # Copyright (c) 1995-1998 Martien Verbruggen
3             # Modified for XY by George Fitch
4             #--------------------------------------------------------------------------
5             #
6             # Name:
7             # GD::Graph::xylines.pm
8             #
9             # $Id: points.pm,v 1.9 2000/04/30 08:32:38 mgjv Exp $
10             #
11             #==========================================================================
12              
13             package GD::Graph::xylines;
14              
15             $GD::Graph::xylines::VERSION = '$Revision: 1.10 $' =~ /\s([\d.]+)/;
16              
17 1     1   2396 use strict;
  1         1  
  1         34  
18            
19 1     1   1414 use GD;
  0            
  0            
20             use GD::Graph::axestype;
21             use GD::Graph::utils qw(:all);
22              
23             @GD::Graph::xylines::ISA = qw( GD::Graph::axestype );
24              
25             use constant PI => 4 * atan2(1,1);
26              
27             my %Defaults = (
28            
29             #Pad things a bit to make them look nicer
30             b_margin => 5,
31             r_margin => 5,
32              
33             # We want long ticks by default
34             x_long_ticks => 1,
35             y_long_ticks => 1,
36            
37             # Number of ticks for the y axis
38             y_tick_number => 10,
39             x_tick_number => 13,
40             x_precision => undef,
41            
42             # Skip every nth label. if 1 will print every label on the axes,
43             # if 2 will print every second, etc..
44             x_label_skip => 1,
45             y_label_skip => 1,
46              
47             # Do we want ticks on the x axis?
48             x_ticks => 1,
49             x_all_ticks => 0,
50              
51             # Where to place the x and y labels
52             x_label_position => 1/2,
53             y_label_position => 1/2,
54              
55             # vertical printing of x labels
56             x_labels_vertical => 1,
57              
58             # Draw axes as a box? (otherwise just left and bottom)
59             box_axis => 1,
60            
61             );
62              
63             sub initialise
64             {
65             my $self = shift;
66              
67             $self->SUPER::initialise();
68              
69             while (my($key, $val) = each %Defaults)
70             { $self->{$key} = $val }
71              
72             $self->set_x_label_font(GD::gdSmallFont);
73             $self->set_y_label_font(GD::gdSmallFont);
74             $self->set_x_axis_font(GD::gdTinyFont);
75             $self->set_y_axis_font(GD::gdTinyFont);
76             $self->set_legend_font(GD::gdTinyFont);
77             $self->set_values_font(GD::gdTinyFont);
78             }
79              
80             # PRIVATE
81             sub set_max_min
82             {
83             my $self = shift;
84              
85             my $x_max = undef;
86             my $x_min = undef;
87             my $y_max = undef;
88             my $y_min = undef;
89            
90             for my $i ( 1 .. $self->{_data}->num_sets ) # 1 because x-labels are [0]
91             {
92             # Contributed by Andrew Crabb - ahc@sol.jhoc1.jhmi.edu
93             my $num_points_limit = $self->{_data}->num_points - 1;
94             for my $j ( 0 .. $num_points_limit )
95             {
96             my $val = $self->{_data}->[$i][$j];
97             $y_max = $val if ((not defined($y_max)) or ($val > $y_max));
98             $y_min = $val if ((not defined($y_min)) or ($val < $y_min));
99             }
100             }
101              
102             # Contributed by Andrew Crabb - ahc@sol.jhoc1.jhmi.edu
103             my $num_points_limit = $self->{_data}->num_points - 1;
104             for my $k ( 0 .. $num_points_limit ) # x-values are at [0]
105             {
106             my $val = $self->{_data}->[0][$k];
107             $x_max = $val if ((not defined($x_max)) or ($val > $x_max));
108             $x_min = $val if ((not defined($x_min)) or ($val < $x_min));
109             }
110              
111             # Set the min and max's
112             $self->{y_min}[1] = $y_min;
113             $self->{y_max}[1] = $y_max;
114              
115             $self->{y_min}[2] = $y_min;
116             $self->{y_max}[2] = $y_max;
117              
118             $self->{x_min} = $x_min;
119             $self->{x_max} = $x_max;
120              
121             # Calculate the needed precision.
122              
123             my $x_pre = int(log(abs(($self->{x_max} - $self->{x_min}) / ($self->{x_tick_number} - 1) + $self->{x_min}))) + 1;
124              
125             $x_pre = $x_pre < 0 ? 0 : $x_pre;
126              
127             # Overwrite these with any user supplied ones
128             $self->{y_min}[1] = $self->{y_min_value} if defined $self->{y_min_value};
129             $self->{y_max}[1] = $self->{y_max_value} if defined $self->{y_max_value};
130              
131             $self->{y_min}[1] = $self->{y1_min_value} if defined $self->{y1_min_value};
132             $self->{y_max}[1] = $self->{y1_max_value} if defined $self->{y1_max_value};
133              
134             $self->{y_min}[2] = $self->{y2_min_value} if defined $self->{y2_min_value};
135             $self->{y_max}[2] = $self->{y2_max_value} if defined $self->{y2_max_value};
136              
137             $self->{x_min} = $self->{x_min_value} if defined $self->{x_min_value};
138             $self->{x_max} = $self->{x_max_value} if defined $self->{x_max_value};
139              
140             $self->{true_x_min} = $self->{x_min};
141             $self->{true_x_max} = $self->{x_max};
142              
143             $self->{true_y_min} = $self->{y_min}[1];
144             $self->{true_y_max} = $self->{y_max}[1];
145              
146             $self->{x_tick_number} = 13 unless defined $self->{x_tick_number};
147             $self->{y_tick_number} = 10 unless defined $self->{y_tick_number};
148              
149             $self->{x_precision} = $x_pre unless defined $self->{x_precision};
150              
151             return $self;
152              
153             }
154            
155             sub setup_coords
156             {
157             my $s = shift;
158              
159             # Do some sanity checks
160             $s->{two_axes} = 0 if $s->{_data}->num_sets != 2 || $s->{two_axes} < 0;
161             $s->{two_axes} = 1 if $s->{two_axes} > 1;
162              
163             delete $s->{y_label2} unless $s->{two_axes};
164              
165             # Set some heights for text
166             $s->{tfh} = 0 unless $s->{title};
167             $s->{xlfh} = 0 unless $s->{x_label};
168              
169             # Make sure the y1 axis has a label if there is one set for y in
170             # general
171             $s->{y1_label} = $s->{y_label} if !$s->{y1_label} && $s->{y_label};
172              
173             # Set axis tick text heights and widths to 0 if they don't need to
174             # be plotted.
175             $s->{xafh} = 0, $s->{xafw} = 0 unless $s->{x_plot_values};
176             $s->{yafh} = 0, $s->{yafw} = 0 unless $s->{y_plot_values};
177              
178             # Calculate minima and maxima for the axes
179             $s->set_max_min() or return;
180              
181             # Create the labels for the axes, and calculate the max length
182             $s->create_y_labels();
183             $s->create_x_labels(); # CONTRIB Scott Prahl
184              
185             # Calculate the boundaries of the chart
186             $s->_setup_boundaries() or return;
187              
188             # get the zero axis level
189             (undef, $s->{zeropoint}) = $s->val_to_pixel(0, 0, 1);
190              
191             # More sanity checks
192             $s->{x_label_skip} = 1 if $s->{x_label_skip} < 1;
193             $s->{y_label_skip} = 1 if $s->{y_label_skip} < 1;
194             $s->{y_tick_number} = 1 if $s->{y_tick_number} < 1;
195              
196             return $s;
197             }
198              
199             #
200             # Ticks and values for x axes
201             #
202             sub draw_x_ticks_number
203             {
204              
205             my $self = shift;
206              
207             for (my $i = 0; $i < $self->{x_tick_number}; $i++)
208             {
209              
210             my $x_val = sprintf "%0.$self->{x_precision}f", $i * ($self->{x_max} - $self->{x_min}) / ($self->{x_tick_number} - 1) + $self->{x_min};
211              
212             my ($x, $y) = $self->val_to_pixel($x_val, 0, 1);
213              
214             if (defined $self->{x_number_format})
215             {
216             $x_val = ref $self->{x_number_format} eq 'CODE' ?
217             &{$self->{x_number_format}}($x_val) :
218             sprintf($self->{x_number_format}, $x_val);
219             }
220            
221             $y = $self->{bottom} unless $self->{zero_axis_only};
222              
223             # CONTRIB Damon Brodie for x_tick_offset
224             next if (!$self->{x_all_ticks} and
225             ($i - $self->{x_tick_offset}) % $self->{x_label_skip} and
226             $i != $self->{_data}->num_points - 1
227             );
228              
229             if ($self->{x_ticks})
230             {
231             if ($self->{x_long_ticks})
232             {
233             $self->{graph}->line($x, $self->{bottom}, $x, $self->{top},
234             $self->{fgci});
235             }
236             else
237             {
238             $self->{graph}->line($x, $y, $x, $y - $self->{x_tick_length},
239             $self->{fgci});
240             }
241             }
242              
243             # CONTRIB Damon Brodie for x_tick_offset
244             next if
245             ($i - $self->{x_tick_offset}) % ($self->{x_label_skip}) and
246             $i != $self->{_data}->num_points - 1;
247              
248             $self->{gdta_x_axis}->set_text($x_val);
249              
250             my $yt = $y + $self->{axis_space};
251              
252             if ($self->{x_labels_vertical})
253             {
254             $self->{gdta_x_axis}->set_align('center', 'right');
255             $self->{gdta_x_axis}->draw($x, $yt, PI/2);
256             }
257             else
258             {
259             $self->{gdta_x_axis}->set_align('top', 'center');
260             $self->{gdta_x_axis}->draw($x, $yt);
261             }
262             }
263              
264             return $self;
265             }
266              
267              
268             sub draw_data_set
269             {
270             my $self = shift;
271             my $ds = shift;
272              
273             my @values = $self->{_data}->y_values($ds) or
274             return $self->_set_error("Impossible illegal data set: $ds",
275             $self->{_data}->error);
276              
277             # Pick a colour
278             my $dsci = $self->set_clr($self->pick_data_clr($ds));
279              
280             my $xl = undef;
281             my $yl = undef;
282              
283             for (my $i = 0; $i < @values; $i++)
284             {
285             next unless defined $values[$i];
286             my ($xp, $yp) = $self->val_to_pixel(
287             $self->{_data}->get_x($i), $values[$i], $ds);
288             $self->draw_line($xl,$yl,$xp,$yp,1,$dsci)
289             if (defined $xl && defined $yl);
290              
291             $xl = $xp;
292             $yl = $yp;
293              
294             }
295              
296             return $ds;
297             }
298              
299             sub draw_line # ($xs, $ys, $xe, $ye, $type, $colour_index)
300             {
301             my $self = shift;
302             my ($xs, $ys, $xe, $ye, $type, $clr) = @_;
303              
304             my $lw = $self->{line_width};
305             my $lts = $self->{line_type_scale};
306              
307             my $style = gdStyled;
308             my @pattern = ();
309              
310             LINE: {
311              
312             ($type == 2) && do {
313             # dashed
314              
315             for (1 .. $lts) { push @pattern, $clr }
316             for (1 .. $lts) { push @pattern, gdTransparent }
317              
318             $self->{graph}->setStyle(@pattern);
319              
320             last LINE;
321             };
322              
323             ($type == 3) && do {
324             # dotted,
325              
326             for (1 .. 2) { push @pattern, $clr }
327             for (1 .. 2) { push @pattern, gdTransparent }
328              
329             $self->{graph}->setStyle(@pattern);
330              
331             last LINE;
332             };
333              
334             ($type == 4) && do {
335             # dashed and dotted
336              
337             for (1 .. $lts) { push @pattern, $clr }
338             for (1 .. 2) { push @pattern, gdTransparent }
339             for (1 .. 2) { push @pattern, $clr }
340             for (1 .. 2) { push @pattern, gdTransparent }
341              
342             $self->{graph}->setStyle(@pattern);
343              
344             last LINE;
345             };
346              
347             # default: solid
348             $style = $clr;
349             }
350             # Tried the line_width thing with setBrush, ugly results
351             # TODO: This loop probably should be around the datasets
352             # for nicer results
353             my $i;
354             for $i (1..$lw)
355             {
356             my $yslw = $ys + int($lw/2) - $i;
357             my $yelw = $ye + int($lw/2) - $i;
358              
359             # Need the setstyle to reset
360             $self->{graph}->setStyle(@pattern) if (@pattern);
361             $self->{graph}->line( $xs, $yslw, $xe, $yelw, $style );
362             }
363             }
364              
365              
366             #
367             # Convert value coordinates to pixel coordinates on the canvas.
368             #
369             sub val_to_pixel # ($x, $y, $i) in real coords ($Dataspace),
370             { # return [x, y] in pixel coords
371             my $self = shift;
372             my ($x, $y, $i) = @_;
373              
374             my $x_min = $self->{x_min};
375             my $x_max = $self->{x_max};
376              
377             my $x_step = abs(($self->{right} - $self->{left})/($x_max - $x_min));
378              
379             my $ret_x = $self->{left} + ($x - $x_min) * $x_step;
380              
381             my $y_min = ($self->{two_axes} && $i == 2) ?
382             $self->{y_min}[2] : $self->{y_min}[1];
383              
384             my $y_max = ($self->{two_axes} && $i == 2) ?
385             $self->{y_max}[2] : $self->{y_max}[1];
386              
387             my $y_step = abs(($self->{bottom} - $self->{top})/($y_max - $y_min));
388              
389             my $ret_y = $self->{bottom} - ($y - $y_min) * $y_step;
390              
391             return(_round($ret_x), _round($ret_y));
392             }
393              
394              
395             "Just another true value";
396              
397             =head1 NAME
398              
399             XYlines - XY plotting module for GD::Graph.
400              
401             =head1 SYNOPSIS
402              
403             use GD::Graph::xylines;
404              
405             =head1 DESCRIPTION
406              
407             B is a I module that uses GD::Graph, GD,
408             to create and display PNG output for XY graphs with lines.
409              
410             =head1 USAGE
411              
412             See GD::Graph documentation for usage for all graphs.
413              
414             =head1 METHODS AND FUNCTIONS
415              
416             See GD::Graph documentation for methods for all GD::Graph graphs.
417              
418             =head1 OPTIONS
419              
420             =head2 Options for all graphs
421              
422             See GD::Graph documentation for options for all graphs.
423              
424             =head2 Options for graphs with axes
425              
426             See GD::Graph documentation for options for graphs with axes.
427              
428             =head1 CHANGE LOG
429              
430             =head2 GDGraph-XY-0.92
431              
432             B
433              
434             Added x_number_format functionality that mimics y_number_format
435             at the request of Ramon Acedo Rodriguez Erar@same-si.com
436              
437             =head2 GDGraph-XY-0.91
438              
439             B
440              
441             Thanks to some contributions by Andrew Crabb, ahc@sol.jhoc1.jhmi.edu,
442             the modules now pass the -w. Yes, they should have done this in the
443             first place, but I forgot.
444              
445             =head1 AUTHOR
446              
447             Written by: Martien Verbruggen Emgjv@comdyn.com.auE
448             Modified by: George 'Gaffer' Fitch Egaf3@gaf3.com
449              
450             =head2 Copyright
451              
452             GIFgraph: Copyright (c) 1995-1999 Martien Verbruggen.
453             Chart::PNGgraph: Copyright (c) 1999 Steve Bonds.
454             GD::Graph: Copyright (c) 1999 Martien Verbruggen.
455              
456             All rights reserved. This package is free software; you can redistribute
457             it and/or modify it under the same terms as Perl itself.
458              
459             =cut