(
$GD::Graph::pie::VERSION
) =
'$Revision: 1.21 $'
=~ /\s([\d.]+)/;
@GD::Graph::pie::ISA
=
qw( GD::Graph )
;
my
$ANGLE_OFFSET
= 90;
my
%Defaults
= (
pie_height
=>
undef
,
'3d'
=> 1,
start_angle
=> 0,
suppress_angle
=> 0,
label
=>
undef
,
axislabelclr
=>
'black'
,
);
sub
_has_default {
my
$self
=
shift
;
my
$attr
=
shift
||
return
;
exists
$Defaults
{
$attr
} ||
$self
->SUPER::_has_default(
$attr
);
}
sub
initialise
{
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);
}
sub
plot
{
my
$self
=
shift
;
my
$data
=
shift
;
$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
;
return
$self
->{graph};
}
sub
set_label_font
{
my
$self
=
shift
;
$self
->_set_font(
'gdta_label'
,
@_
) or
return
;
$self
->{gdta_label}->set_align(
'bottom'
,
'center'
);
}
sub
set_value_font
{
my
$self
=
shift
;
$self
->_set_font(
'gdta_value'
,
@_
) or
return
;
$self
->{gdta_value}->set_align(
'center'
,
'center'
);
}
sub
setup_coords()
{
my
$self
=
shift
;
$self
->{
'3d'
} = 0
if
$self
->{pie_height} <= 0;
$self
->set(
pie_height
=> 0)
unless
$self
->{
'3d'
};
my
$tfh
=
$self
->{title} ?
$self
->{gdta_title}->get(
'height'
) : 0;
my
$lfh
=
$self
->{label} ?
$self
->{gdta_label}->get(
'height'
) : 0;
$self
->{bottom} =
$self
->{height} -
$self
->{pie_height} -
$self
->{b_margin} -
(
$lfh
?
$lfh
+
$self
->{text_space} : 0 ) - 1;
$self
->{top} =
$self
->{t_margin} + (
$tfh
?
$tfh
+
$self
->{text_space} : 0 );
return
$self
->_set_error(
'Vertical size too small'
)
if
$self
->{bottom} -
$self
->{top} <= 0;
$self
->{left} =
$self
->{l_margin};
$self
->{right} =
$self
->{width} -
$self
->{r_margin} - 1;
$self
->{right}--
if
(
$self
->{right} -
$self
->{left}) % 2;
$self
->{bottom}--
if
(
$self
->{bottom} -
$self
->{top}) % 2;
return
$self
->_set_error(
'Horizontal size too small'
)
if
$self
->{right} -
$self
->{left} <= 0;
$self
->{w} =
$self
->{right} -
$self
->{left} + 1;
$self
->{h} =
$self
->{bottom} -
$self
->{top} + 1;
$self
->{xc} = (
$self
->{right} +
$self
->{left})/2;
$self
->{yc} = (
$self
->{bottom} +
$self
->{top})/2;
return
$self
;
}
sub
setup_text
{
my
$self
=
shift
;
if
(
$self
->{title} )
{
$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});
}
$self
->{gdta_value}->set(
colour
=>
$self
->{alci});
return
$self
;
}
sub
draw_text
{
my
$self
=
shift
;
$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
;
}
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
;
}
sub
draw_data
{
my
$self
=
shift
;
my
$total
= 0;
my
@values
=
$self
->{_data}->y_values(1);
for
(
@values
)
{
$total
+=
$_
}
return
$self
->_set_error(
"Pie data total is <= 0"
)
unless
$total
> 0;
my
$ac
=
$self
->{acci};
my
$pb
=
$self
->{start_angle};
for
(
my
$i
= 0;
$i
<
@values
;
$i
++)
{
next
unless
$values
[
$i
];
my
$dc
=
$self
->set_clr_uniq(
$self
->pick_data_clr(
$i
+ 1));
my
$pa
=
$pb
;
$pb
+=
my
$slice_angle
= 360 *
$values
[
$i
]/
$total
;
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
);
$self
->{graph}->line(
$xe
,
$ye
,
$xe
,
$ye
+
$self
->{pie_height},
$ac
)
if
in_front(
$pa
) &&
$self
->{
'3d'
};
(
$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
(
$self
->{
'3d'
})
{
foreach
my
$fill
(
$self
->_get_pie_front_coords(
$pa
,
$pb
))
{
my
(
$fx
,
$fy
) =
@$fill
;
my
$new_y
=
$fy
+
$self
->{pie_height}/2;
while
(
$new_y
>
$fy
) {
if
(
$self
->{graph}->getPixel(
$fx
,
$new_y
) !=
$ac
) {
$self
->{graph}->fillToBorder(
$fx
,
$new_y
,
$ac
,
$dc
);
last
;
}
}
continue
{
$new_y
-- }
}
}
}
$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
;
}
sub
_get_pie_front_coords
{
my
$self
=
shift
;
my
$pa
= level_angle(
shift
);
my
$pb
= level_angle(
shift
);
my
@fills
= ();
if
(in_front(
$pa
))
{
if
(in_front(
$pb
))
{
if
(
$pa
>=
$pb
)
{
my
(
$x
,
$y
) = cartesian(
$self
->{w}/2,
$pa
,
$self
->{xc},
$self
->{yc},
$self
->{h}/
$self
->{w}
);
push
@fills
, [
$x
- 1,
$y
]
if
$x
- 1 >
$self
->{xc} -
$self
->{w}/2;
$pa
= level_angle(-
$ANGLE_OFFSET
);
}
}
else
{
$pb
=
$ANGLE_OFFSET
;
}
}
else
{
if
(in_front(
$pb
))
{
$pa
=
$ANGLE_OFFSET
- 180;
}
elsif
(
$pa
>=
$pb
&& (
$pa
< 0 ||
$pb
> 0)
or
$pa
< 0 &&
$pb
> 0
)
{
$pa
=
$ANGLE_OFFSET
- 180;
$pb
=
$ANGLE_OFFSET
;
}
else
{
return
;
}
}
my
(
$x
,
$y
) = cartesian(
$self
->{w}/2, (
$pa
+
$pb
)/2,
$self
->{xc},
$self
->{yc},
$self
->{h}/
$self
->{w}
);
push
@fills
, [
$x
,
$y
];
return
@fills
;
}
sub
in_front
{
my
$a
= level_angle(
shift
);
return
$a
> (
$ANGLE_OFFSET
- 180 + 0.00000001) &&
$a
<
$ANGLE_OFFSET
- 0.000000001;
}
sub
level_angle
{
my
$a
=
shift
;
return
level_angle(
$a
-360)
if
(
$a
> 180 );
return
level_angle(
$a
+360)
if
(
$a
<= -180 );
return
$a
;
}
sub
put_slice_label
{
my
$self
=
shift
;
my
(
$x
,
$y
,
$label
) =
@_
;
return
unless
defined
$label
;
$self
->{gdta_value}->set_text(
$label
);
$self
->{gdta_value}->draw(
$x
,
$y
);
}
sub
cartesian
{
my
(
$r
,
$phi
,
$xi
,
$yi
,
$cr
) =
@_
;
return
map
_round(
$_
), (
$xi
+
$r
*
cos
(PI * (
$phi
+
$ANGLE_OFFSET
)/180),
$yi
+
$cr
*
$r
*
sin
(PI * (
$phi
+
$ANGLE_OFFSET
)/180)
)
}
"Just another true value"
;