pie.pm
上传用户:shbosideng
上传日期:2013-05-04
资源大小:1555k
文件大小:8k
- #==========================================================================
- # Copyright (c) 1995-1998 Martien Verbruggen
- #--------------------------------------------------------------------------
- #
- # Name:
- # GIFgraph::pie.pm
- #
- # $Id: pie.pm,v 1.1.1.1 2002/02/26 10:16:37 oetiker Exp $
- #
- #==========================================================================
- package GIFgraph::pie;
- use strict qw(vars refs subs);
- use GIFgraph;
- use GIFgraph::legend;
- use GIFgraph::utils qw(:all);
- use GIFgraph::colour qw(:colours :lists);
- @GIFgraph::pie::ISA = qw( GIFgraph::legend GIFgraph );
- my $ANGLE_OFFSET = 90;
- my %Defaults = (
-
- # Set the height of the pie.
- # Because of the dependency of this on runtime information, this
- # is being set in GIFgraph::pie::initialise
-
- # pie_height => _round(0.1*${'gifx'}),
-
- # 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,
- );
- {
- # PUBLIC methods, documented in pod
- sub plot($) # (@data)
- {
- my $self = shift;
- my $data = shift;
- $self->check_data($data);
- $self->init_graph($self->{graph});
- $self->plot_legend($self->{graph});
- $self->setup_coords();
- $self->draw_text($self->{graph});
- $self->draw_pie($self->{graph});
- $self->draw_data($data, $self->{graph});
- return $self->{graph}->gif;
- }
-
- sub set_label_font($) # (fontname)
- {
- my $self = shift;
- $self->{lf} = shift;
- $self->set(
- lfw => $self->{lf}->width,
- lfh => $self->{lf}->height,
- );
- }
-
- sub set_value_font($) # (fontname)
- {
- my $self = shift;
- $self->{vf} = shift;
- $self->set(
- vfw => $self->{vf}->width,
- vfh => $self->{vf}->height,
- );
- }
-
- # Inherit defaults() from GIFgraph
-
- # PRIVATE
- # called on construction by new.
- sub initialise()
- {
- my $self = shift;
-
- $self->SUPER::initialise();
-
- my $key;
- foreach $key (keys %Defaults)
- {
- $self->set( $key => $Defaults{$key} );
- }
-
- $self->set( pie_height => _round(0.1 * $self->{gify}) );
-
- $self->set_value_font(GD::gdTinyFont);
- $self->set_label_font(GD::gdSmallFont);
- }
- # inherit checkdata from GIFgraph
-
- # Setup the coordinate system and colours, calculate the
- # relative axis coordinates in respect to the gif size.
-
- sub setup_coords()
- {
- my $s = shift;
-
- # Make sure we're not reserving space we don't need.
- $s->set(tfh => 0) unless ( $s->{title} );
- $s->set(lfh => 0) unless ( $s->{label} );
- $s->set('3d' => 0) if ( $s->{pie_height} <= 0 );
- $s->set(pie_height => 0) unless ( $s->{'3d'} );
-
- # Calculate the bounding box for the pie, and
- # some width, height, and centre parameters
- $s->{bottom} =
- $s->{gify} - $s->{pie_height} - $s->{b_margin} -
- ( $s->{lfh} ? $s->{lfh} + $s->{text_space} : 0 );
- $s->{top} =
- $s->{t_margin} + ( $s->{tfh} ? $s->{tfh} + $s->{text_space} : 0 );
- $s->{left} = $s->{l_margin};
- $s->{right} = $s->{gifx} - $s->{r_margin};
- ( $s->{w}, $s->{h} ) =
- ( $s->{right}-$s->{left}, $s->{bottom}-$s->{top} );
- ( $s->{xc}, $s->{yc} ) =
- ( ($s->{right}+$s->{left})/2, ($s->{bottom}+$s->{top})/2 );
-
- die "Vertical Gif size too small"
- if ( ($s->{bottom} - $s->{top}) <= 0 );
- die "Horizontal Gif size too small"
- if ( ($s->{right} - $s->{left}) <= 0 );
- }
-
- # inherit open_graph from GIFgraph
-
- # Put the text on the canvas.
- sub draw_text($) # (GD::Image)
- {
- my $s = shift;
- my $g = shift;
-
- if ( $s->{tfh} )
- {
- my $tx = $s->{xc} - length($s->{title}) * $s->{tfw}/2;
- $g->string($s->{tf}, $tx, $s->{t_margin}, $s->{title}, $s->{tci});
- }
- if ( $s->{lfh} )
- {
- my $tx = $s->{xc} - length($s->{label}) * $s->{lfw}/2;
- my $ty = $s->{gify} - $s->{b_margin} - $s->{lfh};
- $g->string($s->{lf}, $tx, $ty, $s->{label}, $s->{lci});
- }
- }
-
- # draw the pie, without the data slices
-
- sub draw_pie($) # (GD::Image)
- {
- my $s = shift;
- my $g = shift;
- my $left = $s->{xc} - $s->{w}/2;
- $g->arc(
- $s->{xc}, $s->{yc},
- $s->{w}, $s->{h},
- 0, 360, $s->{acci}
- );
- $g->arc(
- $s->{xc}, $s->{yc} + $s->{pie_height},
- $s->{w}, $s->{h},
- 0, 180, $s->{acci}
- ) if ( $s->{'3d'} );
- $g->line(
- $left, $s->{yc},
- $left, $s->{yc} + $s->{pie_height},
- $s->{acci}
- );
- $g->line(
- $left + $s->{w}, $s->{yc},
- $left + $s->{w}, $s->{yc} + $s->{pie_height},
- $s->{acci}
- );
- }
-
- # Draw the data slices
-
- sub draw_data($$) # (@data, GD::Image)
- {
- my $s = shift;
- my $data = shift;
- my $g = shift;
- my $total = 0;
- my $j = 1; # for now, only one pie..
-
- my $i;
- for $i ( 0 .. $s->{numpoints} )
- {
- $total += $data->[$j][$i];
- }
- die "no Total" unless $total;
-
- my $ac = $s->{acci}; # Accent colour
- my $pb = $s->{start_angle};
- my $val = 0;
- for $i ( 0..$s->{numpoints} )
- {
- # Set the data colour. Colours index from 1 not 0.
- my $dc = $s->set_clr( $g, $s->pick_data_clr($i+1) );
- # Set the angles of the pie slice
- my $pa = $pb;
- $pb += 360 * $data->[1][$i]/$total;
- # Calculate the end points of the lines at the boundaries of
- # the pie slice
- my ($xe, $ye) =
- cartesian(
- $s->{w}/2, $pa,
- $s->{xc}, $s->{yc}, $s->{h}/$s->{w}
- );
- $g->line($s->{xc}, $s->{yc}, $xe, $ye, $ac);
- # Draw the lines on the front of the pie
- $g->line($xe, $ye, $xe, $ye + $s->{pie_height}, $ac)
- if ( in_front($pa) && $s->{'3d'} );
- # Make an estimate of a point in the middle of the pie slice
- # And fill it
- ($xe, $ye) =
- cartesian(
- 3 * $s->{w}/8, ($pa+$pb)/2,
- $s->{xc}, $s->{yc}, $s->{h}/$s->{w}
- );
- $g->fillToBorder($xe, $ye, $ac, $dc);
- # Horrible kludge by AF # $s->put_label($g, $xe, $ye, $data->[0][$i]);
- # If it's 3d, colour the front ones as well
- if ( $s->{'3d'} )
- {
- my ($xe, $ye) = $s->_get_pie_front_coords($pa, $pb);
- $g->fillToBorder($xe, $ye + $s->{pie_height}/2, $ac, $dc)
- if (defined($xe) && defined($ye));
- }
- }
- # More horrible kludge by AF
- $pb = $s->{start_angle};
- for $i ( 0..$s->{numpoints} )
- {
- my $pa = $pb;
- $pb += 360*$data->[1][$i]/$total;
- my ($xe, $ye) =
- cartesian(
- 3 * $s->{w}/8, ($pa+$pb)/2,
- $s->{xc}, $s->{yc}, $s->{h}/$s->{w}
- );
- $s->put_label($g, $xe, $ye, $$data[0][$i]);
- }
- } #GIFgraph::pie::draw_data
- sub _get_pie_front_coords($$) # (angle 1, angle 2)
- {
- my $s = shift;
- my $pa = level_angle(shift);
- my $pb = level_angle(shift);
- if (in_front($pa))
- {
- if (in_front($pb))
- {
- # both in front
- # don't do anything
- }
- 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(
- $s->{w}/2, ($pa+$pb)/2,
- $s->{xc}, $s->{yc}, $s->{h}/$s->{w}
- );
- return ($x, $y);
- }
-
- # return true if this angle is on the front of the pie
- sub in_front($) # (angle)
- {
- my $a = level_angle( shift );
- ( $a > ($ANGLE_OFFSET - 180) && $a < $ANGLE_OFFSET ) ? 1 : 0;
- }
-
- # 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;
- }
-
- # put the label on the pie
-
- sub put_label($) # (GD:Image)
- {
- my $s = shift;
- my $g = shift;
- my ($x, $y, $label) = @_;
- $x -= length($label) * $s->{vfw}/2;
- $y -= $s->{vfw}/2;
- $g->string($s->{vf}, $x, $y, $label, $s->{alci});
- }
-
- # return x, y coordinates from input
- # radius, angle, center x and y and a scaling factor (height/width)
- #
- # $ANGLE_OFFSET is used to define where 0 is meant to be
- sub cartesian($$$$$)
- {
- my ($r, $phi, $xi, $yi, $cr) = @_;
- my $PI=4*atan2(1, 1);
- return (
- $xi + $r * cos($PI * ($phi + $ANGLE_OFFSET)/180),
- $yi + $cr * $r * sin($PI * ($phi + $ANGLE_OFFSET)/180)
- );
- }
- } # End of package GIFgraph::pie
-
- 1;