File Coverage

blib/lib/GD/Graph/bars3d.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             # Module: GD::Graph::bars3d
3             #
4             # Copyright (C) 1999,2001 Wadsack-Allen. All Rights Reserved.
5             #
6             # Based on GD::Graph::bars.pm,v 1.16 2000/03/18 10:58:39 mgjv
7             # Copyright (c) 1995-1998 Martien Verbruggen
8             #
9             #--------------------------------------------------------------------------
10             # Date Modification Author
11             # -------------------------------------------------------------------------
12             # 1999SEP18 Created 3D bar chart class (this module) JAW
13             # 1999SEP19 Rewrote to include a single bar-drawing JAW
14             # function and process all bars in series
15             # 1999SEP19 Implemented support for overwrite 2 style JAW
16             # 1999SEP19 Fixed a bug in color cycler (colors were off by 1) JAW
17             # 2000JAN19 Converted to GD::Graph class JAW
18             # 2000MAR10 Fixed bug where bars ran off bottom of chart JAW
19             # 2000APR18 Modified to be compatible with GD::Graph 1.30 JAW
20             # 2000APR24 Fixed a lot of rendering bugs and added shading JAW
21             # 2000AUG21 Added 3d shading JAW
22             # 2000AUG24 Fixed shading on cycle_clrs option JAW
23             # 06Dec2002 Fixed on-bar rendering with bars.pm draw_values JW
24             #==========================================================================
25             package GD::Graph::bars3d;
26              
27 1     1   1596 use strict;
  1         3  
  1         51  
28              
29 1     1   1152 use GD::Graph::axestype3d;
  0            
  0            
30             use GD::Graph::bars;
31             use GD::Graph::utils qw(:all);
32             use GD::Graph::colour qw(:colours);
33              
34             @GD::Graph::bars3d::ISA = qw(GD::Graph::axestype3d);
35             $GD::Graph::bars3d::VERSION = '0.63';
36              
37             use constant PI => 4 * atan2(1,1);
38              
39              
40             my %Defaults = (
41             # Spacing between the bars
42             bar_spacing => 0,
43            
44             # The 3-d extrusion depth of the bars
45             bar_depth => 10,
46             );
47              
48             sub initialise
49             {
50             my $self = shift;
51              
52             my $rc = $self->SUPER::initialise();
53             $self->set(correct_width => 1);
54              
55             while( my($key, $val) = each %Defaults ) {
56             $self->{$key} = $val
57             } # end while
58              
59             return $rc;
60             } # end initialise
61              
62             sub set
63             {
64             my $s = shift;
65             my %args = @_;
66              
67             $s->{_set_error} = 0;
68              
69             for (keys %args)
70             {
71             /^bar_depth$/ and do
72             {
73             $s->{bar_depth} = $args{$_};
74             delete $args{$_};
75             next;
76             };
77             }
78              
79             return $s->SUPER::set(%args);
80             }
81              
82              
83             # CONTRIB Jeremy Wadsack
84             # This is a complete overhaul of the original GD::Graph::bars
85             # design, because all versions (overwrite = 0, 1, 2)
86             # require that the bars be drawn in a loop of point over sets
87             sub draw_data
88             {
89             my $self = shift;
90             my $g = $self->{graph};
91              
92             my $bar_s = _round($self->{bar_spacing}/2);
93              
94             my $zero = $self->{zeropoint};
95              
96             my $i;
97             my @iterate = (0 .. $self->{_data}->num_points());
98             for $i ($self->{rotate_chart} ? reverse(@iterate) : @iterate) {
99             my ($xp, $t);
100             my $overwrite = 0;
101             $overwrite = $self->{overwrite} if defined $self->{overwrite};
102            
103             my $j;
104             my @iterate = (1 .. $self->{_data}->num_sets());
105             for $j (($self->{rotate_chart} && $self->{cumulate} == 0) ? reverse(@iterate) : @iterate) {
106             my $value = $self->{_data}->get_y( $j, $i );
107             next unless defined $value;
108              
109             my $bottom = $self->_get_bottom($j, $i);
110             $value = $self->{_data}->get_y_cumulative($j, $i)
111             if ($self->{cumulate});
112              
113             # Pick a data colour, calc shading colors too, if requested
114             # cycle_clrs option sets the color based on the point, not the dataset.
115             my @rgb;
116             if( $self->{cycle_clrs} ) {
117             @rgb = $self->pick_data_clr( $i + 1 );
118             } else {
119             @rgb = $self->pick_data_clr( $j );
120             } # end if
121             my $dsci = $self->set_clr( @rgb );
122             if( $self->{'3d_shading'} ) {
123             $self->{'3d_highlights'}[$dsci] = $self->set_clr( $self->_brighten( @rgb ) );
124             $self->{'3d_shadows'}[$dsci] = $self->set_clr( $self->_darken( @rgb ) );
125             } # end if
126            
127             # contrib "Bremford, Mike"
128             my $brci;
129             if( $self->{cycle_clrs} > 1 ) {
130             $brci = $self->set_clr($self->pick_data_clr($i + 1));
131             } else {
132             $brci = $self->set_clr($self->pick_border_clr($j));
133             } # end if
134              
135              
136             # get coordinates of top and center of bar
137             ($xp, $t) = $self->val_to_pixel($i + 1, $value, $j);
138              
139             # calculate offsets of this bar
140             my $x_offset = 0;
141             my $y_offset = 0;
142             if( $overwrite == 1 ) {
143             $x_offset = $self->{bar_depth} * ($self->{_data}->num_sets() - $j);
144             $y_offset = $self->{bar_depth} * ($self->{_data}->num_sets() - $j);
145             }
146             $t -= $y_offset;
147              
148              
149             # calculate left and right of bar
150             my ($l, $r);
151             if ($self->{rotate_chart}) {
152             $l = $bottom;
153             ($r) = $self->val_to_pixel($i + 1, $value, $j);
154             }
155              
156             if( (ref $self eq 'GD::Graph::mixed') || ($overwrite >= 1) )
157             {
158             if ($self->{rotate_chart}) {
159             $bottom = $t + $self->{x_step}/2 - $bar_s + $x_offset;
160             $t = $t - $self->{x_step}/2 + $bar_s + $x_offset;
161             }
162             else
163             {
164             $l = $xp - $self->{x_step}/2 + $bar_s + $x_offset;
165             $r = $xp + $self->{x_step}/2 - $bar_s + $x_offset;
166             }
167             }
168             else
169             {
170             if ($self->{rotate_chart}) {
171             warn "base is $t";
172             $bottom = $t - $self->{x_step}/2
173             + ($j) * $self->{x_step}/$self->{_data}->num_sets()
174             + $bar_s + $x_offset;
175             $t = $t - $self->{x_step}/2
176             + ($j-1) * $self->{x_step}/$self->{_data}->num_sets()
177             - $bar_s + $x_offset;
178             warn "top bottom is ($t, $bottom)";
179             }
180             else
181             {
182             $l = $xp
183             - $self->{x_step}/2
184             + ($j - 1) * $self->{x_step}/$self->{_data}->num_sets()
185             + $bar_s + $x_offset;
186             $r = $xp
187             - $self->{x_step}/2
188             + $j * $self->{x_step}/$self->{_data}->num_sets()
189             - $bar_s + $x_offset;
190             }
191             }
192              
193             if ($value >= 0) {
194             # draw the positive bar
195             $self->draw_bar( $g, $l, $t, $r, $bottom-$y_offset, $dsci, $brci, 0 )
196             } else {
197             # draw the negative bar
198             $self->draw_bar( $g, $l, $bottom-$y_offset, $r, $t, $dsci, $brci, -1 )
199             } # end if
200              
201             } # end for
202             } # end for
203              
204              
205             # redraw the 'zero' axis, front and right
206             if( $self->{zero_axis} ) {
207             $g->line(
208             $self->{left}, $self->{zeropoint},
209             $self->{right}, $self->{zeropoint},
210             $self->{fgci} );
211             $g->line(
212             $self->{right}, $self->{zeropoint},
213             $self->{right}+$self->{depth_3d}, $self->{zeropoint}-$self->{depth_3d},
214             $self->{fgci} );
215             } # end if
216              
217             # redraw the box face
218             if ( $self->{box_axis} ) {
219             # Axes box
220             $g->rectangle($self->{left}, $self->{top}, $self->{right}, $self->{bottom}, $self->{fgci});
221             $g->line($self->{right}, $self->{top}, $self->{right} + $self->{depth_3d}, $self->{top} - $self->{depth_3d}, $self->{fgci});
222             $g->line($self->{right}, $self->{bottom}, $self->{right} + $self->{depth_3d}, $self->{bottom} - $self->{depth_3d}, $self->{fgci});
223             } # end if
224              
225             return $self;
226            
227             } # end draw_data
228              
229             # CONTRIB Jeremy Wadsack
230             # This function draws a bar at the given
231             # coordinates. This is called in all three
232             # overwrite modes.
233             sub draw_bar {
234             my $self = shift;
235             my $g = shift;
236             my( $l, $t, $r, $b, $dsci, $brci, $neg ) = @_;
237            
238             # get depth of the bar
239             my $depth = $self->{bar_depth};
240              
241             # get the bar shadow depth and color
242             my $bsd = $self->{shadow_depth};
243             my $bsci = $self->set_clr(_rgb($self->{shadowclr}));
244              
245             my( $xi );
246              
247             # shadow
248             if( $bsd > 0 ) {
249             my $sb = $b - $depth;
250             my $st = $t - $depth + $bsd;
251            
252             if( $neg != 0 ) {
253             $st -= $bsd;
254             if( $self->{zero_axis_only} ) {
255             $sb += $bsd;
256             } else {
257             $sb = _min($b-$depth+$bsd, $self->{bottom}-$depth);
258             } # end if
259             } # end if
260              
261             # ** If this isn't the back bar, then no side shadow should be
262             # drawn or else the top should be lowered by
263             # ($bsd * dataset_num), it should be drawn on the back surface,
264             # and a shadow should be drawn behind the front bar if the
265             # bar is positive and the back is negative.
266            
267             $g->filledRectangle($l+$depth+$bsd,
268             $st,
269             $r+$depth+$bsd,
270             $sb,
271             $bsci);
272              
273             # Only draw bottom shadow if at the bottom and has bottom
274             # axis. Always draw top shadow
275             if( ($neg == 0) || ($sb >= $self->{bottom}-$depth) ) {
276             my $poly = new GD::Polygon;
277             $poly->addPt( $r, $b );
278             $poly->addPt( $r+$bsd, $b );
279             $poly->addPt( $r+$depth+$bsd, $b-$depth );
280             $poly->addPt( $r+$depth, $b-$depth );
281             $g->filledPolygon( $poly, $bsci );
282             } # end if
283              
284             } # end if
285              
286             # side
287             my $poly = new GD::Polygon;
288             $poly->addPt( $r, $t );
289             $poly->addPt( $r+$depth, $t-$depth );
290             $poly->addPt( $r+$depth, $b-$depth );
291             $poly->addPt( $r, $b );
292             if( $self->{'3d_shading'} ) {
293             $g->filledPolygon( $poly, $self->{'3d_shadows'}[$dsci] );
294             } else {
295             $g->filledPolygon( $poly, $dsci );
296             } # end if
297             $g->polygon( $poly, $brci );
298              
299             # top
300             # -- only draw negative tops if the bar starts at zero
301             if( ($neg == 0) || ($t <= $self->{zeropoint}) ) {
302             $poly = new GD::Polygon;
303             $poly->addPt( $l, $t );
304             $poly->addPt( $l+$depth, $t-$depth );
305             $poly->addPt( $r+$depth, $t-$depth );
306             $poly->addPt( $r, $t );
307             if( $self->{'3d_shading'} ) {
308             $g->filledPolygon( $poly, $self->{'3d_highlights'}[$dsci] );
309             } else {
310             $g->filledPolygon( $poly, $dsci );
311             } # end if
312             $g->polygon( $poly, $brci );
313             } # end if
314              
315             # face
316             $g->filledRectangle( $l, $t, $r, $b, $dsci );
317             $g->rectangle( $l, $t, $r, $b, $brci );
318              
319             } # end draw_bar
320              
321             # [JAW] Overrides axestype's set_max_min.
322             # Go through the parent's process then adjust the baseline to 0 for bar graphs.
323             sub set_max_min {
324             my $self = shift;
325              
326             $self->SUPER::set_max_min( @_ );
327            
328             # This code is taken from Martien's axestype.pm
329             for my $i (1..($self->{two_axes} ? 2 : 1)) {
330             # If at the same side of the zero axis
331             if( $self->{y_max}[$i] && $self->{y_min}[$i]/$self->{y_max}[$i] > 0 ) {
332             $self->{y_min}[$i] > 0 ?
333             $self->{y_min}[$i] = 0 :
334             $self->{y_max}[$i] = 0 ;
335             } # end if
336             } # end for
337              
338             return $self;
339             } # end set_max_min
340              
341              
342             # [JW] Just use the one in GD::Graph::bars
343             sub draw_values
344             {
345             return &GD::Graph::bars::draw_values( @_ );
346             }
347              
348              
349             1;