/* {{{ piechart */
/*
** This is a class for creating pie charts. Generally you just have
** to instantiate it, and then make a call to the "init" method to
** set the size and transfer the data.
**
** The data is an array of arrays that consist the following data:
** o numeric value
** o value legend
** o red \
** o green > the RGB values for the color of the slice/legend
** o blue /
**
*/
class piechart {
/* {{{ attributes */
var $im;
var $width, $height;
var $data;
var $colors;
var $angles;
var $left=10;
var $right=200;
var $top=100;
var $bottom=10;
var $head_top=10;
var $head_space=5;
var $legend_left=20;
var $center_x;
var $center_y;
var $diameter;
/* sum of values */
var $sum;
/* font sizes */
var $fx, $fy;
var $legend_num = "";
/* }}} */
/* {{{ constants */
var $PI = 3.1415926535897931;
/* }}} */
/* {{{ roundoff */
/*
** PHP has no function for rounding off doubles to the nearest
** integer so we have to roll our own.
*/
function roundoff ($v) {
if ( $v - floor($v) >= 0.5) {
return(ceil($v));
} else {
return(floor($v));
}
}
/* }}} */
/* {{{ deg2rad */
/*
** The built-in trig functions use radians and there's no
** function in PHP to convert between degrees and radians
*/
function deg2rad ($degrees) {
return (($this->PI * $degrees) / doubleval(180));
}
/* }}} */
/* {{{ get_xy_factors */
/*
** Calculate the directional vector for the sides of the
** piece of pie.
*/
function get_xy_factors ($degrees) {
$x = cos($this->deg2rad($degrees));
$y = sin($this->deg2rad($degrees));
return (array($x, $y));
}
/* }}} */
/* {{{ init */
/*
** Initialize the object and draw the pie. This would be the
** constructor in an ordinary OO scenario -- just that we haven't
** got constructors in PHP, now do we? ;-)
*/
function init ($w, $h, $d) {
$this->im = ImageCreate($w, $h);
$this->width = $w;
$this->height = $h;
$this->data = $d;
$this->da_width = ($this->width - $this->left - $this->right);
$this->da_height = ($this->height - $this->top - $this->bottom);
$this->center_x = intval($this->left + ($this->da_width / 2));
$this->center_y = intval($this->top + ($this->da_height / 2));
/* font sizes */
$this->fx = array(0, 5,6,7,8,9);
$this->fy = array(0, 7,8,10,14,11);
/* decide the diameter of the pie */
if ($this->da_height > $this->da_width) {
$this->diameter = $this->da_width;
} else {
$this->diameter = $this->da_height;
}
$this->white = ImageColorAllocate($this->im, 255, 255, 255);
$this->black = ImageColorAllocate($this->im, 0, 0, 0);
$n = count($this->data);
for ($i = 0; $i < $n; $i++) {
$this->colors[$i] = ImageColorAllocate($this->im, $this->data[$i][2],
$this->data[$i][3],
$this->data[$i][4]);
$this->sum += $this->data[$i][0];
}
$from = 0; $to = 0;
for ($i = 0; $i < $n; $i++) {
$this->angles[$i] = $this->roundoff(($this->data[$i][0] * 360)
/ doubleval($this->sum));
$to = $from + $this->angles[$i];
$col = $this->colors[$i];
$foo = $this->angles[$i];
$this->draw_slice($this->center_x,
$this->center_y,
$from,
$to,
$this->colors[$i]);
$from += $this->angles[$i];
}
}
/* }}} */
/* {{{ set_legend_percent */
/* utility function to set an attribute so we display percentages */
function set_legend_percent () {
$this->legend_num = "p";
}
/* }}} */
/* {{{ set_legend_value */
/* utility function to set an attribute so we display values */
function set_legend_value () {
$this->legend_num = "v";
}
/* }}} */
/* {{{ draw_point */
/*
** This function is just here for debugging purposes. It is
** sometimes very useful to be able to draw an X to check
** coordinates.
*/
function draw_point($x, $y) {
ImageLine($this->im, $x-4, $y-4, $x+4, $y+4, $this->black);
ImageLine($this->im, $x-4, $y+4, $x+4, $y-4, $this->black);
}
/* }}} */
/* {{{ draw_margins */
/*
** Also a debugging function to show where the margins are at
*/
function draw_margins () {
ImageLine($this->im, 0, $this->top, $this->width,
$this->top,
$this->black);
ImageLine($this->im, 0, $this->height - $this->bottom,
$this->width, $this->height - $this->bottom,
$this->black);
ImageLine($this->im, $this->left, 0, $this->left,
$this->height, $this->black);
ImageLine($this->im, $this->width - $this->right,
0, $this->width - $this->right,
$this->height, $this->black);
}
/* }}} */
/* {{{ draw_legends */
/*
** Draw legends at the right side of the pie chart. This function
** accepts a fontsize and gathers all the other information from
** the multilevel data array
*/
function draw_legends ($fontsize) {
$n = count($this->data);
$x1 = $this->width - $this->right + $this->legend_left;
$x2 = $x1 + $this->fy[$fontsize];;
for ($i = 0; $i < $n; $i++) {
/* determine Y coordinates */
$y1 = ($i * $this->fy[$fontsize] * 1.5) + $this->top;
$y2 = $y1 + $this->fy[$fontsize];
/* draw the legend color rectangle */
ImageFilledRectangle($this->im, $x1, $y1, $x2, $y2, $this->colors[$i]);
ImageRectangle($this->im, $x1, $y1, $x2, $y2, $this->black);
$legend = $this->data[$i][1];
/* decide what to show after legend */
switch ($this->legend_num) {
case "v":
$legend .= sprintf( " (%.2f)", $this->data[$i][0]);
break;
case "p":
$legend .= sprintf( " (%.2f%%)",
($this->data[$i][0]
* 100 / doubleval($this->sum)));
break;
}
ImageString($this->im, $fontsize, $x2 + 5, $y1,
$legend, $this->black);
}
}
/* }}} */
/* {{{ draw_heading */
/*
** This function accepts an array of arrays containing (in order):
** o The text of the heading as a string
** o The fontsize as an integer
** o The justification ("c"=center)
**
*/
function draw_heading($head_data) {
$n = count($head_data);
$y = $this->head_top;
for ($i = 0; $i < $n; $i++) {
switch($head_data[$i][2]) {
case "c":
$x = ($this->width - $this->fx[$head_data[$i][1]]
* strlen($head_data[$i][0])) / 2;
break;
case "r": /* uses left margin here... */
$x = $this->width - $this->left -
($this->fx[$head_data[$i][1]] * strlen($head_data[$i][0]));
break;
default:
$x = $this->left;
break;
}
ImageString($this->im, $head_data[$i][1], $x, $y, $head_data[$i][0],
$this->black);
$y += ($this->fy[$head_data[$i][1]] + $this->head_space);
}
}
/* }}} */
/* {{{ draw_slice */
/*
** This function draws a piece of pie centered at x,y starting at
** "from" degrees and ending at "to" degrees using the specified color.
*/
function draw_slice ($x, $y, $from, $to, $color) {
# Awful Kludge!!!
if ($to > 360) {
$to = 360;
}
ImageArc($this->im, $this->center_x, $this->center_y,
$this->diameter, $this->diameter, $from, $to, $color);
/* First line */
$axy2 = $this->get_xy_factors($from);
$ax2 = floor($this->center_x + ($axy2[0] * ($this->diameter /2)));
$ay2 = floor($this->center_y + ($axy2[1] * ($this->diameter /2)));
ImageLine($this->im, $this->center_x, $this->center_y, $ax2, $ay2, $color);
/* Second line */
$bxy2 = $this->get_xy_factors($to);
$bx2 = ceil($this->center_x + ($bxy2[0] * ($this->diameter /2)));
$by2 = ceil($this->center_y + ($bxy2[1] * ($this->diameter /2)));
ImageLine($this->im, $this->center_x, $this->center_y, $bx2, $by2, $color);
/* decide where to start filling, then fill */
$xy2 = $this->get_xy_factors((($to - $from) / 2) + $from);
$x2 = floor($this->center_x + ($xy2[0] * ($this->diameter /3)));
$y2 = floor($this->center_y + ($xy2[1] * ($this->diameter /3)));
ImageFillToBorder($this->im, $x2, $y2, $color, $color);
}
/* }}} */
/* {{{ display */
/*
** Make sure the legends are drawn, then output the image to the
** client
*/
function display() {
$this->draw_legends(2);
# $this->draw_margins();
ImageGif($this->im, "/tmp/pie.gif");
Header( "Content-type: image/gif");
ImageGif($this->im);
}
/* }}} */
};
/* }}} */
/* {{{ Test code */
$vals = array(
array(100, "First value", 190, 0 ,0),
array(100, "Second value", 0, 190, 0),
array(100, "Third value", 0, 0 ,190),
array(100, "Fourth value", 0, 190 ,190),
array(301.2437, "Fifth value", 204, 0 ,204),
array(308, "Sixth value", 204,204,0)
);
$heads = array(
array( "First line (centered)", 4, "c"),
array( "Second line (left justified)", 4, "l"),
array( "Third line (right justified)", 4, "r")
);
$pie = new piechart;
$pie->init(400, 300, $vals);
$pie->draw_heading($heads);
$pie->set_legend_percent();
$pie->display();
/* }}} */
/*
* Local Variables:
* tab-width: 3
* End: */
?>