1#==========================================================================
2# Module: GD::Graph::axestype3d
3#
4# Copyright (C) 1999,2001 Wadsack-Allen. All Rights Reserved.
5#
6# Based on axestype.pm,v 1.21 2000/04/15 08:59:36 mgjv
7#          Copyright (c) 1995-1998 Martien Verbruggen
8#
9#--------------------------------------------------------------------------
10# Date		Modification				                                 Author
11# -------------------------------------------------------------------------
12# 1999SEP18 Created 3D axestype base class (this                         JW
13#           module) changes noted in comments.
14# 1999OCT15 Fixed to include all GIFgraph functions                      JW
15#           necessary for PNG support.
16# 2000JAN19 Converted to GD::Graph sublcass                              JW
17# 2000FEB21 Fixed bug in y-labels' height                                JW
18# 2000APR18 Updated for compatibility with GD::Graph 1.30                JW
19# 2000AUG21 Added 3d shading                                             JW
20# 2000SEP04 Allowed box_clr without box axis                             JW
21# 06Dec2001 Fixed bug in rendering of x tick when x_tick_number is set   JW
22#==========================================================================
23# TODO
24#		* Modify to use true 3-d extrusions at any theta and phi
25#==========================================================================
26package GD::Graph::axestype3d;
27
28use strict;
29
30use GD::Graph;
31use GD::Graph::axestype;
32use GD::Graph::utils qw(:all);
33use GD::Graph::colour qw(:colours);
34use Carp;
35
36@GD::Graph::axestype3d::ISA = qw(GD::Graph::axestype);
37$GD::Graph::axestype3d::VERSION = '0.63';
38
39# Commented inheritance from GD::Graph::axestype unless otherwise noted.
40
41use constant PI => 4 * atan2(1,1);
42
43my %Defaults = (
44	depth_3d           => 20,
45	'3d_shading'       => 1,
46
47	# the rest are inherited
48);
49
50# Inherit _has_default
51
52
53# Can't inherit initialise, because %Defaults is referenced file-
54# specific, not class specific.
55sub initialise
56{
57	my $self = shift;
58
59	my $rc = $self->SUPER::initialise();
60
61	while( my($key, $val) = each %Defaults ) {
62		$self->{$key} = $val
63	} # end while
64
65	return $rc;
66} # end initialise
67
68# PUBLIC
69# Inherit plot
70# Inherit set
71# Inherit setup_text
72# Inherit set_x_label_font
73# Inherit set_y_label_font
74# Inherit set_x_axis_font
75# Inherit set_y_axis_font
76# Inherit set_legend
77# Inherit set_legend_font
78
79
80
81# ----------------------------------------------------------
82# Sub: init_graph
83#
84# Args: (None)
85#
86# Description:
87# Override GD::Graph::init_graph to add 3d shading colors,
88# if requested
89#
90# [From GD::Graph]
91# Initialise the graph output canvas, setting colours (and
92# getting back index numbers for them) setting the graph to
93# transparent, and interlaced, putting a logo (if defined)
94# on there.
95# ----------------------------------------------------------
96# Date      Modification                              Author
97# ----------------------------------------------------------
98# 20Aug2000 Added to support 3d graph extensions          JW
99# ----------------------------------------------------------
100sub init_graph {
101	my $self = shift;
102
103	# Sets up the canvas and color palette
104	$self->SUPER::init_graph( @_ );
105
106	# Now create highlights and showdows for each color
107	# in the palette
108	if( $self->{'3d_shading'} ) {
109		$self->{'3d_highlights'} = [];
110		$self->{'3d_shadows'} = [];
111		$self->{'3d_highlights'}[$self->{bgci}] = $self->set_clr( $self->_brighten( _rgb($self->{bgclr}) ) );
112		$self->{'3d_shadows'}[$self->{bgci}]    = $self->set_clr( $self->_darken( _rgb($self->{bgclr}) ) );
113
114		$self->{'3d_highlights'}[$self->{fgci}] = $self->set_clr( $self->_brighten( _rgb($self->{fgclr}) ) );
115		$self->{'3d_shadows'}[$self->{fgci}]    = $self->set_clr( $self->_darken( _rgb($self->{fgclr}) ) );
116
117		$self->{'3d_highlights'}[$self->{tci}] = $self->set_clr( $self->_brighten( _rgb($self->{textclr}) ) );
118		$self->{'3d_shadows'}[$self->{tci}]    = $self->set_clr( $self->_darken( _rgb($self->{textclr}) ) );
119
120		$self->{'3d_highlights'}[$self->{lci}] = $self->set_clr( $self->_brighten( _rgb($self->{labelclr}) ) );
121		$self->{'3d_shadows'}[$self->{lci}]    = $self->set_clr( $self->_darken( _rgb($self->{labelclr}) ) );
122
123		$self->{'3d_highlights'}[$self->{alci}] = $self->set_clr( $self->_brighten( _rgb($self->{axislabelclr}) ) );
124		$self->{'3d_shadows'}[$self->{alci}]    = $self->set_clr( $self->_darken( _rgb($self->{axislabelclr}) ) );
125
126		$self->{'3d_highlights'}[$self->{acci}] = $self->set_clr( $self->_brighten( _rgb($self->{accentclr}) ) );
127		$self->{'3d_shadows'}[$self->{acci}]    = $self->set_clr( $self->_darken( _rgb($self->{accentclr}) ) );
128
129		$self->{'3d_highlights'}[$self->{valuesci}] = $self->set_clr( $self->_brighten( _rgb($self->{valuesclr}) ) );
130		$self->{'3d_shadows'}[$self->{valuesci}]    = $self->set_clr( $self->_darken( _rgb($self->{valuesclr}) ) );
131
132		$self->{'3d_highlights'}[$self->{legendci}] = $self->set_clr( $self->_brighten( _rgb($self->{legendclr}) ) );
133		$self->{'3d_shadows'}[$self->{legendci}]    = $self->set_clr( $self->_darken( _rgb($self->{legendclr}) ) );
134
135		if( $self->{boxclr} ) {
136			$self->{'3d_highlights'}[$self->{boxci}] = $self->set_clr( $self->_brighten( _rgb($self->{boxclr}) ) );
137			$self->{'3d_shadows'}[$self->{boxci}]    = $self->set_clr( $self->_darken( _rgb($self->{boxclr}) ) );
138		} # end if
139	} # end if
140
141	return $self;
142} # end init_graph
143
144
145# PRIVATE
146
147# ----------------------------------------------------------
148# Sub: _brighten
149#
150# Args: $r, $g, $b
151#	$r, $g, $b	The Red, Green, and Blue components of a color
152#
153# Description: Brightens the color by adding white
154# ----------------------------------------------------------
155# Date      Modification                              Author
156# ----------------------------------------------------------
157# 21AUG2000 Created to build 3d highlights table          JW
158# ----------------------------------------------------------
159sub _brighten {
160	my $self = shift;
161	my( $r, $g, $b ) = @_;
162	my $p = ($r + $g + $b) / 70;
163	$p = 3 if $p < 3;
164	my $f = _max( $r / $p, _max( $g / $p, $b / $p ) );
165	$r = _min( 255, int( $r + $f ) );
166	$g = _min( 255, int( $g + $f ) );
167	$b = _min( 255, int( $b + $f ) );
168	return( $r, $g, $b );
169} # end _brighten
170
171# ----------------------------------------------------------
172# Sub: _darken
173#
174# Args: $r, $g, $b
175#	$r, $g, $b	The Red, Green, and Blue components of a color
176#
177# Description: Darkens the color by adding black
178# ----------------------------------------------------------
179# Date      Modification                              Author
180# ----------------------------------------------------------
181# 21AUG2000 Created to build 3d shadows table          JW
182# ----------------------------------------------------------
183sub _darken {
184	my $self = shift;
185	my( $r, $g, $b ) = @_;
186	my $p = ($r + $g + $b) / 70;
187	$p = 3 if $p < 3;
188	my $f = _max( $r / $p, _max( $g / $p, $b / $p) );
189	$r = _max( 0, int( $r - $f ) );
190	$g = _max( 0, int( $g - $f ) );
191	$b = _max( 0, int( $b - $f ) );
192	return( $r, $g, $b );
193} # end _darken
194
195
196# inherit check_data from GD::Graph
197
198# [JAW] Setup boundaries as parent, the adjust for 3d extrusion
199sub _setup_boundaries
200{
201	my $self = shift;
202
203	$self->SUPER::_setup_boundaries();
204
205	# adjust for top of 3-d extrusion
206	$self->{top} += $self->{depth_3d};
207
208	return $self->_set_error('Vertical size too small')
209		if $self->{bottom} <= $self->{top};
210
211	# adjust for right of 3-d extrusion
212	$self->{right} -= $self->{depth_3d};
213
214	return $self->_set_error('Horizontal size too small')
215		if $self->{right} <= $self->{left};
216
217	return $self;
218} # end _setup_boundaries
219
220# [JAW] Determine 3d-extrusion depth, then call parent
221sub setup_coords
222{
223	my $self = shift;
224
225	# Calculate the 3d-depth of the graph
226	# Note this sets a minimum depth of ~20 pixels
227#	if (!defined $self->{x_tick_number}) {
228		my $depth = _max( $self->{bar_depth}, $self->{line_depth} );
229		if( $self->{overwrite} == 1 ) {
230			$depth *= $self->{_data}->num_sets();
231		} # end if
232	   $self->{depth_3d} = _max( $depth, $self->{depth_3d} );
233#	} # end if
234
235	$self->SUPER::setup_coords();
236
237	return $self;
238} # end setup_coords
239
240# Inherit create_y_labels
241# Inherit get_x_axis_label_height
242# Inherit create_x_labels
243# inherit open_graph from GD::Graph
244# Inherit draw_text
245
246# [JAW] Draws entire bounding cube for 3-d extrusion
247sub draw_axes
248{
249	my $s = shift;
250	my $g = $s->{graph};
251
252	my ($l, $r, $b, $t) =
253		( $s->{left}, $s->{right}, $s->{bottom}, $s->{top} );
254	my $depth = $s->{depth_3d};
255
256	if ( $s->{box_axis} ) {
257		# -- Draw a bounding box
258		if( $s->{boxci} ) {
259			# -- Fill the box with color
260			# Back box
261			$g->filledRectangle($l+$depth+1, $t-$depth+1, $r+$depth-1, $b-$depth-1, $s->{boxci});
262
263			# Left side
264			my $poly = new GD::Polygon;
265			$poly->addPt( $l, $t );
266			$poly->addPt( $l + $depth, $t - $depth );
267			$poly->addPt( $l + $depth, $b - $depth );
268			$poly->addPt( $l, $b );
269			if( $s->{'3d_shading'} ) {
270				$g->filledPolygon( $poly, $s->{'3d_shadows'}[$s->{boxci}] );
271			} else {
272				$g->filledPolygon( $poly, $s->{boxci} );
273			} # end if
274
275			# Bottom
276			$poly = new GD::Polygon;
277			$poly->addPt( $l, $b );
278			$poly->addPt( $l + $depth, $b - $depth );
279			$poly->addPt( $r + $depth, $b - $depth );
280			$poly->addPt( $r, $b );
281			if( $s->{'3d_shading'} ) {
282				$g->filledPolygon( $poly, $s->{'3d_highlights'}[$s->{boxci}] );
283			} else {
284				$g->filledPolygon( $poly, $s->{boxci} );
285			} # end if
286		} # end if
287
288		# -- Draw the box frame
289
290		# Back box
291		$g->rectangle($l+$depth, $t-$depth, $r+$depth, $b-$depth, $s->{fgci});
292
293		# Connecting frame
294		$g->line($l, $t, $l + $depth, $t - $depth, $s->{fgci});
295		$g->line($r, $t, $r + $depth, $t - $depth, $s->{fgci});
296		$g->line($l, $b, $l + $depth, $b - $depth, $s->{fgci});
297		$g->line($r, $b, $r + $depth, $b - $depth, $s->{fgci});
298
299		# Front box
300		$g->rectangle($l, $t, $r, $b, $s->{fgci});
301
302	} else {
303		if( $s->{boxci} ) {
304			# -- Fill the background box with color
305			# Back box
306			$g->filledRectangle($l+$depth+1, $t-$depth+1, $r+$depth-1, $b-$depth-1, $s->{boxci});
307
308			# Left side
309			my $poly = new GD::Polygon;
310			$poly->addPt( $l, $t );
311			$poly->addPt( $l + $depth, $t - $depth );
312			$poly->addPt( $l + $depth, $b - $depth );
313			$poly->addPt( $l, $b );
314			if( $s->{'3d_shading'} ) {
315				$g->filledPolygon( $poly, $s->{'3d_shadows'}[$s->{boxci}] );
316			} else {
317				$g->filledPolygon( $poly, $s->{boxci} );
318			} # end if
319
320			# Bottom
321			$poly = new GD::Polygon;
322			$poly->addPt( $l, $b );
323			$poly->addPt( $l + $depth, $b - $depth );
324			$poly->addPt( $r + $depth, $b - $depth );
325			$poly->addPt( $r, $b );
326			if( $s->{'3d_shading'} ) {
327				$g->filledPolygon( $poly, $s->{'3d_highlights'}[$s->{boxci}] );
328			} else {
329				$g->filledPolygon( $poly, $s->{boxci} );
330			} # end if
331		} # end if
332		# -- Draw the frame only for back & sides
333
334		# Back box
335		$g->rectangle($l + $depth, $t - $depth, $r + $depth, $b - $depth, $s->{fgci});
336
337		# Y axis
338		my $poly = new GD::Polygon;
339		$poly->addPt( $l, $t );
340		$poly->addPt( $l, $b );
341		$poly->addPt( $l + $depth, $b - $depth );
342		$poly->addPt( $l + $depth, $t - $depth );
343		$g->polygon( $poly, $s->{fgci} );
344
345		# X axis
346		if( !$s->{zero_axis_only} ) {
347			$poly = new GD::Polygon;
348			$poly->addPt( $l, $b );
349			$poly->addPt( $r, $b );
350			$poly->addPt( $r + $depth, $b - $depth );
351			$poly->addPt( $l + $depth, $b - $depth );
352			$g->polygon( $poly, $s->{fgci} );
353		} # end if
354
355		# Second Y axis
356		if( $s->{two_axes} ){
357			$poly = new GD::Polygon;
358			$poly->addPt( $r, $b );
359			$poly->addPt( $r, $t );
360			$poly->addPt( $r + $depth, $t - $depth );
361			$poly->addPt( $r + $depth, $b - $depth );
362			$g->polygon( $poly, $s->{fgci} );
363		} # end if
364	} # end if
365
366	# Zero axis
367	if ($s->{zero_axis} or $s->{zero_axis_only})	{
368		my ($x, $y) = $s->val_to_pixel(0, 0, 1);
369		my $poly = new GD::Polygon;
370		$poly->addPt( $l, $y );
371		$poly->addPt( $r, $y );
372		$poly->addPt( $r + $depth, $y - $depth );
373		$poly->addPt( $l + $depth, $y - $depth);
374		$g->polygon( $poly, $s->{fgci} );
375	} # end if
376
377} # end draw_axes
378
379# [JAW] Draws ticks and values for y axes in 3d extrusion
380# Modified from MVERB source
381sub draw_y_ticks
382{
383	my $self = shift;
384
385	for my $t (0 .. $self->{y_tick_number})
386	{
387		for my $a (1 .. ($self->{two_axes} + 1))
388		{
389			my $value = $self->{y_values}[$a][$t];
390			my $label = $self->{y_labels}[$a][$t];
391
392			my ($x, $y) = $self->val_to_pixel(0, $value, $a);
393			$x = ($a == 1) ? $self->{left} : $self->{right};
394
395			# CONTRIB Jeremy Wadsack
396			# Draw on the back of the extrusion
397			$x += $self->{depth_3d};
398			$y -= $self->{depth_3d};
399
400			if ($self->{y_long_ticks})
401			{
402				$self->{graph}->line(
403					$x, $y,
404					$x + $self->{right} - $self->{left}, $y,
405					$self->{fgci}
406				) unless ($a-1);
407				# CONTRIB Jeremy Wadsack
408				# Draw conector ticks
409				$self->{graph}->line( $x - $self->{depth_3d},
410				                      $y + $self->{depth_3d},
411				                      $x,
412				                      $y,
413				                      $self->{fgci}
414				) unless ($a-1);
415			}
416			else
417			{
418				$self->{graph}->line(
419					$x, $y,
420					$x + (3 - 2 * $a) * $self->{y_tick_length}, $y,
421					$self->{fgci}
422				);
423				# CONTRIB Jeremy Wadsack
424				# Draw conector ticks
425				$self->{graph}->line( $x - $self->{depth_3d},
426				                      $y + $self->{depth_3d},
427				                      $x - $self->{depth_3d} + (3 - 2 * $a) * $self->{y_tick_length},
428				                      $y + $self->{depth_3d} - (3 - 2 * $a) * $self->{y_tick_length},
429				                      $self->{fgci}
430				);
431			}
432
433			next
434				if $t % ($self->{y_label_skip}) || ! $self->{y_plot_values};
435
436			$self->{gdta_y_axis}->set_text($label);
437			$self->{gdta_y_axis}->set_align('center',
438				$a == 1 ? 'right' : 'left');
439			$x -= (3 - 2 * $a) * $self->{axis_space};
440
441			# CONTRIB Jeremy Wadsack
442			# Subtract 3-d extrusion width from left axis label
443			# (it was added for ticks)
444			$x -= (2 - $a) * $self->{depth_3d};
445
446			# CONTRIB Jeremy Wadsack
447			# Add 3-d extrusion height to label
448			# (it was subtracted for ticks)
449			$y += $self->{depth_3d};
450
451			$self->{gdta_y_axis}->draw($x, $y);
452
453		} # end foreach
454	} # end foreach
455
456	return $self;
457
458} # end draw_y_ticks
459
460# [JAW] Darws ticks and values for x axes wih 3d extrusion
461# Modified from MVERB source
462sub draw_x_ticks
463{
464	my $self = shift;
465
466	for (my $i = 0; $i < $self->{_data}->num_points; $i++)
467	{
468		my ($x, $y) = $self->val_to_pixel($i + 1, 0, 1);
469
470		$y = $self->{bottom} unless $self->{zero_axis_only};
471
472		# CONTRIB  Damon Brodie for x_tick_offset
473		next if (!$self->{x_all_ticks} and
474				($i - $self->{x_tick_offset}) % $self->{x_label_skip} and
475				$i != $self->{_data}->num_points - 1
476			);
477
478		# CONTRIB Jeremy Wadsack
479		# Draw on the back of the extrusion
480		$x += $self->{depth_3d};
481		$y -= $self->{depth_3d};
482
483		if ($self->{x_ticks})
484		{
485			if ($self->{x_long_ticks})
486			{
487				# CONTRIB Jeremy Wadsack
488				# Move up by 3d depth
489				$self->{graph}->line( $x,
490				                      $self->{bottom} - $self->{depth_3d},
491				                      $x,
492				                      $self->{top} - $self->{depth_3d},
493				                      $self->{fgci});
494				# CONTRIB Jeremy Wadsack
495				# Draw conector ticks
496				$self->{graph}->line( $x - $self->{depth_3d},
497				                      $y + $self->{depth_3d},
498				                      $x,
499				                      $y,
500				                      $self->{fgci}
501				);
502			}
503			else
504			{
505				$self->{graph}->line( $x, $y, $x, $y - $self->{x_tick_length}, $self->{fgci} );
506				# CONTRIB Jeremy Wadsack
507				# Draw conector ticks
508				$self->{graph}->line( $x - $self->{depth_3d},
509				                      $y + $self->{depth_3d},
510				                      $x - $self->{depth_3d} + $self->{x_tick_length},
511				                      $y + $self->{depth_3d} - $self->{x_tick_length},
512				                      $self->{fgci}
513				);
514			}
515		}
516
517		# CONTRIB Damon Brodie for x_tick_offset
518		next if
519			($i - $self->{x_tick_offset}) % ($self->{x_label_skip}) and
520			$i != $self->{_data}->num_points - 1;
521
522		$self->{gdta_x_axis}->set_text($self->{_data}->get_x($i));
523
524		# CONTRIB Jeremy Wadsack
525		# Subtract 3-d extrusion width from left label
526		# Add 3-d extrusion height to left label
527		# (they were changed for ticks)
528		$x -= $self->{depth_3d};
529		$y += $self->{depth_3d};
530
531		my $yt = $y + $self->{axis_space};
532
533		if ($self->{x_labels_vertical})
534		{
535			$self->{gdta_x_axis}->set_align('center', 'right');
536			$self->{gdta_x_axis}->draw($x, $yt, PI/2);
537		}
538		else
539		{
540			$self->{gdta_x_axis}->set_align('top', 'center');
541			$self->{gdta_x_axis}->draw($x, $yt);
542		}
543
544	} # end for
545
546	return $self;
547
548} # end draw_x_ticks
549
550
551# CONTRIB Scott Prahl
552# Assume x array contains equally spaced x-values
553# and generate an appropriate axis
554#
555####
556# 'True' numerical X axis addition
557# From: Gary Deschaines
558#
559# These modification to draw_x_ticks_number pass x-tick values to the
560# val_to_pixel subroutine instead of x-tick indices when ture[sic] numerical
561# x-axis mode is detected.  Also, x_tick_offset and x_label_skip are
562# processed differently when true numerical x-axis mode is detected to
563# allow labeled major x-tick marks and un-labeled minor x-tick marks.
564#
565# For example:
566#
567#      x_tick_number =>  14,
568#      x_ticks       =>   1,
569#      x_long_ticks  =>   1,
570#      x_tick_length =>  -4,
571#      x_min_value   => 100,
572#      x_max_value   => 800,
573#      x_tick_offset =>   2,
574#      x_label_skip  =>   2,
575#
576#
577#      ~         ~    ~    ~    ~    ~    ~    ~    ~    ~    ~    ~         ~
578#      |         |    |    |    |    |    |    |    |    |    |    |         |
579#   1 -|         |    |    |    |    |    |    |    |    |    |    |         |
580#      |         |    |    |    |    |    |    |    |    |    |    |         |
581#   0 _|_________|____|____|____|____|____|____|____|____|____|____|_________|
582#                |    |    |    |    |    |    |    |    |    |    |
583#               200       300       400       500       600       700
584####
585# [JAW] Added commented items for 3d rendering
586# Based on MVERB source
587sub draw_x_ticks_number
588{
589	my $self = shift;
590
591	for my $i (0 .. $self->{x_tick_number})
592	{
593		my ($value, $x, $y);
594
595 		if (defined($self->{x_min_value}) && defined($self->{x_max_value}))
596 		{
597			next if ($i - $self->{x_tick_offset}) < 0;
598 			next if ($i + $self->{x_tick_offset}) > $self->{x_tick_number};
599 			$value = $self->{x_values}[$i];
600 			($x, $y) = $self->val_to_pixel($value, 0, 1);
601 		}
602 		else
603 		{
604			$value = ($self->{_data}->num_points - 1)
605						* ($self->{x_values}[$i] - $self->{true_x_min})
606						/ ($self->{true_x_max} - $self->{true_x_min});
607 			($x, $y) = $self->val_to_pixel($value + 1, 0, 1);
608 		}
609
610		$y = $self->{bottom} unless $self->{zero_axis_only};
611
612		# Draw on the back of the extrusion
613		$x += $self->{depth_3d};
614		$y -= $self->{depth_3d};
615
616		if ($self->{x_ticks})
617		{
618			if ($self->{x_long_ticks})
619			{
620				# XXX This mod needs to be done everywhere ticks are
621				# drawn
622				if ( $self->{x_tick_length} >= 0 )
623				{
624					# Move up by 3d depth
625					$self->{graph}->line( $x,
626					                      $self->{bottom} - $self->{depth_3d},
627												 $x,
628												 $self->{top} - $self->{depth_3d},
629												 $self->{fgci});
630				}
631				else
632				{
633					$self->{graph}->line(
634						$x, $self->{bottom} - $self->{x_tick_length},
635						$x, $self->{top}, $self->{fgci});
636				}
637				# CONTRIB Jeremy Wadsack
638				# Draw conector ticks
639				$self->{graph}->line( $x - $self->{depth_3d},
640				                      $y + $self->{depth_3d},
641				                      $x,
642				                      $y,
643				                      $self->{fgci}
644				);
645			}
646			else
647			{
648				$self->{graph}->line($x, $y,
649					$x, $y - $self->{x_tick_length}, $self->{fgci} );
650				# CONTRIB Jeremy Wadsack
651				# Draw conector ticks
652				$self->{graph}->line( $x - $self->{depth_3d},
653				                      $y + $self->{depth_3d},
654				                      $x, - $self->{depth_3d} + $self->{tick_length},
655				                      $y, + $self->{depth_3d} - $self->{tick_length},
656				                      $self->{fgci}
657				);
658			} # end if -- x_long_ticks
659		} # end if -- x_ticks
660
661		# If we have to skip labels, we'll do it here.
662		# Make sure to always draw the last one.
663		next if $i % $self->{x_label_skip} && $i != $self->{x_tick_number};
664
665		$self->{gdta_x_axis}->set_text($self->{x_labels}[$i]);
666
667		# CONTRIB Jeremy Wadsack
668		# Subtract 3-d extrusion width from left label
669		# Add 3-d extrusion height to left label
670		# (they were changed for ticks)
671		$x -= $self->{depth_3d};
672		$y += $self->{depth_3d};
673
674		if ($self->{x_labels_vertical})
675		{
676			$self->{gdta_x_axis}->set_align('center', 'right');
677			my $yt = $y + $self->{text_space}/2;
678			$self->{gdta_x_axis}->draw($x, $yt, PI/2);
679		}
680		else
681		{
682			$self->{gdta_x_axis}->set_align('top', 'center');
683			my $yt = $y + $self->{text_space}/2;
684			$self->{gdta_x_axis}->draw($x, $yt);
685		} # end if
686	} # end for
687
688	return $self;
689
690} # end draw_x_tick_number
691
692# Inherit draw_ticks
693# Inherit draw_data
694# Inherit draw_data_set
695# Inherit set_max_min
696# Inherit get_max_y
697# Inherit get_min_y
698# Inherit get_max_min_y_all
699# Inherit _get_bottom
700# Inherit val_to_pixel
701# Inherit setup_legend
702
703
704# [JW] Override draw_legend and reverse the drawing order
705# if cumulate is enabled so legend matches data on chart
706sub draw_legend
707{
708	my $self = shift;
709
710	return unless defined $self->{legend};
711
712	my $xl = $self->{lg_xs} + $self->{legend_spacing};
713	my $y  = $self->{lg_ys} + $self->{legend_spacing} - 1;
714
715	# If there's a frame, offset by the size and margin
716	$xl += $self->{legend_frame_margin} + $self->{legend_frame_size} if $self->{legend_frame_size};
717	$y += $self->{legend_frame_margin} + $self->{legend_frame_size} if $self->{legend_frame_size};
718
719	my $i = 0;
720	my $row = 1;
721	my $x = $xl;	# start position of current element
722	my @legends = @{$self->{legend}};
723	my $i_step = 1;
724
725	# If we are working in cumulate mode, then reverse the drawing order
726	if( $self->{cumulate} ) {
727		@legends = reverse @legends;
728		$i = scalar(@legends);
729		$i = $self->{_data}->num_sets if $self->{_data}->num_sets < $i;
730		$i++;
731		$i_step = -1;
732	} # end if
733
734	foreach my $legend (@legends)
735	{
736		$i += $i_step;
737
738		# Legend for Pie goes over first set, and all points
739		# Works in either direction
740		last if $i > $self->{_data}->num_sets;
741		last if $i < 1;
742
743		my $xe = $x;	# position within an element
744
745		next unless defined($legend) && $legend ne "";
746
747		$self->draw_legend_marker($i, $xe, $y);
748
749		$xe += $self->{legend_marker_width} + $self->{legend_spacing};
750		my $ys = int($y + $self->{lg_el_height}/2 - $self->{lgfh}/2);
751
752		$self->{gdta_legend}->set_text($legend);
753		$self->{gdta_legend}->draw($xe, $ys);
754
755		$x += $self->{lg_el_width};
756
757		if (++$row > $self->{lg_cols})
758		{
759			$row = 1;
760			$y += $self->{lg_el_height};
761			$x = $xl;
762		}
763	}
764
765	# If there's a frame, draw it now
766	if( $self->{legend_frame_size} ) {
767		$x = $self->{lg_xs} + $self->{legend_spacing};
768		$y = $self->{lg_ys} + $self->{legend_spacing} - 1;
769
770		for $i ( 0 .. $self->{legend_frame_size} - 1 ) {
771			$self->{graph}->rectangle(
772				$x + $i,
773				$y + $i,
774				$x + $self->{lg_x_size} + 2 * $self->{legend_frame_margin} - $i - 1,
775				$y + $self->{lg_y_size} + 2 * $self->{legend_frame_margin} - $i - 1,
776				$self->{acci},
777			);
778		} # end for
779	} # end if
780
781}
782
783
784
785# Inherit draw_legend_marker
786
7871;
788