<?php /* -*- C++ -*- */
/*
** $Id$
** $Source$
**
** PHP Class for creating bar charts using the GD library functions.
**
** Authors: Bjørn Borud, <borud@guardian.no>
*/

class barchart {
	 /* minimum allowed maximum */
	 var $min_allowed_max;

	 /* Image data */
	 var $im;
	 var $width, $height;
	 var $white, $black, $gray;
	 var $data;
	 var $colors;
	 var $legends;

	 /* margins for drawing area */
	 var $left   = 60;
	 var $right  = 200;
	 var $top    = 70;
	 var $bottom = 70;
	 var $y_tick_padding = 10;

	 /* drawing parameters */
	 var $bar_width;
	 var $bar_padding = 5; /* space between bars */
	 var $y_scale;

	 /* legends, labels and titles */
	 var $y_label = "";
	 var $title = "";

	 /* drawing area (the graph without borders etc.) */
	 var $d_width, $d_height;

	 var $y_ticks;
	 var $y_data_max;
	 var $y_bar_max;

	 var $num_bars, $bar_width, $num_parts;

	 /* default fonts */
	 var $legend_font = 2;


	 /* Compute the values for Y axis ticks */
	 function get_y_ticks ($y_max) {
		  $order_y   = floor(log10($y_max));
		  $magnitude = pow(10, $order_y);
		  $fdig      = ($y_max / $magnitude) * 10;
		  $top       = ceil($fdig) * $magnitude * 0.1;

		  /* Limit number of divisions to max 10*/
		  if ($fdig < 50) {
				$divs = 0.5;
		  } else {
				$divs = 1.0;
		  }

		  /* compute number of horizontal Y lines */

		  $tlines = $top / ($divs * $magnitude);
		  for ($i = 0; $i < $tlines; $i++) {
				$retval[] = ($i + 1) * $divs * $magnitude;
		  }

		  return($retval);
	 }


	 /* find the max sum of a bar */
	 function get_max_value ($d) {
		  $bar_count  = count($d);
		  $part_count = count($d[0]);
		  $max_value = 0;

		  for ($i = 0; $i < $bar_count; $i++) {
				$sum = 0;
				for ($j = 0; $j < $part_count; $j++) {
					 $sum += $d[$i][$j];
				}
		
				if ($sum > $max_value) {
					 $max_value = $sum;
				}
		  }
		  /* If max is too small, set it to something sane. */
		  if ($max_value <= $this->min_allowed_max) {
				$max_value = $this->min_allowed_max;
		  }

		  return($max_value);
	 }


	 function init($x, $y, $d, $c, $l) {
		  $this->min_allowed_max = 5;
		  $this->im    = ImageCreateTrueColor($x, $y);
		  $this->width  = $x;
		  $this->height = $y;

		  $this->data = $d;
		  $this->legends = $l;

		  $this->num_bars = count($this->data);
		  $this->num_parts = count($this->data[0]);	 

		  /* drawing area */
		  $this->d_width  = $this->width - $this->right - $this->left;
		  $this->d_height = $this->height - $this->top - $this->bottom;

		  $this->y_data_max = $this->get_max_value($this->data);
		  $this->y_ticks    = $this->get_y_ticks($this->y_data_max);
		  $this->y_bar_max  = $this->y_ticks[count($this->y_ticks) - 1];

		  $this->bar_width = ($this->d_width / $this->num_bars);


		  /* if the bar width is real small then round to 1 */
		  if ($this->bar_width < 1) {
				$this->bar_width = 1;
				$this->bar_padding = 0;
		  }

		  $this->y_scale = $this->d_height / $this->y_bar_max;


		  $this->white = ImageColorAllocate($this->im, 255, 255, 255);
		  $this->black = ImageColorAllocate($this->im,   0,   0,   0);
		  $this->red   = ImageColorAllocate($this->im, 190,   0,   0);
		  $this->green = ImageColorAllocate($this->im,   0, 190,   0);
		  $this->blue  = ImageColorAllocate($this->im,   0,   0, 190);
		  $this->gray  = ImageColorAllocate($this->im, 204, 204, 204);

		  $num_colors = count($c);

		  ImageAlphaBlending($this->im, false);
		  ImageFilledRectangle($this->im, 0, 0, $x, $y, $this->white);
		  ImageAlphaBlending($this->im, true);

		  for ($i = 0; $i < $num_colors; $i++) {
				if(count($c[$i])>3) {
					$this->colors[] = ImageColorResolveAlpha($this->im, 
																 $c[$i][0],
																 $c[$i][1],
																 $c[$i][2],
																 $c[$i][3]);

				} else {
					$this->colors[] = ImageColorAllocate($this->im, 
																 $c[$i][0],
																 $c[$i][1],
																 $c[$i][2]);
				}
		  }
	 }


	 /*
	 ** remap a pair of coordinates.  sort coordinates if we're
	 ** going to use them to draw a rectangle by setting the last
	 ** argument equal to 1
	 */
	 function remap_quad($x1, $y1, $x2, $y2, $rect) {
		  $tx1 = $x1 + $this->left;
		  $tx2 = $x2 + $this->left;

		  $ty1 = $this->height - $this->bottom - $y1;
		  $ty2 = $this->height - $this->bottom - $y2;

		  if ($rect == 1) {
				if ($tx1 > $tx2) { $tmp = $tx1; $tx1 = $tx2; $tx2 = $tmp;}
				if ($ty1 > $ty2) { $tmp = $ty1; $ty1 = $ty2; $ty2 = $tmp;}
		  }

		  return(array($tx1, $ty1, $tx2, $ty2));
	 }


	 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);
	 }


	 function draw_line($x1, $y1, $x2, $y2, $color) {
		  $c = $this->remap_quad($x1, $y1, $x2, $y2, 0);
		  ImageLine($this->im, $c[0], $c[1], $c[2], $c[3], $color);
	 }


	 function draw_filled_rect($x1, $y1, $x2, $y2, $color) {
		  $c = $this->remap_quad($x1, $y1, $x2, $y2, 1);

		  ImageFilledRectangle($this->im, $c[0], $c[1], 
									  $c[2], $c[3], $color);
	 }


	 function draw_string ($size, $x, $y, $s, $color) {
		  $c = $this->remap_quad($x, $y, 0, 0, 0);
		  ImageString($this->im, $size, $c[0], $c[1], $s, $color);
	 }


	 function draw_string_up($size, $x, $y, $s, $color) {
	 	$c = $this->remap_quad($x, $y, 0, 0, 0);
		ImageStringUp($this->im, $size, $c[0], $c[1], $s, $color);
	 }


	 function string_width ($s, $size) {
		  return(strlen($s) * ImageFontWidth($size));
	 }


	 function draw_title($title, $fontsize) {

		  $x = ($this->width - $this->string_width($title, $fontsize)) / 2;
		  $y = ($this->top - $this->yx[$fontsize]) / 3;

		  ImageString($this->im, $fontsize, $x, $y, $title, $this->black);
	 }


	 function draw_x_labels ($labels, $fontsize) {

		  /* how many lines of labels */
		  $n_lines = count($labels);

		  $y_offset = 10;

		  /* y position of the first row of labels */
		  $y = $this->height - $this->bottom + $y_offset;

		  for ($i = 0; $i < $n_lines; $i++) {

				/* number of "columns" */
				$n_strings = count($labels[$i]);
				$dx = $this->d_width / (double)$n_strings;
				$half_padded_dx = ($dx - $this->bar_padding) / 2.0;

				for ($j = 0; $j < $n_strings; $j++) {

					 $label_width = $this->string_width($labels[$i][$j], $fontsize);

					 $x = $this->left + ($j * $dx) + $this->bar_padding +
						  $half_padded_dx - ($label_width / 2);

					 ImageString($this->im, 
									 $fontsize, 
									 $x,
									 $y,
									 $labels[$i][$j],
									 $this->black);
				}
				$y += ImageFontHeight($fontsize) + 2;

		  }
	 }


	 function draw_axis ($axis_color, $font_color, $fontsize) {

		  $this->draw_line(0,-1,$this->d_width + 10 ,-1, $axis_color);
		  $this->draw_line(0,0,0,$this->d_height + 10, $axis_color);

		  $num_y_lines = count($this->y_ticks);

		  /* draw the zero */
		  $this->draw_string($fontsize,
									- (ImageFontWidth($fontsize)
										+ $this->y_tick_padding),
									(ImageFontHeight($fontsize) * 0.5),
									0,
									$font_color);

		  /* draw the horizontal lines and their values */
		  for ($i = 0; $i < $num_y_lines; $i++) {
				$y = $this->y_scale * $this->y_ticks[$i];
				$this->draw_line(0, $y, $this->d_width, $y, $axis_color);

				$y_tick = (int) $this->y_ticks[$i];
				$x_pos = - ($this->string_width($y_tick, $fontsize) 
								+ $this->y_tick_padding);

				$this->draw_string($fontsize, 
										 $x_pos, 
										 $y + (ImageFontHeight($fontsize) * 0.5), 
										 $y_tick, 
										 $font_color);

		  }
	 }


	 function draw_legends ($fontsize) {

		  $num_legends = count($this->legends);

		  $y1 = 0;
		  for ($i = $num_legends -1; $i >= 0; $i--) {

				$x1 = $this->d_width + 20;
				$y1 += ImageFontHeight($fontsize) * 2;
				$x2 = $this->d_width + 20 + ImageFontHeight($fontsize);
				$y2 = $y1 + ImageFontHeight($fontsize);


				$this->draw_filled_rect($x1, $y1, $x2, $y2, $this->colors[$i]);

				$this->draw_string($fontsize, 
										 $x2 + 10, 
										 $y2, 
										 $this->legends[$i],
										 $this->black);

		  }

		  /* Draw Y axis unit */
		  $yaxisunit_font = 2;
		  $ycenterpos = ($this->d_height / 2) - ($this->string_width($yaxisunit_font, $this->y_label) / 2);
		  $this->draw_string_up($yaxisunit_font, -60, $ycenterpos, $this->y_label, $this->black);

		  /* Draw graph title */
		  $title_font = 5;
		  $xcenterpos = ($this->d_width / 2) - ($this->string_width($title_font, $this->title) / 2);
		  $this->draw_string($title_font, $xcenterpos, $this->d_height + 40, $this->title, $this->black);
	 }


	 function draw_bars() {
		  for ($i = 0; $i < $this->num_bars; $i++) {

				$x1 = $i * $this->bar_width + $this->bar_padding;
				$x2 = $x1 + $this->bar_width - $this->bar_padding;

				$y1 = 0;

				for ($j = $this->num_parts-1; $j >= 0; $j--) {

					 $y2 = $y1 + $this->data[$i][$j];
					 $this->draw_filled_rect($x1,
													 $y1 * $this->y_scale,
													 $x2,
													 $y2 * $this->y_scale,
													 $this->colors[$j] );

					 $y1 += $this->data[$i][$j];
				}
		
		  }
	 }


	 function display() {
	 
		  $this->draw_axis($this->black, $this->red, 2);
		  $this->draw_legends($this->legend_font);
		  $this->draw_bars();

		  ImagePNG($this->im);
	 }


	 function destroy() {
		  if ($this->im) {
				ImageDestroy($this->im);
		  }
	 }
};
 

/*
 * Local Variables:
 * tab-width: 3
 * End:
 */
?>
