array(), 1 => array()); /** * The names of the axes * @var array */ private $axis = array(0 => 'X Axis', 1 => 'Y Axis'); /** * Number of ticks to display on the axes * @var integer */ private $ticks = 10; // ################################################################### /** * Does the actual graphing and returns a byte stream of a PNG image * * @return string Byte stream */ public function graph() { $colors = $this->_primeColors(); $this->_paintCanvas(); // draw the axes $originx = self::PADDING + imagefontwidth(1) + self::SPACING + imagefontwidth(3) + self::PADDING; $originy = $this->dimensions['height'] - (self::PADDING + imagefontheight(1) + self::SPACING + imagefontheight(3) + self::SPACING); $endx = $this->dimensions['width'] - self::PADDING - 150 - self::PADDING; $endy = 40; $length = $endx - $originx; $height = $originy - $endy; imageline($this->image, $originx, $originy, $endx, $originy, $colors['grey']); imageline($this->image, $originx, $originy, $originx, $endy, $colors['grey']); // just to give us some padding $this->ticks++; // calculates the standard deviation of the two piles to determine the x and y intervals $xmin = min($this->piles[0]); $xmax = max($this->piles[0]); $xint = round(($xmax - $xmin) / $this->ticks); $xmin = ($xmin - $xint < 0 ? 0 : $xmin - $xint); $xmax = $xmax + $xint; $ymin = min($this->piles[1]); $ymax = max($this->piles[1]); $yint = round(($ymax - $ymin) / $this->ticks); $ymin = ($ymin - $yint < 0 ? 0 : $ymin - $yint); $ymax = $ymax + $yint; // label the axes imagestring($this->image, 3, $length / 2, $this->dimensions['height'] - self::SPACING - imagefontheight(3), $this->axis[0], $colors['black']); imagestringup($this->image, 3, self::SPACING, $height / 2 + $endy, $this->axis[1], $colors['black']); // score the axes $count = 0; for ($i = $originx; $i <= $endx; $i += ($length / $this->ticks)) { imageline($this->image, $i - self::SPACING, $originy + self::SPACING, $i + self::SPACING, $originy - self::SPACING, $colors['grey']); imagestring($this->image, 1, $i, $originy + self::PADDING, round($count), $colors['black']); $count += $xint; } $count = 0; for ($i = $originy; $i >= $endy; $i -= ($height / $this->ticks)) { imageline($this->image, $originx, $i, $endx, $i, $colors['grey']); imagestring($this->image, 1, self::SPACING + self::SPACING + self::PADDING + self::SPACING, $i - self::SPACING, round($count), $colors['black']); $count += $yint; } // go through and plot each dataset foreach ($this->dataset AS $data) { // plot each point and connect the dots $oldpoint = null; foreach ($data[1] AS $points) { $xcord = $originx + ($points[0] * ($length / $xmax)); $ycord = $originy - ($points[1] * ($height / $ymax)); imagefilledellipse($this->image, $xcord, $ycord, 5, 5, $data[2]); if ($oldpoint) { imageline($this->image, $xcord, $ycord, $oldpoint[0], $oldpoint[1], $data[2]); } $oldpoint = array($xcord, $ycord); } } return $this->_imageFlush(); } // ################################################################### /** * Adds a "line" with a given name and a set of datapoints in the form * array(x, y) * * @param string The line's name * @param array Array of array(x,y) as data points */ public function addDataSet($name, $points) { $this->_addPoints($points); $this->_sortPoints($points); $this->dataset[] = array($name, $points, $this->_fetchColor()); } // ################################################################### /** * This does the same thing as addDataSet(), except the client code * can specify the color in the form of array(R, G, B) * * @param string The line's name * @param array Array of array(x,y) as data points * @param array A color in the form of 3 RGB points */ public function addDataSetColor($name, $points, $color) { $this->_addPoints($points); $this->_sortPoints($points); $this->dataset[] = array($name, $points, imagecolorallocate($this->image, $color[0], $color[1], $color[2])); } // ################################################################### /** * Adds a set of points to the piles and ensures that they are all valid * * @param array Points to add */ private function _addPoints($points) { $xpairs = array(); foreach ($points AS $point) { if (isset($xpairs["$point[0]"])) { trigger_error('You cannot have more than one of the same x-values for a given data set'); } $xpairs["$point[0]"] = $point[0]; $this->piles[0][] = $point[0]; $this->piles[1][] = $point[1]; } } // ################################################################### /** * Sorts an array of points using quick sort so they're in x-increasing * order * * @param array Array of points */ private function _sortPoints(&$points) { $this->_quickSortPoints($points, 0, sizeof($points) - 1); } // ################################################################### /** * Quicksort function for sorting function * * @param array Array of points * @param integer Lower bound * @param integer Upper bound */ private function _quickSortPoints(&$points, $low, $high) { if (($high - $low) > 1) { $partition = $this->_partitionPoints($points, $low, $high); $this->_quickSortPoints($points, $low, $partition); $this->_quickSortPoints($points, $partition + 1, $high); } } // ################################################################### /** * Quicksort partitioner: returns the index of the pivot element where * all x-coords on the left side of pivot are less than or equal to * pivot, and all x-coords are higher to the right * * @param array Array of points * @param integer Lower bound * @param integer Upper bound * * @return integer Pivot index */ private function _partitionPoints(&$points, $low, $high) { $pivot = $low; for ($unsorted = $low + 1; $unsorted <= $high; $unsorted++) { if ($points[$unsorted][0] < $points[$pivot][0]) { $temp = $points[$pivot]; $points[$pivot] = $points[$unsorted]; $points[$unsorted] = $points[$pivot + 1]; $points[$pivot + 1] = $temp; $pivot++; } } return $pivot; } // ################################################################### /** * Returns the unbiased statistical standard deviation of an array of * values * * @param array Array of values * * @return float Standard deviation (unbiased) */ private function _standardDeviation($vals) { $average = $this->_arrayAverage($vals); $popVariance = array(); foreach ($vals AS $val) { $popVariance[] = pow($val - $average, 2); } return sqrt($this->_arrayAverage($popVariance)); } // ################################################################### /** * Returns the stastical mean of an array of values * * @param array Array of values * * @return float Statistical mean */ private function _arrayAverage($vals) { return array_sum($vals) / count($vals); } // ################################################################### /** * Sets the titles of the two axes * * @param string X-axis name * @param string Y-axis name */ public function setAxes($xaxis, $yaxis) { $this->axis[0] = $xaxis; $this->axis[1] = $yaxis; } } /*=====================================================================* || ################################################################### || # $HeadURL$ || # $Id$ || ################################################################### \*=====================================================================*/ ?>