2c2
< #			   Copyright (c) 2003 Brad Murray
---
> #              Copyright (c) 1995-1998 Martien Verbruggen
5,6c5,6
< #	Name:
< #		GD::Graph::radar.pm
---
> #   Name:
> #       GD::Graph::pie.pm
7a8
> # $Id: pie.pm,v 1.20 2003/02/10 22:12:41 mgjv Exp $
11c12
< package GD::Graph::radar;
---
> package GD::Graph::pie;
13c14
< $GD::Graph::pie::VERSION = '0.1';
---
> ($GD::Graph::pie::VERSION) = '$Revision: 1.20 $' =~ /\s([\d.]+)/;
26c27
< @GD::Graph::radar::ISA = qw( GD::Graph );
---
> @GD::Graph::pie::ISA = qw( GD::Graph );
30,31c31
< my %Defaults =
< (
---
> my %Defaults = (
33,54c33,54
<  # The angle at which to start the first data set
<  # 0 is pointing straight down
<  start_angle => 0,
< 
<  # and some public attributes without defaults
<  label		=> undef,
< 
<  # Absolute graphs always start at zero and cannot have negative
<  # values.  non-absolute graphs start at minimum data set value
<  absolute => 1,
< 
<  # number of scale markers to draw
<  nmarkers => 6,
< 
<  # if set, draw a polygon connecting the apices of each line
<  polygon => 1,
< 
<  # if defined, fill the polygon with the colour specified as
<  # a hex rgb string.  Note that if one of the data
<  # elements is zero, then the polygon will not fill as we fill
<  # from the origin
<  poly_fill => "#e4e4e4",
---
>     # Set the height of the pie.
>     # Because of the dependency of this on runtime information, this
>     # is being set in GD::Graph::pie::initialise
>  
>     #   pie_height => _round(0.1*${'width'}),
>     pie_height  => undef,
>  
>     # Do you want a 3D pie?
>     '3d'        => 1,
>  
>     # The angle at which to start the first data set
>     # 0 is at the front/bottom
>     start_angle => 0,
> 
>     # Angle below which a label on a pie slice is suppressed.
>     suppress_angle => 0,    # CONTRIB idea ryan <xomina@bitstream.net>
> 
>     # and some public attributes without defaults
>     label       => undef,
> 
>     # This misnamed attribute is used for pie marker colours
>     axislabelclr => 'black',
59,61c59,61
< 	my $self = shift;
< 	my $attr = shift || return;
< 	exists $Defaults{$attr} || $self->SUPER::_has_default($attr);
---
>     my $self = shift;
>     my $attr = shift || return;
>     exists $Defaults{$attr} || $self->SUPER::_has_default($attr);
66,71c66,72
< 	my $self = shift;
< 	$self->SUPER::initialise();
< 	while (my($key, $val) = each %Defaults)
< 		{ $self->{$key} = $val }
< 	$self->set_value_font(gdTinyFont);
< 	$self->set_label_font(gdSmallFont);
---
>     my $self = shift;
>     $self->SUPER::initialise();
>     while (my($key, $val) = each %Defaults)
>         { $self->{$key} = $val }
>     $self->set( pie_height => _round(0.1 * $self->{height}) );
>     $self->set_value_font(gdTinyFont);
>     $self->set_label_font(gdSmallFont);
77,78c78,79
< 	my $self = shift;
< 	my $data = shift;
---
>     my $self = shift;
>     my $data = shift;
80,85c81,87
< 	$self->check_data($data) 		or return;
< 	$self->init_graph() 			or return;
< 	$self->setup_text()				or return;
< 	$self->setup_coords() 			or return;
< 	$self->draw_text()				or return;
< 	$self->draw_data()				or return;
---
>     $self->check_data($data)        or return;
>     $self->init_graph()             or return;
>     $self->setup_text()             or return;
>     $self->setup_coords()           or return;
>     $self->draw_text()              or return;
>     $self->draw_pie()               or return;
>     $self->draw_data()              or return;
87c89
< 	return $self->{graph};
---
>     return $self->{graph};
92,94c94,96
< 	my $self = shift;
< 	$self->_set_font('gdta_label', @_) or return;
< 	$self->{gdta_label}->set_align('bottom', 'center');
---
>     my $self = shift;
>     $self->_set_font('gdta_label', @_) or return;
>     $self->{gdta_label}->set_align('bottom', 'center');
99,101c101,103
< 	my $self = shift;
< 	$self->_set_font('gdta_value', @_) or return;
< 	$self->{gdta_value}->set_align('center', 'center');
---
>     my $self = shift;
>     $self->_set_font('gdta_value', @_) or return;
>     $self->{gdta_value}->set_align('center', 'center');
113c115
< 	my $self = shift;
---
>     my $self = shift;
115c117,119
< 	# Make sure we're not reserving space we don't need.
---
>     # Make sure we're not reserving space we don't need.
>     $self->{'3d'} = 0           if     $self->{pie_height} <= 0;
>     $self->set(pie_height => 0) unless $self->{'3d'};
117,118c121,122
< 	my $tfh = $self->{title} ? $self->{gdta_title}->get('height') : 0;
< 	my $lfh = $self->{label} ? $self->{gdta_label}->get('height') : 0;
---
>     my $tfh = $self->{title} ? $self->{gdta_title}->get('height') : 0;
>     my $lfh = $self->{label} ? $self->{gdta_label}->get('height') : 0;
120,126c124,130
< 	# Calculate the bounding box for the graph, and
< 	# some width, height, and centre parameters
< 	$self->{bottom} = 
< 		$self->{height} - $self->{b_margin} -
< 		( $lfh ? $lfh + $self->{text_space} : 0 );
< 	$self->{top} = 
< 		$self->{t_margin} + ( $tfh ? $tfh + $self->{text_space} : 0 );
---
>     # Calculate the bounding box for the pie, and
>     # some width, height, and centre parameters
>     $self->{bottom} = 
>         $self->{height} - $self->{pie_height} - $self->{b_margin} -
>         ( $lfh ? $lfh + $self->{text_space} : 0 );
>     $self->{top} = 
>         $self->{t_margin} + ( $tfh ? $tfh + $self->{text_space} : 0 );
128,129c132,133
< 	return $self->_set_error('Vertical size too small') 
< 		if $self->{bottom} - $self->{top} <= 0;
---
>     return $self->_set_error('Vertical size too small') 
>         if $self->{bottom} - $self->{top} <= 0;
131,132c135,136
< 	$self->{left} = $self->{l_margin};
< 	$self->{right} = $self->{width} - $self->{r_margin};
---
>     $self->{left} = $self->{l_margin};
>     $self->{right} = $self->{width} - $self->{r_margin};
134,135c138,139
< 	return $self->_set_error('Horizontal size too small')
< 		if $self->{right} - $self->{left} <= 0;
---
>     return $self->_set_error('Horizontal size too small')
>         if $self->{right} - $self->{left} <= 0;
137,138c141,142
< 	$self->{w} = $self->{right}  - $self->{left};
< 	$self->{h} = $self->{bottom} - $self->{top};
---
>     $self->{w} = $self->{right}  - $self->{left};
>     $self->{h} = $self->{bottom} - $self->{top};
140,141c144,145
< 	$self->{xc} = ($self->{right}  + $self->{left})/2; 
< 	$self->{yc} = ($self->{bottom} + $self->{top})/2;
---
>     $self->{xc} = ($self->{right}  + $self->{left})/2; 
>     $self->{yc} = ($self->{bottom} + $self->{top})/2;
143c147
< 	return $self;
---
>     return $self;
151c155
< 	my $self = shift;
---
>     my $self = shift;
153,164c157,168
< 	if ( $self->{title} ) 
< 	{
< 		#print "'$s->{title}' at ($s->{xc},$s->{t_margin})\n";
< 		$self->{gdta_title}->set(colour => $self->{tci});
< 		$self->{gdta_title}->set_text($self->{title});
< 	}
< 
< 	if ( $self->{label} ) 
< 	{
< 		$self->{gdta_label}->set(colour => $self->{lci});
< 		$self->{gdta_label}->set_text($self->{label});
< 	}
---
>     if ( $self->{title} ) 
>     {
>         #print "'$s->{title}' at ($s->{xc},$s->{t_margin})\n";
>         $self->{gdta_title}->set(colour => $self->{tci});
>         $self->{gdta_title}->set_text($self->{title});
>     }
> 
>     if ( $self->{label} ) 
>     {
>         $self->{gdta_label}->set(colour => $self->{lci});
>         $self->{gdta_label}->set_text($self->{label});
>     }
166c170
< 	$self->{gdta_value}->set(colour => $self->{alci});
---
>     $self->{gdta_value}->set(colour => $self->{alci});
168c172
< 	return $self;
---
>     return $self;
174c178
< 	my $self = shift;
---
>     my $self = shift;
176,181c180,185
< 	$self->{gdta_title}->draw($self->{xc}, $self->{t_margin}) 
< 		if $self->{title}; 
< 	$self->{gdta_label}->draw($self->{xc}, $self->{height} - $self->{b_margin})
< 		if $self->{label};
< 	
< 	return $self;
---
>     $self->{gdta_title}->draw($self->{xc}, $self->{t_margin}) 
>         if $self->{title}; 
>     $self->{gdta_label}->draw($self->{xc}, $self->{height} - $self->{b_margin})
>         if $self->{label};
>     
>     return $self;
184c188,222
< # Draw the data lines and the polygon
---
> # draw the pie, without the data slices
> sub draw_pie
> {
>     my $self = shift;
> 
>     my $left = $self->{xc} - $self->{w}/2;
> 
>     $self->{graph}->arc(
>         $self->{xc}, $self->{yc}, 
>         $self->{w}, $self->{h},
>         0, 360, $self->{acci}
>     );
> 
>     $self->{graph}->arc(
>         $self->{xc}, $self->{yc} + $self->{pie_height}, 
>         $self->{w}, $self->{h},
>         0, 180, $self->{acci}
>     ) if ( $self->{'3d'} );
> 
>     $self->{graph}->line(
>         $left, $self->{yc},
>         $left, $self->{yc} + $self->{pie_height}, 
>         $self->{acci}
>     );
> 
>     $self->{graph}->line(
>         $left + $self->{w}, $self->{yc},
>         $left + $self->{w}, $self->{yc} + $self->{pie_height}, 
>         $self->{acci}
>     );
> 
>     return $self;
> }
> 
> # Draw the data slices
188c226,393
< 	my $self = shift;
---
>     my $self = shift;
> 
>     my $total = 0;
>     my @values = $self->{_data}->y_values(1);   # for now, only one pie..
>     for (@values)
>     {   
>         $total += $_ 
>     }
> 
>     return $self->_set_error("Pie data total is <= 0") 
>         unless $total > 0;
> 
>     my $ac = $self->{acci};         # Accent colour
>     my $pb = $self->{start_angle};
> 
>     for (my $i = 0; $i < @values; $i++)
>     {
>         # Set the data colour
>         my $dc = $self->set_clr_uniq($self->pick_data_clr($i + 1));
> 
>         # Set the angles of the pie slice
>         # Angle 0 faces down, positive angles are clockwise 
>         # from there.
>         #         ---
>         #        /   \
>         #        |    |
>         #        \ | /
>         #         ---
>         #          0
>         # $pa/$pb include the start_angle (so if start_angle
>         # is 90, there will be no pa/pb < 90.
>         my $pa = $pb;
>         $pb += my $slice_angle = 360 * $values[$i]/$total;
> 
>         # Calculate the end points of the lines at the boundaries of
>         # the pie slice
>         my ($xe, $ye) = cartesian(
>                 $self->{w}/2, $pa, 
>                 $self->{xc}, $self->{yc}, $self->{h}/$self->{w}
>             );
> 
>         $self->{graph}->line($self->{xc}, $self->{yc}, $xe, $ye, $ac);
> 
>         # Draw the lines on the front of the pie
>         $self->{graph}->line($xe, $ye, $xe, $ye + $self->{pie_height}, $ac)
>             if in_front($pa) && $self->{'3d'};
> 
>         # Make an estimate of a point in the middle of the pie slice
>         # And fill it
>         ($xe, $ye) = cartesian(
>                 3 * $self->{w}/8, ($pa+$pb)/2,
>                 $self->{xc}, $self->{yc}, $self->{h}/$self->{w}
>             );
> 
>         $self->{graph}->fillToBorder($xe, $ye, $ac, $dc);
> 
>         # If it's 3d, colour the front ones as well
>         #
>         # if one slice is very large (>180 deg) then we will need to
>         # fill it twice.  sbonds.
>         #
>         # Independently noted and fixed by Jeremy Wadsack, in a slightly
>         # different way.
>         if ($self->{'3d'}) 
>         {
>             foreach my $fill ($self->_get_pie_front_coords($pa, $pb)) 
>             {
>                 $self->{graph}->fillToBorder(
>                     $fill->[0], $fill->[1] + $self->{pie_height}/2, 
>                     $ac, $dc);
>             }
>         }
>     }
> 
>     # CONTRIB Jeremy Wadsack
>     #
>     # Large text, sticking out over the pie edge, could cause 3D pies to
>     # fill improperly: Drawing the text for a given slice before the
>     # next slice was drawn and filled could make the slice boundary
>     # disappear, causing the fill colour to flow out.  With this
>     # implementation, all the text is on top of the pie.
> 
>     $pb = $self->{start_angle};
>     for (my $i = 0; $i < @values; $i++)
>     {
>         next unless $values[$i];
> 
>         my $pa = $pb;
>         $pb += my $slice_angle = 360 * $values[$i]/$total;
> 
>         next if $slice_angle <= $self->{suppress_angle};
> 
>         my ($xe, $ye) = 
>             cartesian(
>                 3 * $self->{w}/8, ($pa+$pb)/2,
>                 $self->{xc}, $self->{yc}, $self->{h}/$self->{w}
>             );
> 
>         $self->put_slice_label($xe, $ye, $self->{_data}->get_x($i));
>     }
> 
>     return $self;
> 
> } #GD::Graph::pie::draw_data
> 
> sub _get_pie_front_coords # (angle 1, angle 2)
> {
>     my $self = shift;
>     my $pa = level_angle(shift);
>     my $pb = level_angle(shift);
>     my @fills = ();
> 
>     if (in_front($pa))
>     {
>         if (in_front($pb))
>         {
>             # both in front
>             # don't do anything
>             # Ah, but if this wraps all the way around the back
>             # then both pieces of the front need to be filled.
>             # sbonds.
>             if ($pa > $pb ) 
>             {
>                 # This takes care of the left bit on the front
>                 # Since we know exactly where we are, and in which
>                 # direction this works, we can just get the coordinates
>                 # for $pa.
>                 my ($x, $y) = cartesian(
>                     $self->{w}/2, $pa,
>                     $self->{xc}, $self->{yc}, $self->{h}/$self->{w}
>                 );
> 
>                 # and move one pixel to the left, but only if we don't
>                 # fall out of the pie!.
>                 push @fills, [$x - 1, $y]
>                     if $x - 1 > $self->{xc} - $self->{w}/2;
> 
>                 # Reset $pa to the right edge of the front arc, to do
>                 # the right bit on the front.
>                 $pa = level_angle(-$ANGLE_OFFSET);
>             }
>         }
>         else
>         {
>             # start in front, end in back
>             $pb = $ANGLE_OFFSET;
>         }
>     }
>     else
>     {
>         if (in_front($pb))
>         {
>             # start in back, end in front
>             $pa = $ANGLE_OFFSET - 180;
>         }
>         else
>         {
>             # both in back
>             return;
>         }
>     }
> 
>     my ($x, $y) = cartesian(
>         $self->{w}/2, ($pa + $pb)/2,
>         $self->{xc}, $self->{yc}, $self->{h}/$self->{w}
>     );
> 
>     push @fills, [$x, $y];
190,301c395,396
< 	my $max_val = 0;
< 	my @values = $self->{_data}->y_values(1);	# for now, only one
< 	my $min_val = $values[0];
<         my $scale = 1;
< 
< 	for (@values)
< 	{	
< 	    if ($_ > $max_val) { $max_val = $_; }
< 	    if ($_ < $min_val) { $min_val = $_; }
< 	}
< 
< 	if ($self->{'absolute'}) {
< 	    $scale = ($self->{w}/2) / $max_val;
< 	} else {
< 	    $scale = ($self->{w}/2) / ($max_val - $min_val);
< 	}
<        
< 	my $ac = $self->{acci};			# Accent colour
< 	my $pb = $self->{start_angle};
< 
< 	my $poly = new GD::Polygon;
< 	my @vertices = ();
< 
< 	for (my $i = 0; $i < @values; $i++)
< 	{
< 	    # Set the angles of each arm
< 	    # Angle 0 faces down, positive angles are clockwise 
< 	    # from there.
< 	    #         ---
< 	    #        /   \
< 	    #        |    |
< 	    #        \ | /
< 	    #         ---
< 	    #          0
< 	    # $pa/$pb include the start_angle (so if start_angle
< 	    # is 90, there will be no pa/pb < 90.
< 	    my $pa = $pb;
< 	    $pb += my $slice_angle = 360 / @values;
< 
< 	    # Calculate the end points of the lines at the boundaries of
< 	    # the pie slice
< 	    my $radius = $values[$i] * $scale;
< 	    if ($radius < 0 && $self->{'absolute'}) {$radius = 0;}
< 	    my ($xe, $ye) = cartesian(
< 				      $radius,
< 				      $pa, 
< 				      $self->{xc}, $self->{yc},
< 				      $self->{h}/$self->{w}
< 				      );
< 	    if ($self->{'polygon'}) {
< 		$poly->addPt($xe,$ye);
< 	    }
< 	    push(@vertices,[$xe,$ye]);
< 
< 	}
< 
< 	# draw the apex polygon
< 	$self->{'graph'}->polygon($poly,$ac);
< 	if (defined $self->{'poly_fill'}) {
< 	    my ($r,$g,$b) = GD::Graph::colour::hex2rgb($self->{'poly_fill'});
< 	    my $fc = $self->{'graph'}->colorAllocate($r,$g,$b);
< 	    $self->{'graph'}->fill($self->{'xc'}, $self->{'yc'}, $fc);
< 	}
< 
<         # draw markers
< 	my $mark_incr = 1;
<         if ($self->{'absolute'}) {
< 	    $mark_incr = int($max_val / $self->{'nmarkers'});
< 	} else {
< 	    $mark_incr = int(($max_val - $min_val) / $self->{'nmarkers'});
< 	}
< 	for (1..$self->{'nmarkers'}) {
< 	    my $width = 2 * $_ * $mark_incr * $scale;
< 	    $self->{'graph'}->arc(
< 				  $self->{'xc'}, $self->{'yc'},
< 				  $width,
< 				  $width * ($self->{h}/$self->{w}), 
< 				  0, 360,
< 				  $ac,
< 				  );
< 	}
< 
< 	# draw radar value bars
<         my $dc = $self->{'graph'}->colorAllocate(0,0,0);
< 	for (@vertices) {
< 	    $self->{graph}->line($self->{xc}, $self->{yc},
< 				 $_->[0], $_->[1], $dc);
< 	}
< 
< 	# draw labels
< 	$pb = $self->{start_angle};
< 	for (my $i = 0; $i < @values; $i++)
< 	{
< 	    next unless $values[$i];
< 
< 	    my $pa = $pb;
< 	    $pb += my $slice_angle = 360 / @values;
< 
< 	    next if $self->{suppress_angle} &&
<                     $slice_angle <= $self->{suppress_angle};
< 
< 	    my ($xe, $ye) = 
< 		cartesian(
< 			  3 * $self->{w}/8, $pa,
< 			  $self->{xc}, $self->{yc},
< 			  $self->{h}/$self->{w}
< 			  );
< 
< 	    $self->put_slice_label($xe, $ye, $self->{_data}->get_x($i));
< 	}
<        
< 	return $self;
---
>     return @fills;
> }
303c398,418
< } #GD::Graph::radar::draw_data
---
> # return true if this angle is on the front of the pie
> # XXX UGLY! We need to leave a slight room for error because of rounding
> # problems
> sub in_front
> {
>     my $a = level_angle(shift);
>     return 
>         $a > ($ANGLE_OFFSET - 180 + 0.00000001) && 
>         $a < $ANGLE_OFFSET - 0.000000001;
> }
> 
> # XXX Ugh! I need to fix this. See the GD::Text module for better ways
> # of doing this.
> # return a value for angle between -180 and 180
> sub level_angle # (angle)
> {
>     my $a = shift;
>     return level_angle($a-360) if ( $a > 180 );
>     return level_angle($a+360) if ( $a <= -180 );
>     return $a;
> }
326,328c441,443
< 	    $xi + $r * cos(PI * ($phi + $ANGLE_OFFSET)/180), 
< 	    $yi + $cr * $r * sin(PI * ($phi + $ANGLE_OFFSET)/180)
< 	    )
---
>         $xi + $r * cos(PI * ($phi + $ANGLE_OFFSET)/180), 
>         $yi + $cr * $r * sin(PI * ($phi + $ANGLE_OFFSET)/180)
>     )
332d446
< 
