+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=212
+ * @abstract
+ */
+class Image_Canvas_GD extends Image_Canvas_WithMap
+{
+
+ /**
+ * The canvas of the graph
+ * @var resource
+ * @access private
+ */
+ var $_canvas;
+
+ /**
+ * The canvas to use for tiled filling
+ * @var resource
+ * @access private
+ */
+ var $_tileImage = null;
+
+ /**
+ * Is version GD2 installed?
+ * @var bool
+ * @access private
+ */
+ var $_gd2 = true;
+
+ /**
+ * Antialiasing?
+ *
+ * Possible values 'off', 'driver' and 'native'
+ *
+ * @var string
+ * @access private
+ */
+ var $_antialias = 'off';
+
+ var $_alpha = false;
+
+ var $_clipping = array();
+
+ /**
+ * Create the GD canvas.
+ *
+ * Parameters available:
+ *
+ * 'width' The width of the graph on the canvas
+ *
+ * 'height' The height of the graph on the canvas
+ *
+ * 'left' The left offset of the graph on the canvas
+ *
+ * 'top' The top offset of the graph on the canvas
+ *
+ * 'antialias' = 'native' enables native GD antialiasing - this
+ * method has no severe impact on performance (approx +5%). Requires PHP
+ * 4.3.2 (with bundled GD2)
+ *
+ * 'antialias' = {true|'driver'} Image_Graph implemented method. This method
+ * has a severe impact on performance, drawing an antialiased line this
+ * way is about XX times slower, with an overall performance impact of
+ * about +40%. The justification for this method is that if native support
+ * is not available this can be used, it is also a future feature that this
+ * method for antialiasing will support line styles.
+ *
+ * Use antialiased for best results with a line/area chart having just a few
+ * datapoints. Native antialiasing does not provide a good appearance with
+ * short lines, as for example with smoothed charts. Antialiasing does not
+ * (currently) work with linestyles, neither native nor driver method!
+ *
+ * 'noalpha' = true If alpha blending is to be disabled
+ *
+ * 'filename' An image to open, on which the graph is created on
+ *
+ * 'gd' A GD resource to add the image to, use this option to continue
+ * working on an already existing GD resource. Make sure this is passed 'by-
+ * reference' (using &)
+ *
+ * 'usemap' Initialize an image map
+ *
+ * 'gd' and 'filename' are mutually exclusive with 'gd' as preference
+ *
+ * 'width' and 'height' are required unless 'filename' or 'gd' are
+ * specified, in which case the width and height are taken as the actual
+ * image width/height. If the latter is the case and 'left' and/or 'top' was
+ * also specified, the actual 'width'/'height' are altered so that the graph
+ * fits inside the canvas (i.e 'height' = actual height - top, etc.)
+ *
+ * @param array $param Parameter array
+ */
+ function Image_Canvas_GD($param)
+ {
+ include_once 'Image/Canvas/Color.php';
+
+ parent::Image_Canvas_WithMap($param);
+
+ $this->_gd2 = ($this->_version() == 2);
+ $this->_font = array('font' => 1, 'color' => 'black');
+
+ if ((isset($param['gd'])) && (is_resource($param['gd']))) {
+ $this->_canvas =& $param['gd'];
+ } elseif (isset($param['filename'])) {
+ $this->_canvas =& $this->_getGD($param['filename']);
+ } else {
+ if ($this->_gd2) {
+ $this->_canvas = ImageCreateTrueColor(
+ $this->_width,
+ $this->_height
+ );
+ if ((!isset($param['noalpha'])) || ($param['noalpha'] !== true)) {
+ ImageAlphaBlending($this->_canvas, true);
+ $this->_alpha = true;
+ }
+ } else {
+ $this->_canvas = ImageCreate($this->_width, $this->_height);
+ }
+ }
+
+ if (isset($param['antialias'])) {
+ $this->_antialias = $param['antialias'];
+ }
+
+ if ($this->_antialias === true) {
+ $this->_antialias = 'driver';
+ }
+
+ if (($this->_gd2) && ($this->_antialias === 'native') && (function_exists('ImageAntialias'))) {
+ ImageAntialias($this->_canvas, true);
+ }
+ }
+
+ /**
+ * Get an GD image resource from a file
+ *
+ * @param string $filename
+ * @return mixed The GD image resource
+ * @access private
+ */
+ function &_getGD($filename)
+ {
+ $info = getimagesize($filename);
+
+ $result = null;
+ switch($info[2]) {
+ case IMG_PNG:
+ $result =& ImageCreateFromPNG($filename);
+ break;
+
+ case IMG_JPG:
+ $result =& ImageCreateFromJPEG($filename);
+ break;
+
+ case IMG_GIF:
+ $result =& ImageCreateFromGIF($filename);
+ break;
+ }
+ return $result;
+ }
+
+ /**
+ * Get the color index for the RGB color
+ *
+ * @param int $color The color
+ * @return int The GD image index of the color
+ * @access private
+ */
+ function _color($color = false)
+ {
+ if (($color === false) || ($color === 'opague') || ($color === 'transparent')) {
+ return ImageColorTransparent($this->_canvas);
+ } else {
+ return Image_Canvas_Color::allocateColor($this->_canvas, $color);
+ }
+ }
+
+ /**
+ * Get the GD applicable linestyle
+ *
+ * @param mixed $lineStyle The line style to return, false if the one
+ * explicitly set
+ * @return mixed A GD compatible linestyle
+ * @access private
+ */
+ function _getLineStyle($lineStyle = false)
+ {
+ if ($this->_gd2) {
+ ImageSetThickness($this->_canvas, $this->_thickness);
+ }
+
+ if ($lineStyle == 'transparent') {
+ return false;
+ } elseif ($lineStyle === false) {
+ if (is_array($this->_lineStyle)) {
+ $colors = array();
+ foreach ($this->_lineStyle as $color) {
+ if ($color === 'transparent') {
+ $color = false;
+ }
+ $colors[] = $this->_color($color);
+ }
+ ImageSetStyle($this->_canvas, $colors);
+ return IMG_COLOR_STYLED;
+ } else {
+ return $this->_color($this->_lineStyle);
+ }
+ } else {
+ return $this->_color($lineStyle);
+ }
+ }
+
+ /**
+ * Get the GD applicable fillstyle
+ *
+ * @param mixed $fillStyle The fillstyle to return, false if the one
+ * explicitly set
+ * @return mixed A GD compatible fillstyle
+ * @access private
+ */
+ function _getFillStyle($fillStyle = false, $x0 = 0, $y0 = 0, $x1 = 0, $y1 = 0)
+ {
+ if ($this->_tileImage != null) {
+ ImageDestroy($this->_tileImage);
+ $this->_tileImage = null;
+ }
+ if ($fillStyle == 'transparent') {
+ return false;
+ } elseif ($fillStyle === false) {
+ if (is_resource($this->_fillStyle)) {
+ $x = min($x0, $x1);
+ $y = min($y0, $y1);
+ $w = abs($x1 - $x0) + 1;
+ $h = abs($y1 - $y0) + 1;
+ if ($this->_gd2) {
+ $this->_tileImage = ImageCreateTrueColor(
+ $this->getWidth(),
+ $this->getHeight()
+ );
+
+ ImageCopyResampled(
+ $this->_tileImage,
+ $this->_fillStyle,
+ $x,
+ $y,
+ 0,
+ 0,
+ $w,
+ $h,
+ ImageSX($this->_fillStyle),
+ ImageSY($this->_fillStyle)
+ );
+ } else {
+ $this->_tileImage = ImageCreate(
+ $this->getWidth(),
+ $this->getHeight()
+ );
+
+ ImageCopyResized(
+ $this->_tileImage,
+ $this->_fillStyle,
+ $x,
+ $y,
+ 0,
+ 0,
+ $w,
+ $h,
+ ImageSX($this->_fillStyle),
+ ImageSY($this->_fillStyle)
+ );
+ }
+ ImageSetTile($this->_canvas, $this->_tileImage);
+ return IMG_COLOR_TILED;
+ } elseif ((is_array($this->_fillStyle)) && (isset($this->_fillStyle['direction']))) {
+ $width = abs($x1 - $x0) + 1;
+ $height = abs($y1 - $y0) + 1;
+
+ switch ($this->_fillStyle['direction']) {
+ case 'horizontal':
+ $count = $width;
+ break;
+
+ case 'vertical':
+ $count = $height;
+ break;
+
+ case 'horizontal_mirror':
+ $count = $width / 2;
+ break;
+
+ case 'vertical_mirror':
+ $count = $height / 2;
+ break;
+
+ case 'diagonal_tl_br':
+ case 'diagonal_bl_tr':
+ $count = sqrt($width * $width + $height * $height);
+ break;
+
+ case 'radial':
+ $count = max($width, $height, sqrt($width * $width + $height * $height)) + 1;
+ break;
+
+ }
+
+ $count = round($count);
+
+ if ($this->_gd2) {
+ $this->_tileImage = ImageCreateTrueColor(
+ $this->getWidth(),
+ $this->getHeight()
+ );
+ } else {
+ $this->_tileImage = ImageCreate(
+ $this->getWidth(),
+ $this->getHeight()
+ );
+ }
+
+
+ $startColor = Image_Canvas_Color::color2RGB(
+ ($this->_fillStyle['direction'] == 'radial' ?
+ $this->_fillStyle['end'] :
+ $this->_fillStyle['start']
+ )
+ );
+ $endColor = Image_Canvas_Color::color2RGB(
+ ($this->_fillStyle['direction'] == 'radial' ?
+ $this->_fillStyle['start'] :
+ $this->_fillStyle['end']
+ )
+ );
+
+ $redIncrement = ($endColor[0] - $startColor[0]) / $count;
+ $greenIncrement = ($endColor[1] - $startColor[1]) / $count;
+ $blueIncrement = ($endColor[2] - $startColor[2]) / $count;
+
+ $color = false;
+ for ($i = 0; $i < $count; $i ++) {
+ unset($color);
+ if ($i == 0) {
+ $color = $startColor;
+ unset($color[3]);
+ } else {
+ $color[0] = round(($redIncrement * $i) +
+ $redIncrement + $startColor[0]);
+ $color[1] = round(($greenIncrement * $i) +
+ $greenIncrement + $startColor[1]);
+ $color[2] = round(($blueIncrement * $i) +
+ $blueIncrement + $startColor[2]);
+ }
+ $color = Image_Canvas_Color::allocateColor(
+ $this->_tileImage,
+ $color
+ );
+
+ switch ($this->_fillStyle['direction']) {
+ case 'horizontal':
+ ImageLine($this->_tileImage,
+ $x0 + $i,
+ $y0,
+ $x0 + $i,
+ $y1, $color);
+ break;
+
+ case 'vertical':
+ ImageLine($this->_tileImage,
+ $x0,
+ $y1 - $i,
+ $x1,
+ $y1 - $i, $color);
+ break;
+
+ case 'horizontal_mirror':
+ if (($x0 + $i) <= ($x1 - $i)) {
+ ImageLine($this->_tileImage,
+ $x0 + $i,
+ $y0,
+ $x0 + $i,
+ $y1, $color);
+
+ ImageLine($this->_tileImage,
+ $x1 - $i,
+ $y0,
+ $x1 - $i,
+ $y1, $color);
+ }
+ break;
+
+ case 'vertical_mirror':
+ if (($y0 + $i) <= ($y1 - $i)) {
+ ImageLine($this->_tileImage,
+ $x0,
+ $y0 + $i,
+ $x1,
+ $y0 + $i, $color);
+ ImageLine($this->_tileImage,
+ $x0,
+ $y1 - $i,
+ $x1,
+ $y1 - $i, $color);
+ }
+ break;
+
+ case 'diagonal_tl_br':
+ if (($i > $width) && ($i > $height)) {
+ $polygon = array (
+ $x1, $y0 + $i - $width - 1,
+ $x1, $y1,
+ $x0 + $i - $height - 1, $y1);
+ } elseif ($i > $width) {
+ $polygon = array (
+ $x0, $y0 + $i,
+ $x0, $y1,
+ $x1, $y1,
+ $x1, $y0 + $i - $width - 1);
+ } elseif ($i > $height) {
+ $polygon = array (
+ $x0 + $i - $height - 1, $y1,
+ $x1, $y1,
+ $x1, $y0,
+ $x0 + $i, $y0);
+ } else {
+ $polygon = array (
+ $x0, $y0 + $i,
+ $x0, $y1,
+ $x1, $y1,
+ $x1, $y0,
+ $x0 + $i, $y0);
+ }
+ ImageFilledPolygon(
+ $this->_tileImage,
+ $polygon,
+ count($polygon) / 2,
+ $color
+ );
+ break;
+
+ case 'diagonal_bl_tr':
+ if (($i > $width) && ($i > $height)) {
+ $polygon = array (
+ $x1, $y1 - $i + $width - 1,
+ $x1, $y0,
+ $x0 + $i - $height - 1, $y0);
+ } elseif ($i > $width) {
+ $polygon = array (
+ $x0, $y1 - $i,
+ $x0, $y0,
+ $x1, $y0,
+ $x1, $y1 - $i + $width - 1);
+ } elseif ($i > $height) {
+ $polygon = array (
+ $x0 + $i - $height - 1, $y0,
+ $x1, $y0,
+ $x1, $y1,
+ $x0 + $i, $y1);
+ } else {
+ $polygon = array (
+ $x0, $y1 - $i,
+ $x0, $y0,
+ $x1, $y0,
+ $x1, $y1,
+ $x0 + $i, $y1);
+ }
+ ImageFilledPolygon(
+ $this->_tileImage,
+ $polygon,
+ count($polygon) / 2,
+ $color
+ );
+ break;
+
+ case 'radial':
+ if (($this->_gd2) && ($i < $count)) {
+ ImageFilledEllipse(
+ $this->_tileImage,
+ $x0 + $width / 2,
+ $y0 + $height / 2,
+ $count - $i,
+ $count - $i,
+ $color
+ );
+ }
+ break;
+ }
+ }
+ ImageSetTile($this->_canvas, $this->_tileImage);
+ return IMG_COLOR_TILED;
+ } else {
+ return $this->_color($this->_fillStyle);
+ }
+ } else {
+ return $this->_color($fillStyle);
+ }
+ }
+
+ /**
+ * Sets an image that should be used for filling
+ *
+ * @param string $filename The filename of the image to fill with
+ */
+ function setFillImage($filename)
+ {
+ $this->_fillStyle =& $this->_getGD($filename);
+ }
+
+ /**
+ * Sets the font options.
+ *
+ * The $font array may have the following entries:
+ *
+ * 'ttf' = the .ttf file (either the basename, filename or full path)
+ * If 'ttf' is specified, then the following can be specified
+ *
+ * 'size' = size in pixels
+ *
+ * 'angle' = the angle with which to write the text
+ *
+ * @param array $font The font options.
+ */
+ function setFont($fontOptions)
+ {
+ parent::setFont($fontOptions);
+
+ if (isset($this->_font['ttf'])) {
+ $this->_font['file'] = str_replace('\\', '/', Image_Canvas_Tool::fontMap($this->_font['ttf']));
+ } elseif (!isset($this->_font['font'])) {
+ $this->_font['font'] = 1;
+ }
+
+ if (!isset($this->_font['color'])) {
+ $this->_font['color'] = 'black';
+ }
+
+ if ((isset($this->_font['angle'])) && ($this->_font['angle'] === false)) {
+ $this->_font['angle'] = 0;
+ }
+ }
+
+ /**
+ * Calculate pixels on a line
+ *
+ * @param int $x0 X start point
+ * @param int $y0 X start point
+ * @param int $x1 X end point
+ * @param int $y1 Y end point
+ * @return array An associated array of x,y points with all pixels on the
+ * line
+ * @access private
+ */
+ function &_linePixels($x0, $y0, $x1, $y1)
+ {
+ $pixels = array();
+ if (abs($x0 - $x1) > abs($y0 - $y1)) {
+ if ($x1 != $x0) {
+ $m = ($y1 - $y0) / ($x1 - $x0);
+ } else {
+ $m = 0;
+ }
+ $b = $y0 - $m * $x0;
+ $strx = min($x0, $x1);
+ $endx = max($x0, $x1);
+ for ($x = $strx; $x <= $endx; $x++) {
+ $pixels[] = array('X' => $x, 'Y' => ($m * $x + $b));
+ }
+ } else {
+ if ($y1 != $y0) {
+ $m = ($x1 - $x0) / ($y1 - $y0);
+ } else {
+ $m = 0;
+ }
+ $b = $x0 - $m * $y0;
+ $stry = min($y0, $y1);
+ $endy = max($y0, $y1);
+ for ($y = $stry; $y <= $endy; $y++) {
+ $pixels[] = array('X' => ($m * $y + $b), 'Y' => $y);
+ }
+ }
+ return $pixels;
+ }
+
+ /**
+ * Draws an antialiased line
+ *
+ * @param int $x0 X start point
+ * @param int $y0 X start point
+ * @param int $x1 X end point
+ * @param int $y1 Y end point
+ * @param mixed $color The line color, can be omitted
+ * @access private
+ */
+ function _antialiasedLine($x0, $y0, $x1, $y1, $color = false)
+ {
+ if (($line = $this->_getLineStyle($color)) !== false) {
+ if ($line >= 0) {
+ $line = ImageColorsForIndex($this->_canvas, $line);
+ $pixels = &$this->_linePixels($x0, $y0, $x1, $y1);
+ foreach ($pixels as $point) {
+ $this->_antialiasedPixel($point['X'], $point['Y'], $line);
+ }
+ unset($pixels);
+ }
+ }
+ }
+
+
+ /**
+ * Draws an antialiased pixel
+ *
+ * @param int $x X point
+ * @param int $y Y point
+ * @param mixed $color The pixel color
+ * @access private
+ */
+ function _antialiasedPixel($x, $y, $color)
+ {
+ $fx = floor($x);
+ $fy = floor($y);
+ $cx = ceil($x);
+ $cy = ceil($y);
+ $xa = $x - $fx;
+ $xb = $cx - $x;
+ $ya = $y - $fy;
+ $yb = $cy - $y;
+ if (($cx == $fx) && ($cy == $fy)) {
+ $this->_antialisedSubPixel($fx, $fy, 0.0, 1.0, $color);
+ } else {
+ $this->_antialisedSubPixel($fx, $fy, $xa + $ya, $xb + $yb, $color);
+ if ($cy != $fy) {
+ $this->_antialisedSubPixel($fx, $cy, $xa + $yb, $xb + $ya, $color);
+ }
+ if ($cx != $fx) {
+ $this->_antialisedSubPixel($cx, $fy, $xb + $ya, $xa + $yb, $color);
+ if ($cy != $fy) {
+ $this->_antialisedSubPixel($cx, $cy, $xb + $yb, $xa + $ya, $color);
+ }
+ }
+ }
+ }
+
+ /**
+ * Antialias'es the pixel around x,y with weights a,b
+ *
+ * @param int $x X point
+ * @param int $y Y point
+ * @param int $a The weight of the current color
+ * @param int $b The weight of the applied/wanted color
+ * @param mixed $color The pixel color
+ * @access private
+ */
+ function _antialisedSubPixel($x, $y, $a, $b, $color)
+ {
+ $x = $this->_getX($x);
+ $y = $this->_getX($y);
+ if (($x >=0 ) && ($y >= 0) && ($x < $this->getWidth()) && ($y < $this->getHeight())) {
+ $tempColor = ImageColorsForIndex($this->_canvas, ImageColorAt($this->_canvas, $x, $y));
+
+ $newColor[0] = min(255, round($tempColor['red'] * $a + $color['red'] * $b));
+ $newColor[1] = min(255, round($tempColor['green'] * $a + $color['green'] * $b));
+ $newColor[2] = min(255, round($tempColor['blue'] * $a + $color['blue'] * $b));
+ //$newColor[3] = 0;
+ $color = '#';
+ foreach ($newColor as $acolor) {
+ $color .= sprintf('%02s', dechex($acolor));
+ }
+ $newColor = $this->_color($color);//,'rgb(' . $newColor[0] . ',' . $newColor[1] . ',' . $newColor[2] .')';
+
+ ImageSetPixel($this->_canvas, $x, $y, $newColor);
+ }
+ }
+
+
+ /**
+ * Draw a line end
+ *
+ * Parameter array:
+ *
+ * 'x': int X point
+ *
+ * 'y': int Y point
+ *
+ * 'end': string The end type of the end
+ *
+ * 'size': int The size of the end
+ *
+ * 'color': string The color of the end
+ *
+ * 'angle': int [optional] The angle with which to draw the end
+ *
+ * @param array $params Parameter array
+ */
+ function drawEnd($params)
+ {
+ $x = $this->_getX($params['x']);
+ $y = $this->_getY($params['y']);
+ $size = $params['size'];
+ //var_dump($params);
+ $angle = deg2rad((isset($params['angle']) ? $params['angle'] : 0));
+ $pi2 = pi() / 2;
+ switch ($params['end']) {
+ case 'lollipop':
+ case 'circle':
+ $this->ellipse(
+ array(
+ 'x' => $x,
+ 'y' => $y,
+ 'rx' => $size / 2,
+ 'ry' => $size / 2,
+ 'fill' => $params['color'],
+ 'line' => $params['color']
+ )
+ );
+ break;
+ case 'diamond':
+ $x0 = round($params['x'] + cos($angle) * $size * 0.65);
+ $y0 = round($params['y'] - sin($angle) * $size * 0.65);
+ $shape = array(
+ $x0 + round(cos($angle) * $size * 0.65),
+ $y0 - round(sin($angle) * $size * 0.65),
+ $x0 + round(cos($angle + $pi2) * $size * 0.65),
+ $y0 - round(sin($angle + $pi2) * $size * 0.65),
+ $x0 + round(cos($angle + pi()) * $size * 0.65),
+ $y0 - round(sin($angle + pi()) * $size * 0.65),
+ $x0 + round(cos($angle + 3 * $pi2) * $size * 0.65),
+ $y0 - round(sin($angle + 3 * $pi2) * $size * 0.65)
+ );
+ break;
+ case 'line':
+ $this->line(
+ array(
+ 'x0' => $x + round(cos($angle + $pi2) * $size / 2),
+ 'y0' => $y - round(sin($angle + $pi2) * $size / 2),
+ 'x1' => $x + round(cos($angle + 3 * $pi2) * $size / 2),
+ 'y1' => $y - round(sin($angle + 3 * $pi2) * $size / 2),
+ 'color' => $params['color']
+ )
+ );
+ break;
+ case 'box':
+ case 'rectangle':
+ $x0 = round($params['x'] + cos($angle) * $size / 2);
+ $y0 = round($params['y'] - sin($angle) * $size / 2);
+ $pi4 = pi() / 4;
+ $shape = array(
+ $x0 + round(cos($angle + $pi4) * $size / 2),
+ $y0 - round(sin($angle + $pi4) * $size / 2),
+ $x0 + round(cos($angle + $pi2 + $pi4) * $size / 2),
+ $y0 - round(sin($angle + $pi2 + $pi4) * $size / 2),
+ $x0 + round(cos($angle + pi() + $pi4) * $size / 2),
+ $y0 - round(sin($angle + pi() + $pi4) * $size / 2),
+ $x0 + round(cos($angle + 3 * $pi2 + $pi4) * $size / 2),
+ $y0 - round(sin($angle + 3 * $pi2 + $pi4) * $size / 2)
+ );
+ break;
+ case 'arrow':
+ $shape = array(
+ $x + cos($angle) * $size,
+ $y - sin($angle) * $size,
+ $x + cos($angle + $pi2) * $size * 0.4,
+ $y - sin($angle + $pi2) * $size * 0.4,
+ $x + cos($angle + 3 * $pi2) * $size * 0.4,
+ $y - sin($angle + 3 * $pi2) * $size * 0.4,
+ );
+ break;
+ case 'arrow2':
+ $shape = array(
+ $x + round(cos($angle) * $size),
+ $y - round(sin($angle) * $size),
+ $x + round(cos($angle + $pi2 + deg2rad(45)) * $size),
+ $y - round(sin($angle + $pi2 + deg2rad(45)) * $size),
+ $x,
+ $y,
+ $x + round(cos($angle + 3 * $pi2 - deg2rad(45)) * $size),
+ $y - round(sin($angle + 3 * $pi2 - deg2rad(45)) * $size),
+ );
+ break;
+ }
+
+ if (isset($shape)) {
+ // output the shape
+ if (($fill = $this->_getFillStyle($params['color'])) !== false) {
+ ImageFilledPolygon($this->_canvas, $shape, count($shape)/2, $fill);
+ }
+ }
+ parent::drawEnd($params);
+ }
+
+ /**
+ * Draw a line
+ *
+ * Parameter array:
+ *
+ * 'x0': int X start point
+ *
+ * 'y0': int Y start point
+ *
+ * 'x1': int X end point
+ *
+ * 'y1': int Y end point
+ *
+ * 'color': mixed [optional] The line color
+ *
+ * @param array $params Parameter array
+ */
+ function line($params)
+ {
+ $x0 = $this->_getX($params['x0']);
+ $y0 = $this->_getY($params['y0']);
+ $x1 = $this->_getX($params['x1']);
+ $y1 = $this->_getY($params['y1']);
+ $color = (isset($params['color']) ? $params['color'] : false);
+
+ $x0 = $this->_getX($x0);
+ $y0 = $this->_getY($y0);
+ $x1 = $this->_getX($x1);
+ $y1 = $this->_getY($y1);
+ if (($this->_antialias === 'driver') && ($x0 != $x1) && ($y0 != $y1)) {
+ $this->_antialiasedLine($x0, $y0, $x1, $y1, $color);
+ } elseif (($line = $this->_getLineStyle($color)) !== false) {
+ ImageLine($this->_canvas, $x0, $y0, $x1, $y1, $line);
+ }
+ parent::line($params);
+ }
+
+ /**
+ * Parameter array:
+ *
+ * 'connect': bool [optional] Specifies whether the start point should be
+ * connected to the endpoint (closed polygon) or not (connected line)
+ *
+ * 'fill': mixed [optional] The fill color
+ *
+ * 'line': mixed [optional] The line color
+ * @param array $params Parameter array
+ */
+ function polygon($params)
+ {
+ include_once 'Image/Canvas/Tool.php';
+
+ $connectEnds = (isset($params['connect']) ? $params['connect'] : false);
+ $fillColor = (isset($params['fill']) ? $params['fill'] : false);
+ $lineColor = (isset($params['line']) ? $params['line'] : false);
+
+ if (!$connectEnds) {
+ $fillColor = 'transparent';
+ }
+ $style = $this->_getLineStyle($lineColor) . $this->_getFillStyle($fillColor);
+
+ $lastPoint = false;
+ foreach ($this->_polygon as $point) {
+ if (($lastPoint) && (isset($lastPoint['P1X'])) &&
+ (isset($lastPoint['P1Y'])) && (isset($lastPoint['P2X'])) &&
+ (isset($lastPoint['P2Y'])))
+ {
+ $dx = abs($point['X'] - $lastPoint['X']);
+ $dy = abs($point['Y'] - $lastPoint['Y']);
+ $d = sqrt($dx * $dx + $dy * $dy);
+ if ($d > 0) {
+ $interval = 1 / $d;
+ for ($t = 0; $t <= 1; $t = $t + $interval) {
+ $x = Image_Canvas_Tool::bezier(
+ $t,
+ $lastPoint['X'],
+ $lastPoint['P1X'],
+ $lastPoint['P2X'],
+ $point['X']
+ );
+
+ $y = Image_Canvas_Tool::bezier(
+ $t,
+ $lastPoint['Y'],
+ $lastPoint['P1Y'],
+ $lastPoint['P2Y'],
+ $point['Y']
+ );
+
+ if (!isset($low['X'])) {
+ $low['X'] = $x;
+ } else {
+ $low['X'] = min($x, $low['X']);
+ }
+ if (!isset($high['X'])) {
+ $high['X'] = $x;
+ } else {
+ $high['X'] = max($x, $high['X']);
+ }
+ if (!isset($low['Y'])) {
+ $low['Y'] = $y;
+ } else {
+ $low['Y'] = min($y, $low['Y']);
+ }
+ if (!isset($high['Y'])) {
+ $high['Y'] = $y;
+ } else {
+ $high['Y'] = max($y, $high['Y']);
+ }
+ $polygon[] = $x;
+ $polygon[] = $y;
+ }
+ if (($t - $interval) < 1) {
+ $x = Image_Canvas_Tool::bezier(
+ 1,
+ $lastPoint['X'],
+ $lastPoint['P1X'],
+ $lastPoint['P2X'],
+ $point['X']
+ );
+
+ $y = Image_Canvas_Tool::bezier(
+ 1,
+ $lastPoint['Y'],
+ $lastPoint['P1Y'],
+ $lastPoint['P2Y'],
+ $point['Y']
+ );
+
+ $polygon[] = $x;
+ $polygon[] = $y;
+ }
+ }
+ } else {
+ if (!isset($low['X'])) {
+ $low['X'] = $point['X'];
+ } else {
+ $low['X'] = min($point['X'], $low['X']);
+ }
+ if (!isset($high['X'])) {
+ $high['X'] = $point['X'];
+ } else {
+ $high['X'] = max($point['X'], $high['X']);
+ }
+ if (!isset($low['Y'])) {
+ $low['Y'] = $point['Y'];
+ } else {
+ $low['Y'] = min($point['Y'], $low['Y']);
+ }
+ if (!isset($high['Y'])) {
+ $high['Y'] = $point['Y'];
+ } else {
+ $high['Y'] = max($point['Y'], $high['Y']);
+ }
+
+ $polygon[] = $point['X'];
+ $polygon[] = $point['Y'];
+ }
+ $lastPoint = $point;
+ }
+
+ if ((isset($polygon)) && (is_array($polygon))) {
+ if ($connectEnds) {
+ if (($fill = $this->_getFillStyle($fillColor, $low['X'], $low['Y'], $high['X'], $high['Y'])) !== false) {
+ ImageFilledPolygon($this->_canvas, $polygon, count($polygon)/2, $fill);
+ }
+ if ($this->_antialias === 'driver') {
+ $pfirst = $p0 = false;
+ reset($polygon);
+
+ while (list(, $x) = each($polygon)) {
+ list(, $y) = each($polygon);
+ if ($p0 !== false) {
+ $this->_antialiasedLine($p0['X'], $p0['Y'], $x, $y, $lineColor);
+ }
+ if ($pfirst === false) {
+ $pfirst = array('X' => $x, 'Y' => $y);
+ }
+ $p0 = array('X' => $x, 'Y' => $y);;
+ }
+
+ $this->_antialiasedLine($p0['X'], $p0['Y'], $pfirst['X'], $pfirst['Y'], $lineColor);
+ } elseif (($line = $this->_getLineStyle($lineColor)) !== false) {
+ ImagePolygon($this->_canvas, $polygon, count($polygon)/2, $line);
+ }
+ } else {
+ $prev_point = false;
+ if ($this->_antialias === 'driver') {
+ reset($polygon);
+ while (list(, $x) = each($polygon)) {
+ list(, $y) = each($polygon);
+ if ($prev_point) {
+ $this->_antialiasedLine(
+ $prev_point['X'],
+ $prev_point['Y'],
+ $x,
+ $y,
+ $lineColor
+ );
+ }
+ $prev_point = array('X' => $x, 'Y' => $y);;
+ }
+ } elseif (($line = $this->_getLineStyle($lineColor)) !== false) {
+ reset($polygon);
+ while (list(, $x) = each($polygon)) {
+ list(, $y) = each($polygon);
+ if ($prev_point) {
+ ImageLine(
+ $this->_canvas,
+ $prev_point['X'],
+ $prev_point['Y'],
+ $x,
+ $y,
+ $line
+ );
+ }
+ $prev_point = array('X' => $x, 'Y' => $y);;
+ }
+ }
+ }
+ }
+
+ parent::polygon($params);
+ }
+
+ /**
+ * Draw a rectangle
+ *
+ * Parameter array:
+ *
+ * 'x0': int X start point
+ *
+ * 'y0': int Y start point
+ *
+ * 'x1': int X end point
+ *
+ * 'y1': int Y end point
+ *
+ * 'fill': mixed [optional] The fill color
+ *
+ * 'line': mixed [optional] The line color
+ *
+ * @param array $params Parameter array
+ */
+ function rectangle($params)
+ {
+ $x0 = $this->_getX($params['x0']);
+ $y0 = $this->_getY($params['y0']);
+ $x1 = $this->_getX($params['x1']);
+ $y1 = $this->_getY($params['y1']);
+ $fillColor = (isset($params['fill']) ? $params['fill'] : false);
+ $lineColor = (isset($params['line']) ? $params['line'] : false);
+
+ if (($fill = $this->_getFillStyle($fillColor, $x0, $y0, $x1, $y1)) !== false) {
+ ImageFilledRectangle($this->_canvas, $x0, $y0, $x1, $y1, $fill);
+ }
+
+ if (($line = $this->_getLineStyle($lineColor)) !== false) {
+ ImageRectangle($this->_canvas, $x0, $y0, $x1, $y1, $line);
+ }
+
+ parent::rectangle($params);
+ }
+
+ /**
+ * Draw an ellipse
+ *
+ * Parameter array:
+ *
+ * 'x': int X center point
+ *
+ * 'y': int Y center point
+ *
+ * 'rx': int X radius
+ *
+ * 'ry': int Y radius
+ *
+ * 'fill': mixed [optional] The fill color
+ *
+ * 'line': mixed [optional] The line color
+ *
+ * @param array $params Parameter array
+ */
+ function ellipse($params)
+ {
+ $x = $this->_getX($params['x']);
+ $y = $this->_getY($params['y']);
+ $rx = $this->_getX($params['rx']);
+ $ry = $this->_getY($params['ry']);
+ $fillColor = (isset($params['fill']) ? $params['fill'] : false);
+ $lineColor = (isset($params['line']) ? $params['line'] : false);
+
+ if (($fill = $this->_getFillStyle($fillColor, $x - $rx, $y - $ry, $x + $rx, $y + $ry)) !== false) {
+ ImageFilledEllipse($this->_canvas, $x, $y, $rx * 2, $ry * 2, $fill);
+ }
+
+ if (($line = $this->_getLineStyle($lineColor)) !== false) {
+ ImageEllipse($this->_canvas, $x, $y, $rx * 2, $ry * 2, $line);
+ }
+ parent::ellipse($params);
+ }
+
+ /**
+ * Draw a pie slice
+ *
+ * Parameter array:
+ *
+ * 'x': int X center point
+ *
+ * 'y': int Y center point
+ *
+ * 'rx': int X radius
+ *
+ * 'ry': int Y radius
+ *
+ * 'v1': int The starting angle (in degrees)
+ *
+ * 'v2': int The end angle (in degrees)
+ *
+ * 'srx': int [optional] Starting X-radius of the pie slice (i.e. for a doughnut)
+ *
+ * 'sry': int [optional] Starting Y-radius of the pie slice (i.e. for a doughnut)
+ *
+ * 'fill': mixed [optional] The fill color
+ *
+ * 'line': mixed [optional] The line color
+ *
+ * @param array $params Parameter array
+ */
+ function pieslice($params)
+ {
+ $x = $this->_getX($params['x']);
+ $y = $this->_getY($params['y']);
+ $rx = $params['rx'];
+ $ry = $params['ry'];
+ $v1 = $params['v1'];
+ $v2 = $params['v2'];
+ $srx = (isset($params['srx']) ? $params['srx'] : 0);
+ $sry = (isset($params['sry']) ? $params['sry'] : 0);
+ $fillColor = (isset($params['fill']) ? $params['fill'] : false);
+ $lineColor = (isset($params['line']) ? $params['line'] : false);
+
+ $dA = 0.1;
+
+ if (($srx !== false) && ($sry !== false)) {
+ $angle = max($v1, $v2);
+ while ($angle >= min($v1, $v2)) {
+ $polygon[] = ($x + $srx * cos(deg2rad($angle % 360)));
+ $polygon[] = ($y + $sry * sin(deg2rad($angle % 360)));
+ $angle -= $dA;
+ }
+ if (($angle + $dA) > min($v1, $v2)) {
+ $polygon[] = ($x + $srx * cos(deg2rad(min($v1, $v2) % 360)));
+ $polygon[] = ($y + $sry * sin(deg2rad(min($v1, $v2) % 360)));
+ }
+ } else {
+ $polygon[] = $x;
+ $polygon[] = $y;
+ }
+
+ $angle = min($v1, $v2);
+ while ($angle <= max($v1, $v2)) {
+ $polygon[] = ($x + $rx * cos(deg2rad($angle % 360)));
+ $polygon[] = ($y + $ry * sin(deg2rad($angle % 360)));
+ $angle += $dA;
+ }
+
+ if (($angle - $dA) < max($v1, $v2)) {
+ $polygon[] = ($x + $rx * cos(deg2rad(max($v1, $v2) % 360)));
+ $polygon[] = ($y + $ry * sin(deg2rad(max($v1, $v2) % 360)));
+ }
+
+ if ((($fill = $this->_getFillStyle($fillColor, $x - $rx - 1, $y - $ry - 1, $x + $rx + 1, $y + $ry + 1)) !== false) && (count($polygon) > 2)) {
+ ImageFilledPolygon($this->_canvas, $polygon, count($polygon) / 2, $fill);
+ }
+
+ if (($line = $this->_getLineStyle($lineColor)) !== false) {
+ ImagePolygon($this->_canvas, $polygon, count($polygon) / 2, $line);
+ }
+
+ parent::pieSlice($params);
+ }
+
+ /**
+ * Get the width of a text,
+ *
+ * @param string $text The text to get the width of
+ * @return int The width of the text
+ */
+ function textWidth($text)
+ {
+ if (isset($this->_font['file'])) {
+ $angle = 0;
+ if (isset($this->_font['angle'])) {
+ $angle = $this->_font['angle'];
+ }
+
+ $width = 0;
+ $lines = explode("\n", $text);
+ foreach ($lines as $line) {
+ $bounds = ImageTTFBBox(
+ $this->_font['size'],
+ $angle,
+ $this->_font['file'],
+ $text
+ );
+
+ $x0 = min($bounds[0], $bounds[2], $bounds[4], $bounds[6]);
+ $x1 = max($bounds[0], $bounds[2], $bounds[4], $bounds[6]);
+ $width = max(abs($x0 - $x1), $width);
+ }
+ return $width;
+ } else {
+ if ((isset($this->_font['vertical'])) && ($this->_font['vertical'])) {
+ return ImageFontHeight($this->_font['font']) * (substr_count($text, "\n") + 1);
+ } else {
+ $width = 0;
+ $lines = explode("\n", $text);
+ foreach ($lines as $line) {
+ $width = max($width, ImageFontWidth($this->_font['font']) * strlen($line));
+ }
+ return $width;
+ }
+ }
+ }
+
+ /**
+ * Get the height of a text.
+ *
+ * Note! This method can give some peculiar results, since ImageTTFBBox() returns the total
+ * bounding box of a text, where ImageTTF() writes the text on the baseline of the text, that
+ * is 'g', 'p', 'q' and other letters that dig under the baseline will appear to have a larger
+ * height than they actually do. Have a look at the tests/text.php test case - the first two
+ * columns, 'left and 'center', both look alright, whereas the last column, 'right', appear
+ * with a larger space between the first text and the second. This is because the total height
+ * is actually smaller by exactly the number of pixels that the 'g' digs under the baseline.
+ * Remove the 'g' from the text and they appear correct.
+ *
+ * @param string $text The text to get the height of
+ * @param bool $force Force the method to calculate the size
+ * @return int The height of the text
+ */
+ function textHeight($text, $force = false)
+ {
+ if (isset($this->_font['file'])) {
+ $angle = 0;
+ if (isset($this->_font['angle'])) {
+ $angle = $this->_font['angle'];
+ }
+
+ $linebreaks = substr_count($text, "\n");
+ if (($angle == 0) && ($force === false)) {
+ /*
+ * if the angle is 0 simply return the size, due to different
+ * heights for example for x-axis labels, making the labels
+ * _not_ appear as written on the same baseline
+ */
+ return $this->_font['size'] + ($this->_font['size'] + 2) * $linebreaks;
+ }
+
+ $height = 0;
+ $lines = explode("\n", $text);
+ foreach ($lines as $line) {
+ $bounds = ImageTTFBBox(
+ $this->_font['size'],
+ $angle,
+ $this->_font['file'],
+ $line
+ );
+
+ $y0 = min($bounds[1], $bounds[3], $bounds[5], $bounds[7]);
+ $y1 = max($bounds[1], $bounds[3], $bounds[5], $bounds[7]);
+ $height += abs($y0 - $y1);
+ }
+ return $height + $linebreaks * 2;
+ } else {
+ if ((isset($this->_font['vertical'])) && ($this->_font['vertical'])) {
+ $width = 0;
+ $lines = explode("\n", $text);
+ foreach ($lines as $line) {
+ $width = max($width, ImageFontWidth($this->_font['font']) * strlen($line));
+ }
+ return $width;
+ } else {
+ return ImageFontHeight($this->_font['font']) * (substr_count($text, "\n") + 1);
+ }
+ }
+ }
+
+ /**
+ * Calculated the absolute bottom-left position of the text, by simulating
+ * the calculation of the baseline drop.
+ * @param int $x The relative x position to write the text
+ * @param int $y The relative y position to write the text
+ * @param string $text The text to write
+ * @param array $align The alignment of the text relative to (x, y)
+ * @returns array An array containing the absolute x and y positions
+ * @access private
+ */
+ function _getAbsolutePosition($x, $y, $text, $align)
+ {
+ if ($this->_font['angle'] > 0) {
+ $w0 = $this->textWidth($text);
+ $h0 = $this->textHeight($text);
+
+ if ($align['vertical'] == 'bottom') {
+ $dy = $y - $h0;
+ }
+ else if ($align['vertical'] == 'center') {
+ $dy = $y - $h0 / 2;
+ }
+ else if ($align['vertical'] == 'top') {
+ $dy = $y;
+ }
+
+ if ($align['horizontal'] == 'right') {
+ $dx = $x - $w0;
+ }
+ else if ($align['horizontal'] == 'center') {
+ $dx = $x - $w0 / 2;
+ }
+ else if ($align['horizontal'] == 'left') {
+ $dx = $x;
+ }
+
+ if (($this->_font['angle'] < 180) && ($this->_font['angle'] >= 0)) {
+ $dy += $h0;
+ }
+ if (($this->_font['angle'] >= 90) && ($this->_font['angle'] < 270)) {
+ $dx += $w0;
+ }
+ }
+ else {
+ // get the maximum size of normal text above base line - sampled by 'Al'
+ $size1 = imagettfbbox($this->_font['size'], 0, $this->_font['file'], 'Al');
+ $height1 = abs($size1[7] - $size1[1]);
+
+ // get the maximum size of all text above base and below line - sampled by 'AlgjpqyQ'
+ $size2 = imagettfbbox($this->_font['size'], 0, $this->_font['file'], 'AlgjpqyQ');
+ $height2 = abs($size2[7] - $size2[1]);
+
+ // get the size of the text, simulating height above baseline beinh max, by sampling using 'Al'
+ $size = imagettfbbox($this->_font['size'], 0, $this->_font['file'], 'Al' . $text);
+ $height = abs($size[7] - $size[1]);
+
+ // if all text is above baseline, i.e. height of text compares to max height above (within 10%)
+ if (abs($height - $height1)/$height1 < 0.1) {
+ $dHeight = 0;
+ }
+ else {
+ $dHeight = abs($height2 - $height1);
+ }
+
+ // specifies the bottom-left corner!
+ $dx = $x + sin(deg2rad($this->_font['angle'])) * $dHeight;
+ $dy = $y - cos(deg2rad($this->_font['angle'])) * $dHeight;
+
+ if ($align['vertical'] == 'top') {
+ $dy += $height;
+ }
+ else if ($align['vertical'] == 'center') {
+ $dy += ($height + $dHeight) / 2;
+ }
+ else if ($align['vertical'] == 'bottom') {
+ $dy += $dHeight;
+ }
+
+ if ($align['horizontal'] == 'center') {
+ $factor = 0.5;
+ }
+ else if ($align['horizontal'] == 'right') {
+ $factor = 1;
+ }
+ else {
+ $factor = 0;
+ }
+
+ if ($factor != 0) {
+ $size = imagettfbbox($this->_font['size'], 0, $this->_font['file'], $text);
+ $w0 = abs($size[2] - $size[0]);
+ $dx -= cos(deg2rad($this->_font['angle'])) * $w0 * $factor;
+ $dy += sin(deg2rad($this->_font['angle'])) * $w0 * $factor;
+ }
+ }
+
+ return array('x' => $dx, 'y' => $dy);
+ }
+
+ /**
+ * Writes text
+ *
+ * Parameter array:
+ *
+ * 'x': int X-point of text
+ *
+ * 'y': int Y-point of text
+ *
+ * 'text': string The text to add
+ *
+ * 'alignment': array [optional] Alignment
+ *
+ * 'color': mixed [optional] The color of the text
+ */
+ function addText($params)
+ {
+ $x0 = $this->_getX($params['x']);
+ $y0 = $this->_getY($params['y']);
+ $text = $params['text'];
+ $color = (isset($params['color']) ? $params['color'] : false);
+ $alignment = (isset($params['alignment']) ? $params['alignment'] : false);
+
+ $text = str_replace("\r", '', $text);
+
+ if (!is_array($alignment)) {
+ $alignment = array('vertical' => 'top', 'horizontal' => 'left');
+ }
+
+ if (!isset($alignment['vertical'])) {
+ $alignment['vertical'] = 'top';
+ }
+
+ if (!isset($alignment['horizontal'])) {
+ $alignment['horizontal'] = 'left';
+ }
+
+ if (isset($this->_font['size'])) {
+ $textHeight = $this->_font['size'] + 2;
+ }
+ else {
+ $textHeight = $this->textHeight('A');
+ }
+ $lines = explode("\n", $text);
+ foreach ($lines as $line) {
+ $x = $x0;
+ $y = $y0;
+
+ $y0 += $textHeight + 2;
+
+ if (($color === false) && (isset($this->_font['color']))) {
+ $color = $this->_font['color'];
+ }
+
+ if ($color != 'transparent') {
+ if (isset($this->_font['file'])) {
+ $result = $this->_getAbsolutePosition($x, $y, $line, $alignment);
+ ImageTTFText(
+ $this->_canvas,
+ $this->_font['size'],
+ $this->_font['angle'],
+ $result['x'],
+ $result['y'],
+ $this->_color($color),
+ $this->_font['file'],
+ $line
+ );
+
+ } else {
+ $width = $this->textWidth($line);
+ $height = $this->textHeight($line);
+ if ($alignment['horizontal'] == 'right') {
+ $x -= $width;
+ }
+ else if ($alignment['horizontal'] == 'center') {
+ $x -= $width / 2;
+ }
+ if ($alignment['vertical'] == 'bottom') {
+ $y -= $height;
+ }
+ else if ($alignment['vertical'] == 'center') {
+ $y -= $height / 2;
+ }
+ if ((isset($this->_font['vertical'])) && ($this->_font['vertical'])) {
+ ImageStringUp(
+ $this->_canvas,
+ $this->_font['font'],
+ $x,
+ $y + $this->textHeight($text),
+ $line,
+ $this->_color($color)
+ );
+ } else {
+ ImageString(
+ $this->_canvas,
+ $this->_font['font'],
+ $x,
+ $y,
+ $line,
+ $this->_color($color)
+ );
+ }
+ }
+ }
+ }
+ parent::addText($params);
+ }
+
+ /**
+ * Overlay image
+ *
+ * Parameter array:
+ *
+ * 'x': int X-point of overlayed image
+ *
+ * 'y': int Y-point of overlayed image
+ *
+ * 'filename': string The filename of the image to overlay
+ *
+ * 'width': int [optional] The width of the overlayed image (resizing if possible)
+ *
+ * 'height': int [optional] The height of the overlayed image (resizing if possible)
+ *
+ * 'alignment': array [optional] Alignment
+ */
+ function image($params)
+ {
+ $x = $this->_getX($params['x']);
+ $y = $this->_getY($params['y']);
+ $filename = $params['filename'];
+ $width = (isset($params['width']) ? $params['width'] : false);
+ $height = (isset($params['height']) ? $params['height'] : false);
+ $alignment = (isset($params['alignment']) ? $params['alignment'] : false);
+
+ if (!is_array($alignment)) {
+ $alignment = array('vertical' => 'top', 'horizontal' => 'left');
+ }
+
+ if (!isset($alignment['vertical'])) {
+ $alignment['vertical'] = 'top';
+ }
+
+ if (!isset($alignment['horizontal'])) {
+ $alignment['horizontal'] = 'left';
+ }
+
+ if (file_exists($filename)) {
+ if (strtolower(substr($filename, -4)) == '.png') {
+ $image = ImageCreateFromPNG($filename);
+ } elseif (strtolower(substr($filename, -4)) == '.gif') {
+ $image = ImageCreateFromGIF($filename);
+ } else {
+ $image = ImageCreateFromJPEG($filename);
+ }
+
+ $imgWidth = ImageSX($image);
+ $imgHeight = ImageSY($image);
+
+ $outputWidth = ($width !== false ? $width : $imgWidth);
+ $outputHeight = ($height !== false ? $height : $imgHeight);
+
+ if ($alignment['horizontal'] == 'right') {
+ $x -= $outputWidth;
+ } elseif ($alignment['horizontal'] == 'center') {
+ $x -= $outputWidth / 2;
+ }
+
+ if ($alignment['vertical'] == 'bottom') {
+ $y -= $outputHeight;
+ } elseif ($alignment['vertical'] == 'center') {
+ $y -= $outputHeight / 2;
+ }
+
+ if ((($width !== false) && ($width != $imgWidth)) ||
+ (($height !== false) && ($height != $imgHeight)))
+ {
+ if ($this->_gd2) {
+ ImageCopyResampled(
+ $this->_canvas,
+ $image,
+ $x,
+ $y,
+ 0,
+ 0,
+ $width,
+ $height,
+ $imgWidth,
+ $imgHeight
+ );
+ } else {
+ ImageCopyResized(
+ $this->_canvas,
+ $image,
+ $x,
+ $y,
+ 0,
+ 0,
+ $width,
+ $height,
+ $imgWidth,
+ $imgHeight
+ );
+ }
+ } else {
+ ImageCopy(
+ $this->_canvas,
+ $image,
+ $x,
+ $y,
+ 0,
+ 0,
+ $imgWidth,
+ $imgHeight
+ );
+ }
+ ImageDestroy($image);
+ }
+ parent::image($params);
+ }
+
+ /**
+ * Set clipping to occur
+ *
+ * Parameter array:
+ *
+ * 'x0': int X point of Upper-left corner
+ * 'y0': int X point of Upper-left corner
+ * 'x1': int X point of lower-right corner
+ * 'y1': int Y point of lower-right corner
+ */
+ function setClipping($params = false)
+ {
+ if ($params === false) {
+ $index = count($this->_clipping) - 1;
+ if (isset($this->_clipping[$index])) {
+ $params = $this->_clipping[$index];
+ $canvas = $params['canvas'];
+ ImageCopy(
+ $canvas,
+ $this->_canvas,
+ min($params['x0'], $params['x1']),
+ min($params['y0'], $params['y1']),
+ min($params['x0'], $params['x1']),
+ min($params['y0'], $params['y1']),
+ abs($params['x1'] - $params['x0'] + 1),
+ abs($params['y1'] - $params['y0'] + 1)
+ );
+ $this->_canvas = $canvas;
+ unset($this->_clipping[$index]);
+ }
+ }
+ else {
+ $params['canvas'] = $this->_canvas;
+
+ if ($this->_gd2) {
+ $this->_canvas = ImageCreateTrueColor(
+ $this->_width,
+ $this->_height
+ );
+ if ($this->_alpha) {
+ ImageAlphaBlending($this->_canvas, true);
+ }
+ } else {
+ $this->_canvas = ImageCreate($this->_width, $this->_height);
+ }
+
+ if (($this->_gd2) && ($this->_antialias === 'native')) {
+ ImageAntialias($this->_canvas, true);
+ }
+
+ ImageCopy($this->_canvas, $params['canvas'], 0, 0, 0, 0, $this->_width, $this->_height);
+
+ $this->_clipping[count($this->_clipping)] = $params;
+ }
+ }
+
+ /**
+ * Get a canvas specific HTML tag.
+ *
+ * This method implicitly saves the canvas to the filename in the
+ * filesystem path specified and parses it as URL specified by URL path
+ *
+ * Parameter array:
+ *
+ * 'filename' string
+ *
+ * 'filepath': string Path to the file on the file system. Remember the final slash
+ *
+ * 'urlpath': string Path to the file available through an URL. Remember the final slash
+ *
+ * 'alt': string [optional] Alternative text on image
+ *
+ * 'cssclass': string [optional] The CSS Stylesheet class
+ *
+ * 'border': int [optional] The border width on the image
+ */
+ function toHtml($params)
+ {
+ parent::toHtml($params);
+ return '
_imageMap) ? ' usemap="#' . $params['filename'] . '"' : '') . '>' .
+ (isset($this->_imageMap) ? "\n" . $this->_imageMap->toHtml(array('name' => $params['filename'])) : '');
+ }
+
+ /**
+ * Resets the canvas.
+ *
+ * Include fillstyle, linestyle, thickness and polygon
+ * @access private
+ */
+ function _reset()
+ {
+ if ($this->_gd2) {
+ ImageSetThickness($this->_canvas, 1);
+ }
+ if ($this->_tileImage != null) {
+ ImageDestroy($this->_tileImage);
+ $this->_tileImage = null;
+ }
+ parent::_reset();
+ $this->_font = array('font' => 1, 'color' => 'black');
+ }
+
+ /**
+ * Check which version of GD is installed
+ *
+ * @return int 0 if GD isn't installed, 1 if GD 1.x is installed and 2 if GD
+ * 2.x is installed
+ * @access private
+ */
+ function _version()
+ {
+ $result = false;
+ if (function_exists('gd_info')) {
+ $info = gd_info();
+ $version = $info['GD Version'];
+ } else {
+ ob_start();
+ phpinfo(8);
+ $php_info = ob_get_contents();
+ ob_end_clean();
+
+ if (ereg("]*>GD Version *<\/td> | ]*>([^<]*)<\/td>",
+ $php_info, $result))
+ {
+ $version = $result[1];
+ } else {
+ $version = null;
+ }
+ }
+
+ if (ereg('1\.[0-9]{1,2}', $version)) {
+ return 1;
+ } elseif (ereg('2\.[0-9]{1,2}', $version)) {
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Marker/Pointing/Radial.php
===================================================================
RCS file: lib/pear/Image/Graph/Marker/Pointing/Radial.php
diff -N lib/pear/Image/Graph/Marker/Pointing/Radial.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Marker/Pointing/Radial.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,91 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Radial.php,v 1.5 2005/08/24 20:36:03 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Marker/Pointing.php
+ */
+require_once 'Image/Graph/Marker/Pointing.php';
+
+/**
+ * A pointing marker in a random angle from the data
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Marker
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Marker_Pointing_Radial extends Image_Graph_Marker_Pointing
+{
+
+ /**
+ * The radius of the radial marker
+ * @var int
+ * @access private
+ */
+ var $_radius;
+
+ /**
+ * Create an radial pointing marker, ie a marker on a defined distance from
+ * the data
+ * @param int $radius The 'length' of the pointer
+ * @param Marker $markerEnd The ending marker that represents 'the head of
+ * the pin'
+ */
+ function Image_Graph_Marker_Pointing_Radial($radius, & $markerEnd)
+ {
+ parent::Image_Graph_Marker_Pointing(0, 0, $markerEnd);
+ $this->_radius = $radius;
+ }
+
+ /**
+ * Draw the marker on the canvas
+ * @param int $x The X (horizontal) position (in pixels) of the marker on
+ * the canvas
+ * @param int $y The Y (vertical) position (in pixels) of the marker on the
+ * canvas
+ * @param array $values The values representing the data the marker 'points'
+ * to
+ * @access private
+ */
+ function _drawMarker($x, $y, $values = false)
+ {
+ $angle = pi() * rand(0, 360) / 180;
+ $this->_deltaX = $this->_radius * cos($angle);
+ $this->_deltaY = $this->_radius * sin($angle);
+ parent::_drawMarker($x, $y, $values);
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Simple.php
===================================================================
RCS file: lib/pear/Image/Graph/Simple.php
diff -N lib/pear/Image/Graph/Simple.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Simple.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,121 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Simple.php,v 1.8 2005/08/24 20:35:54 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph.php
+ */
+require_once 'Image/Graph.php';
+
+/**
+ * Class for simple creation of graphs
+ *
+ * @category Images
+ * @package Image_Graph
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Simple extends Image_Graph
+{
+
+ /**
+ * Image_Graph_Simple [Constructor]
+ *
+ * @param int $width The width of the graph in pixels
+ * @param int $height The height of the graph in pixels
+ */
+ function Image_Graph_Simple($width, $height, $plotType, $data, $title, $lineColor = 'black', $fillColor = 'white', $font = false)
+ {
+ parent::Image_Graph($width, $height);
+
+ $plotarea =& Image_Graph::factory('plotarea');
+
+ $dataset =& Image_Graph::factory('dataset', array($data));
+
+ if ($font === false) {
+ $font =& Image_Graph::factory('Image_Graph_Font');
+ } elseif (is_string($font)) {
+ $font =& Image_Graph::factory('ttf_font', $font);
+ $font->setSize(8);
+ }
+
+ $this->setFont($font);
+
+ $this->add(
+ Image_Graph::vertical(
+ Image_Graph::factory('title',
+ array(
+ $title,
+ array('size_rel' => 2)
+ )
+ ),
+ $plotarea,
+ 10
+ )
+ );
+
+ $plotarea->addNew('line_grid', array(), IMAGE_GRAPH_AXIS_Y);
+
+ $plot =& $plotarea->addNew($plotType, array(&$dataset));
+ $plot->setLineColor($lineColor);
+ $plot->setFillColor($fillColor);
+
+ $axisX =& $plotarea->getAxis(IMAGE_GRAPH_AXIS_X);
+ $axisX->showLabel(
+ IMAGE_GRAPH_LABEL_MINIMUM +
+ IMAGE_GRAPH_LABEL_ZERO +
+ IMAGE_GRAPH_LABEL_MAXIMUM
+ );
+
+ }
+
+ /**
+ * Factory method to create the Image_Simple_Graph object.
+ */
+ function &factory($width, $height, $plotType, $data, $title, $lineColor = 'black', $fillColor = 'white', $font = false)
+ {
+ $obj =& Image_Graph::factory('Image_Graph_Simple',
+ array(
+ $width,
+ $height,
+ $plotType,
+ $data,
+ $title,
+ $lineColor,
+ $fillColor,
+ $font
+ )
+ );
+ return $obj;
+ }
+
+}
+?>
Index: lib/pear/Image/Graph/Axis/Logarithmic.php
===================================================================
RCS file: lib/pear/Image/Graph/Axis/Logarithmic.php
diff -N lib/pear/Image/Graph/Axis/Logarithmic.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Axis/Logarithmic.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,152 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Logarithmic.php,v 1.15 2006/03/02 12:35:57 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Axis.php
+ */
+require_once 'Image/Graph/Axis.php';
+
+/**
+ * Diplays a logarithmic axis (either X- or Y-axis).
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Axis
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Axis_Logarithmic extends Image_Graph_Axis
+{
+
+ /**
+ * Image_Graph_AxisLogarithmic [Constructor].
+ *
+ * Normally a manual creation should not be necessary, axis are
+ * created automatically by the {@link Image_Graph_Plotarea} constructor
+ * unless explicitly defined otherwise
+ *
+ * @param int $type The type (direction) of the Axis, use IMAGE_GRAPH_AXIS_X
+ * for an X-axis (default, may be omitted) and IMAGE_GRAPH_AXIS_Y for Y-
+ * axis)
+ */
+ function Image_Graph_Axis_Logarithmic($type = IMAGE_GRAPH_AXIS_X)
+ {
+ parent::Image_Graph_Axis($type);
+ $this->showLabel(IMAGE_GRAPH_LABEL_MINIMUM + IMAGE_GRAPH_LABEL_MAXIMUM);
+ $this->_minimum = 1;
+ $this->_minimumSet = true;
+ }
+
+ /**
+ * Calculate the label interval
+ *
+ * If explicitly defined this will be calucated to an approximate best.
+ *
+ * @return double The label interval
+ * @access private
+ */
+ function _calcLabelInterval()
+ {
+ $result = parent::_calcLabelInterval();
+ $this->_axisValueSpan = $this->_value($this->_axisSpan);
+ return $result;
+ }
+
+ /**
+ * Preprocessor for values, ie for using logarithmic axis
+ *
+ * @param double $value The value to preprocess
+ * @return double The preprocessed value
+ * @access private
+ */
+ function _value($value)
+ {
+ return log10($value) - log10($this->_minimum);
+ }
+
+ /**
+ * Get next label point
+ *
+ * @param doubt $point The current point, if omitted or false, the first is
+ * returned
+ * @return double The next label point
+ * @access private
+ */
+ function _getNextLabel($currentLabel = false, $level = 1)
+ {
+ if (is_array($this->_labelOptions[$level]['interval'])) {
+ return parent::_getNextLabel($currentLabel, $level);
+ }
+
+ if ($currentLabel !== false) {
+ $value = log10($currentLabel);
+ $base = floor($value);
+ $frac = $value - $base;
+ for ($i = 2; $i < 10; $i++) {
+ if ($frac <= (log10($i)-0.01)) {
+ $label = pow(10, $base)*$i;
+ if ($label > $this->_getMaximum()) {
+ return false;
+ } else {
+ return $label;
+ }
+ }
+ }
+ return pow(10, $base+1);
+ }
+
+ return max(1, $this->_minimum);
+ }
+
+ /**
+ * Get the axis intersection pixel position
+ *
+ * This is only to be called prior to output! I.e. between the user
+ * invokation of Image_Graph::done() and any actual output is performed.
+ * This is because it can change the axis range.
+ *
+ * @param double $value the intersection value to get the pixel-point for
+ * @return double The pixel position along the axis
+ * @access private
+ */
+ function _intersectPoint($value)
+ {
+ if (($value <= 0) && ($value !== 'max') && ($value !== 'min')) {
+ $value = 1;
+ }
+ return parent::_intersectPoint($value);
+ }
+
+}
+
+?>
Index: lang/en_utf8/report_openlearner.php
===================================================================
RCS file: lang/en_utf8/report_openlearner.php
diff -N lang/en_utf8/report_openlearner.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lang/en_utf8/report_openlearner.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,59 @@
+
Index: lib/pear/Image/Graph/Fill/Image.php
===================================================================
RCS file: lib/pear/Image/Graph/Fill/Image.php
diff -N lib/pear/Image/Graph/Fill/Image.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Fill/Image.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,97 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Image.php,v 1.7 2005/08/24 20:36:03 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Fill.php
+ */
+require_once 'Image/Graph/Fill.php';
+
+/**
+ * Fill using an image.
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Fill
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Fill_Image extends Image_Graph_Fill
+{
+
+ /**
+ * The file name
+ * @var stirng
+ * @access private
+ */
+ var $_filename;
+
+ /**
+ * The GD Image resource
+ * @var resource
+ * @access private
+ */
+ var $_image;
+
+ /**
+ * Resize the image to the bounding box of the area to fill
+ * @var bool
+ * @access private
+ */
+ var $_resize = true;
+
+ /**
+ * Image_Graph_ImageFill [Constructor]
+ *
+ * @param string $filename The filename and path of the image to use for filling
+ */
+ function Image_Graph_Fill_Image($filename)
+ {
+ parent::Image_Graph_Fill();
+ $this->_filename = $filename;
+ }
+
+ /**
+ * Return the fillstyle
+ *
+ * @param (something) $ID (Add description)
+ * @return int A GD fillstyle
+ * @access private
+ */
+ function _getFillStyle($ID = false)
+ {
+ return $this->_filename;
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Figure/Rectangle.php
===================================================================
RCS file: lib/pear/Image/Graph/Figure/Rectangle.php
diff -N lib/pear/Image/Graph/Figure/Rectangle.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Figure/Rectangle.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,96 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Rectangle.php,v 1.9 2005/08/24 20:36:01 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Element.php
+ */
+require_once 'Image/Graph/Element.php';
+
+/**
+ * Rectangle to draw on the canvas
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Figure
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Figure_Rectangle extends Image_Graph_Element
+{
+
+ /**
+ * Rectangle [Construcor]
+ *
+ * @param int $x The leftmost pixel of the box on the canvas
+ * @param int $y The topmost pixel of the box on the canvas
+ * @param int $width The width in pixels of the box on the canvas
+ * @param int $height The height in pixels of the box on the canvas
+ */
+ function Image_Graph_Figure_Rectangle($x, $y, $width, $height)
+ {
+ parent::Image_Graph_Element();
+ $this->_setCoords($x, $y, $x + $width, $y + $height);
+ }
+
+ /**
+ * Output the box
+ *
+ * @return bool Was the output 'good' (true) or 'bad' (false).
+ * @access private
+ */
+ function _done()
+ {
+ if (parent::_done() === false) {
+ return false;
+ }
+
+ $this->_canvas->startGroup(get_class($this));
+
+ $this->_getFillStyle();
+ $this->_getLineStyle();
+ $this->_canvas->rectangle(
+ array(
+ 'x0' => $this->_left,
+ 'y0' => $this->_top,
+ 'x1' => $this->_right,
+ 'y1' => $this->_bottom
+ )
+ );
+
+ $this->_canvas->endGroup();
+
+ return true;
+ }
+
+}
+?>
Index: lib/pear/Image/Graph/Marker/Value.php
===================================================================
RCS file: lib/pear/Image/Graph/Marker/Value.php
diff -N lib/pear/Image/Graph/Marker/Value.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Marker/Value.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,214 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Value.php,v 1.10 2006/02/28 22:48:07 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Marker.php
+ */
+require_once 'Image/Graph/Marker.php';
+
+/**
+ * A marker showing the data value.
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Marker
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Marker_Value extends Image_Graph_Marker
+{
+
+ /**
+ * Datapreproccesor to format the value
+ * @var DataPreprocessor
+ * @access private
+ */
+ var $_dataPreprocessor = null;
+
+ /**
+ * Which value to use from the data set, ie the X or Y value
+ * @var int
+ * @access private
+ */
+ var $_useValue;
+
+ /**
+ * Create a value marker, ie a box containing the value of the 'pointing
+ * data'
+ *
+ * @param int $useValue Defines which value to use from the dataset, i.e. the
+ * X or Y value
+ */
+ function Image_Graph_Marker_Value($useValue = IMAGE_GRAPH_VALUE_X)
+ {
+ parent::Image_Graph_Marker();
+ $this->_padding = array('left' => 2, 'top' => 2, 'right' => 2, 'bottom' => 2);
+ $this->_useValue = $useValue;
+ $this->_fillStyle = 'white';
+ $this->_borderStyle = 'black';
+ }
+
+ /**
+ * Sets the background fill style of the element
+ *
+ * @param Image_Graph_Fill $background The background
+ * @see Image_Graph_Fill
+ */
+ function setBackground(& $background)
+ {
+ $this->setFillStyle($background);
+ }
+
+ /**
+ * Sets the background color of the element
+ *
+ * @param mixed $color The color
+ */
+ function setBackgroundColor($color)
+ {
+ $this->setFillColor($color);
+ }
+
+ /**
+ * Sets a data preprocessor for formatting the values
+ *
+ * @param DataPreprocessor $dataPreprocessor The data preprocessor
+ * @return Image_Graph_DataPreprocessor The data preprocessor
+ */
+ function &setDataPreprocessor(& $dataPreprocessor)
+ {
+ $this->_dataPreprocessor =& $dataPreprocessor;
+ return $dataPreprocessor;
+ }
+
+ /**
+ * Get the value to display
+ *
+ * @param array $values The values representing the data the marker 'points'
+ * to
+ * @return string The display value, this is the pre-preprocessor value, to
+ * support for customized with multiple values. i.e show 'x = y' or '(x, y)'
+ * @access private
+ */
+ function _getDisplayValue($values)
+ {
+ switch ($this->_useValue) {
+ case IMAGE_GRAPH_VALUE_X:
+ $value = $values['X'];
+ break;
+
+ case IMAGE_GRAPH_PCT_X_MIN:
+ $value = $values['PCT_MIN_X'];
+ break;
+
+ case IMAGE_GRAPH_PCT_X_MAX:
+ $value = $values['PCT_MAX_X'];
+ break;
+
+ case IMAGE_GRAPH_PCT_Y_MIN:
+ $value = $values['PCT_MIN_Y'];
+ break;
+
+ case IMAGE_GRAPH_PCT_Y_MAX:
+ $value = $values['PCT_MAX_Y'];
+ break;
+
+ case IMAGE_GRAPH_PCT_Y_TOTAL:
+ if (isset($values['SUM_Y'])) {
+ $value = 100 * $values['Y'] / $values['SUM_Y'];
+ }
+ else {
+ $value = 0;
+ }
+ break;
+
+ case IMAGE_GRAPH_POINT_ID:
+ $value = $values['ID'];
+ break;
+
+ default:
+ $value = $values['Y'];
+ break;
+ }
+ return $value;
+ }
+
+ /**
+ * Draw the marker on the canvas
+ *
+ * @param int $x The X (horizontal) position (in pixels) of the marker on
+ * the canvas
+ * @param int $y The Y (vertical) position (in pixels) of the marker on the
+ * canvas
+ * @param array $values The values representing the data the marker 'points'
+ * to
+ * @access private
+ */
+ function _drawMarker($x, $y, $values = false)
+ {
+ parent::_drawMarker($x, $y, $values);
+
+ $value = $this->_getDisplayValue($values);
+
+ if ($this->_dataPreprocessor) {
+ $value = $this->_dataPreprocessor->_process($value);
+ }
+
+ if ($this->_defaultFontOptions !== false) {
+ $this->_canvas->setFont($this->_defaultFontOptions);
+ } else {
+ $this->_canvas->setFont($this->_getFont());
+ }
+
+ $width = $this->_canvas->textWidth($value);
+ $height = $this->_canvas->textHeight($value);
+ $offsetX = $width/2 + $this->_padding['left'];
+ $offsetY = $height/2 + $this->_padding['top'];
+
+ $this->_getFillStyle();
+ $this->_getBorderStyle();
+ $this->_canvas->rectangle(
+ array(
+ 'x0' => $x - $offsetX,
+ 'y0' => $y - $offsetY,
+ 'x1' => $x + $offsetX,
+ 'y1' => $y + $offsetY
+ )
+ );
+
+ $this->write($x, $y, $value, IMAGE_GRAPH_ALIGN_CENTER);
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Plot/Pie.php
===================================================================
RCS file: lib/pear/Image/Graph/Plot/Pie.php
diff -N lib/pear/Image/Graph/Plot/Pie.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Plot/Pie.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,621 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Pie.php,v 1.20 2006/03/06 22:05:22 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Plot.php
+ */
+require_once 'Image/Graph/Plot.php';
+
+/**
+ * 2D Piechart.
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Plot
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Plot_Pie extends Image_Graph_Plot
+{
+
+ /**
+ * The radius of the 'pie' spacing
+ * @access private
+ * @var int
+ */
+ var $_radius = 3;
+
+ /**
+ * Explode pie slices.
+ * @access private
+ * @var mixed
+ */
+ var $_explode = false;
+
+ /**
+ * The starting angle of the plot
+ * @access private
+ * @var int
+ */
+ var $_startingAngle = 0;
+
+ /**
+ * The angle direction (1 = CCW, -1 = CW)
+ * @access private
+ * @var int
+ */
+ var $_angleDirection = 1;
+
+ /**
+ * The diameter of the pie plot
+ * @access private
+ * @var int
+ */
+ var $_diameter = false;
+
+ /**
+ * Group items below this limit together as "the rest"
+ * @access private
+ * @var double
+ */
+ var $_restGroupLimit = false;
+
+ /**
+ * Rest group title
+ * @access private
+ * @var string
+ */
+ var $_restGroupTitle = 'The rest';
+
+ /**
+ * Perform the actual drawing on the legend.
+ *
+ * @param int $x0 The top-left x-coordinate
+ * @param int $y0 The top-left y-coordinate
+ * @param int $x1 The bottom-right x-coordinate
+ * @param int $y1 The bottom-right y-coordinate
+ * @access private
+ */
+ function _drawLegendSample($x0, $y0, $x1, $y1)
+ {
+ $y = ($y0 + $y1) / 2;
+ $this->_canvas->pieslice(
+ array(
+ 'x' => $x1,
+ 'y' => $y,
+ 'rx' => abs($x1 - $x0) / 2,
+ 'ry' => abs($y1 - $y0) / 2,
+ 'v1' => 45,
+ 'v2' => 315
+ )
+ );
+ }
+
+ /**
+ * Calculate marker point data
+ *
+ * @param array $point The point to calculate data for
+ * @param array $nextPoint The next point
+ * @param array $prevPoint The previous point
+ * @param array $totals The pre-calculated totals, if needed
+ * @return array An array containing marker point data
+ * @access private
+ */
+ function _getMarkerData($point, $nextPoint, $prevPoint, &$totals)
+ {
+ $point = parent::_getMarkerData($point, $nextPoint, $prevPoint, $totals);
+
+ $y = $totals['CURRENT_Y'];
+
+ if ($this->_angleDirection < 0) {
+ $y = $totals['ALL_SUM_Y'] - $y;
+ }
+
+ $point['ANGLE'] = 360 * (($y + ($point['Y'] / 2)) / $totals['ALL_SUM_Y']) + $this->_startingAngle;
+ $totals['CURRENT_Y'] += $point['Y'];
+
+ $point['ANG_X'] = cos(deg2rad($point['ANGLE']));
+ $point['ANG_Y'] = sin(deg2rad($point['ANGLE']));
+
+ $point['AX'] = -10 * $point['ANG_X'];
+ $point['AY'] = -10 * $point['ANG_Y'];
+
+ if ((isset($totals['ALL_SUM_Y'])) && ($totals['ALL_SUM_Y'] != 0)) {
+ $point['PCT_MIN_Y'] = $point['PCT_MAX_Y'] = (100 * $point['Y'] / $totals['ALL_SUM_Y']);
+ }
+
+ $point['LENGTH'] = 10; //$radius;
+
+ $x = $point['X'];
+ $explodeRadius = 0;
+ if ((is_array($this->_explode)) && (isset($this->_explode[$x]))) {
+ $explodeRadius = $this->_explode[$x];
+ } elseif (is_numeric($this->_explode)) {
+ $explodeRadius = $this->_explode;
+ }
+
+ $point['MARKER_X'] = $totals['CENTER_X'] +
+ ($totals['RADIUS'] + $explodeRadius) * $point['ANG_X'];
+ $point['MARKER_Y'] = $totals['CENTER_Y'] +
+ ($totals['RADIUS'] + $explodeRadius) * $point['ANG_Y'];
+
+ return $point;
+ }
+
+ /**
+ * Draws markers on the canvas
+ *
+ * @access private
+ */
+ function _drawMarker()
+ {
+
+ if ($this->_marker) {
+ $totals = $this->_getTotals();
+
+ $totals['CENTER_X'] = (int) (($this->_left + $this->_right) / 2);
+ $totals['CENTER_Y'] = (int) (($this->_top + $this->_bottom) / 2);
+
+ $totals['CURRENT_Y'] = 0;
+ $number = 0;
+
+ $diameter = $this->_getDiameter();
+
+ $keys = array_keys($this->_dataset);
+ foreach ($keys as $key) {
+ $dataset =& $this->_dataset[$key];
+
+ if (count($this->_dataset) == 1) {
+ $totals['RADIUS0'] = false;
+ $totals['RADIUS'] = $diameter / 2;
+ } else {
+ $dr = $diameter / (2 * count($this->_dataset));
+
+ $totals['RADIUS0'] = $number * $dr + ($number > 0 ? $this->_radius : 0);
+ $totals['RADIUS'] = ($number + 1) * $dr;
+ }
+
+ $totals['ALL_SUM_Y'] = 0;
+ $totals['CURRENT_Y'] = 0;
+ $dataset->_reset();
+ while ($point = $dataset->_next()) {
+ $totals['ALL_SUM_Y'] += $point['Y'];
+ }
+
+ $dataset->_reset();
+ $currentY = 0;
+ $the_rest = 0;
+ while ($point = $dataset->_next()) {
+ if (($this->_restGroupLimit !== false) && ($point['Y'] <= $this->_restGroupLimit)) {
+ $the_rest += $point['Y'];
+ }
+ else {
+ if ((!is_object($this->_dataSelector)) ||
+ ($this->_dataSelector->select($point))
+ ) {
+ $point = $this->_getMarkerData(
+ $point,
+ false,
+ false,
+ $totals
+ );
+ if (is_array($point)) {
+ $this->_marker->_drawMarker(
+ $point['MARKER_X'],
+ $point['MARKER_Y'],
+ $point
+ );
+ }
+ }
+ }
+ }
+ if ($the_rest > 0) {
+ $point = array('X' => $this->_restGroupTitle, 'Y' => $the_rest);
+ $point = $this->_getMarkerData(
+ $point,
+ false,
+ false,
+ $totals
+ );
+ if (is_array($point)) {
+ $this->_marker->_drawMarker(
+ $point['MARKER_X'],
+ $point['MARKER_Y'],
+ $point
+ );
+ }
+ }
+ $number++;
+ }
+ unset($keys);
+ }
+ }
+
+ /**
+ * Explodes a piece of this pie chart
+ *
+ * @param int $explode Radius to explode with (or an array)
+ * @param string $x The 'x' value to explode or omitted
+ */
+ function explode($explode, $x = false)
+ {
+ if ($x === false) {
+ $this->_explode = $explode;
+ } else {
+ $this->_explode[$x] = $explode;
+ }
+ }
+
+ /**
+ * Set the starting angle of the plot
+ *
+ * East is 0 degrees
+ * South is 90 degrees
+ * West is 180 degrees
+ * North is 270 degrees
+ *
+ * It is also possible to specify the direction of the plot angles (i.e. clockwise 'cw' or
+ * counterclockwise 'ccw')
+ */
+ function setStartingAngle($angle = 0, $direction = 'ccw')
+ {
+ $this->_startingAngle = ($angle % 360);
+ $this->_angleDirection = ($direction == 'ccw' ? 1 : -1);
+ }
+
+ /**
+ * Set the diameter of the pie plot (i.e. the number of pixels the
+ * pie plot should be across)
+ *
+ * Use 'max' for the maximum possible diameter
+ *
+ * Use negative values for the maximum possible - minus this value (fx -2
+ * to leave 1 pixel at each side)
+ *
+ * @param mixed @diameter The number of pixels
+ */
+ function setDiameter($diameter)
+ {
+ $this->_diameter = $diameter;
+ }
+
+ /**
+ * Set the limit for the y-value, where values below are grouped together
+ * as "the rest"
+ *
+ * @param double $limit The limit
+ * @param string $title The title to display in the legends (default 'The
+ * rest')
+ */
+ function setRestGroup($limit, $title = 'The rest')
+ {
+ $this->_restGroupLimit = $limit;
+ $this->_restGroupTitle = $title;
+ }
+
+ /**
+ * Get the diameter of the plot
+ * @return int The number of pixels the diameter is
+ * @access private
+ */
+ function _getDiameter()
+ {
+ $diameter = 0;
+ if ($this->_diameter === false) {
+ $diameter = min($this->height(), $this->width()) * 0.75;
+ }
+ else {
+ if ($this->_diameter === 'max') {
+ $diameter = min($this->height(), $this->width());
+ }
+ elseif ($this->_diameter < 0) {
+ $diameter = min($this->height(), $this->width()) + $this->_diameter;
+ } else {
+ $diameter = $this->_diameter;
+ }
+ }
+ return $diameter;
+ }
+
+
+ /**
+ * Output the plot
+ *
+ * @return bool Was the output 'good' (true) or 'bad' (false).
+ * @access private
+ */
+ function _done()
+ {
+ if (parent::_done() === false) {
+ return false;
+ }
+
+ $number = 0;
+
+ $keys = array_keys($this->_dataset);
+ foreach ($keys as $key) {
+ $dataset =& $this->_dataset[$key];
+
+ $totalY = 0;
+ $dataset->_reset();
+ while ($point = $dataset->_next()) {
+ $totalY += $point['Y'];
+ }
+
+ $centerX = (int) (($this->_left + $this->_right) / 2);
+ $centerY = (int) (($this->_top + $this->_bottom) / 2);
+ $diameter = $this->_getDiameter();
+ if ($this->_angleDirection < 0) {
+ $currentY = $totalY;
+ } else {
+ $currentY = 0; //rand(0, 100)*$totalY/100;
+ }
+ $dataset->_reset();
+
+ if (count($this->_dataset) == 1) {
+ $radius0 = false;
+ $radius1 = $diameter / 2;
+ } else {
+ $dr = $diameter / (2 * count($this->_dataset));
+
+ $radius0 = $number * $dr + ($number > 0 ? $this->_radius : 0);
+ $radius1 = ($number + 1) * $dr;
+ }
+
+ $the_rest = 0;
+ while ($point = $dataset->_next()) {
+ if (($this->_restGroupLimit !== false) && ($point['Y'] <= $this->_restGroupLimit)) {
+ $the_rest += $point['Y'];
+ }
+ else {
+ $angle1 = 360 * ($currentY / $totalY) + $this->_startingAngle;
+ $currentY += $this->_angleDirection * $point['Y'];
+ $angle2 = 360 * ($currentY / $totalY) + $this->_startingAngle;
+
+ $x = $point['X'];
+ $id = $point['ID'];
+
+ $dX = 0;
+ $dY = 0;
+ $explodeRadius = 0;
+ if ((is_array($this->_explode)) && (isset($this->_explode[$x]))) {
+ $explodeRadius = $this->_explode[$x];
+ } elseif (is_numeric($this->_explode)) {
+ $explodeRadius = $this->_explode;
+ }
+
+ if ($explodeRadius > 0) {
+ $dX = $explodeRadius * cos(deg2rad(($angle1 + $angle2) / 2));
+ $dY = $explodeRadius * sin(deg2rad(($angle1 + $angle2) / 2));
+ }
+
+ $ID = $point['ID'];
+ $this->_getFillStyle($ID);
+ $this->_getLineStyle($ID);
+ $this->_canvas->pieslice(
+ $this->_mergeData(
+ $point,
+ array(
+ 'x' => $centerX + $dX,
+ 'y' => $centerY + $dY,
+ 'rx' => $radius1,
+ 'ry' => $radius1,
+ 'v1' => $angle1,
+ 'v2' => $angle2,
+ 'srx' => $radius0,
+ 'sry' => $radius0
+ )
+ )
+ );
+ }
+ }
+
+ if ($the_rest > 0) {
+ $angle1 = 360 * ($currentY / $totalY) + $this->_startingAngle;
+ $currentY += $this->_angleDirection * $the_rest;
+ $angle2 = 360 * ($currentY / $totalY) + $this->_startingAngle;
+
+ $x = 'rest';
+ $id = 'rest';
+
+ $dX = 0;
+ $dY = 0;
+ $explodeRadius = 0;
+ if ((is_array($this->_explode)) && (isset($this->_explode[$x]))) {
+ $explodeRadius = $this->_explode[$x];
+ } elseif (is_numeric($this->_explode)) {
+ $explodeRadius = $this->_explode;
+ }
+
+ if ($explodeRadius > 0) {
+ $dX = $explodeRadius * cos(deg2rad(($angle1 + $angle2) / 2));
+ $dY = $explodeRadius * sin(deg2rad(($angle1 + $angle2) / 2));
+ }
+
+ $ID = $id;
+ $this->_getFillStyle($ID);
+ $this->_getLineStyle($ID);
+ $this->_canvas->pieslice(
+ $this->_mergeData(
+ $point,
+ array(
+ 'x' => $centerX + $dX,
+ 'y' => $centerY + $dY,
+ 'rx' => $radius1,
+ 'ry' => $radius1,
+ 'v1' => $angle1,
+ 'v2' => $angle2,
+ 'srx' => $radius0,
+ 'sry' => $radius0
+ )
+ )
+ );
+ }
+ $number++;
+ }
+ unset($keys);
+ $this->_drawMarker();
+ return true;
+ }
+
+ /**
+ * Draw a sample for use with legend
+ *
+ * @param array $param The parameters for the legend
+ * @access private
+ */
+ function _legendSample(&$param)
+ {
+ if (is_array($this->_dataset)) {
+
+ $this->_canvas->startGroup(get_class($this) . '_' . $this->_title);
+
+ $totals = $this->_getTotals();
+ $totals['CENTER_X'] = (int) (($this->_left + $this->_right) / 2);
+ $totals['CENTER_Y'] = (int) (($this->_top + $this->_bottom) / 2);
+ $totals['RADIUS'] = min($this->height(), $this->width()) * 0.75 * 0.5;
+ $totals['CURRENT_Y'] = 0;
+
+ if (is_a($this->_fillStyle, "Image_Graph_Fill")) {
+ $this->_fillStyle->_reset();
+ }
+
+ $count = 0;
+ $keys = array_keys($this->_dataset);
+ foreach ($keys as $key) {
+ $dataset =& $this->_dataset[$key];
+ $count++;
+
+ $dataset->_reset();
+ $the_rest = 0;
+ while ($point = $dataset->_next()) {
+ $caption = $point['X'];
+ if (($this->_restGroupLimit !== false) && ($point['Y'] <= $this->_restGroupLimit)) {
+ $the_rest += $point['Y'];
+ }
+ else {
+ $this->_canvas->setFont($param['font']);
+ $width = 20 + $param['width'] + $this->_canvas->textWidth($caption);
+ $param['maxwidth'] = max($param['maxwidth'], $width);
+ $x2 = $param['x'] + $width;
+ $y2 = $param['y'] + $param['height']+5;
+
+ if ((($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) != 0) && ($y2 > $param['bottom'])) {
+ $param['y'] = $param['top'];
+ $param['x'] = $x2;
+ $y2 = $param['y'] + $param['height'];
+ } elseif ((($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) == 0) && ($x2 > $param['right'])) {
+ $param['x'] = $param['left'];
+ $param['y'] = $y2;
+ $x2 = $param['x'] + 20 + $param['width'] + $this->_canvas->textWidth($caption);
+ }
+
+ $x = $x0 = $param['x'];
+ $y = $param['y'];
+ $y0 = $param['y'] - $param['height']/2;
+ $x1 = $param['x'] + $param['width'];
+ $y1 = $param['y'] + $param['height']/2;
+
+ if (!isset($param['simulate'])) {
+ $this->_getFillStyle($point['ID']);
+ $this->_getLineStyle($point['ID']);
+ $this->_drawLegendSample($x0, $y0, $x1, $y1);
+
+ if (($this->_marker) && ($dataset) && ($param['show_marker'])) {
+ $prevPoint = $dataset->_nearby(-2);
+ $nextPoint = $dataset->_nearby();
+
+ $p = $this->_getMarkerData($point, $nextPoint, $prevPoint, $totals);
+ if (is_array($point)) {
+ $p['MARKER_X'] = $x+$param['width']/2;
+ $p['MARKER_Y'] = $y;
+ unset ($p['AVERAGE_Y']);
+ $this->_marker->_drawMarker($p['MARKER_X'], $p['MARKER_Y'], $p);
+ }
+ }
+ $this->write($x + $param['width'] +10, $y, $caption, IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_LEFT, $param['font']);
+ }
+
+ if (($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) != 0) {
+ $param['y'] = $y2;
+ } else {
+ $param['x'] = $x2;
+ }
+ }
+ }
+ if ($the_rest > 0) {
+ $this->_canvas->setFont($param['font']);
+ $width = 20 + $param['width'] + $this->_canvas->textWidth($this->_restGroupTitle);
+ $param['maxwidth'] = max($param['maxwidth'], $width);
+ $x2 = $param['x'] + $width;
+ $y2 = $param['y'] + $param['height']+5;
+
+ if ((($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) != 0) && ($y2 > $param['bottom'])) {
+ $param['y'] = $param['top'];
+ $param['x'] = $x2;
+ $y2 = $param['y'] + $param['height'];
+ } elseif ((($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) == 0) && ($x2 > $param['right'])) {
+ $param['x'] = $param['left'];
+ $param['y'] = $y2;
+ $x2 = $param['x'] + 20 + $param['width'] + $this->_canvas->textWidth($this->_restGroupTitle);
+ }
+
+ $x = $x0 = $param['x'];
+ $y = $param['y'];
+ $y0 = $param['y'] - $param['height']/2;
+ $x1 = $param['x'] + $param['width'];
+ $y1 = $param['y'] + $param['height']/2;
+
+ if (!isset($param['simulate'])) {
+ $this->_getFillStyle('rest');
+ $this->_getLineStyle('rest');
+ $this->_drawLegendSample($x0, $y0, $x1, $y1);
+
+ $this->write($x + $param['width'] + 10, $y, $this->_restGroupTitle, IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_LEFT, $param['font']);
+ }
+
+ if (($param['align'] & IMAGE_GRAPH_ALIGN_VERTICAL) != 0) {
+ $param['y'] = $y2;
+ } else {
+ $param['x'] = $x2;
+ }
+ }
+ }
+ unset($keys);
+ $this->_canvas->endGroup();
+ }
+ }
+
+}
+
+?>
Index: lang/en_utf8/help/quiz/openlearnermode.html
===================================================================
RCS file: lang/en_utf8/help/quiz/openlearnermode.html
diff -N lang/en_utf8/help/quiz/openlearnermode.html
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lang/en_utf8/help/quiz/openlearnermode.html 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,20 @@
+Quiz Report Analysis
+
+If you choose Yes for this option then the student will be allowed to view its performance and progress in a quiz in a very detailed form (textual and graphical) and to compare it to that of the other students who attempted the quiz. Quiz Report Analysis is always enabled for the teacher.
+Quiz Report Analysis is a useful self-assessment tool, based on the findings of the studies in open learner modelling. Textual descriptions and charting information are available to students and teacher. Analysis of student performance is both per attempt and per question category. In this way students realize explicitly which topics they know and which topics they need to study harder.Quiz Reports Analysis supports various quiz options such as adaptive mode, student review options and quiz grading methods and uses the appropriate quiz feedback fields for student feedback. It consists of four sections:
+
+Textual Presentation
+
+In this section, students can view their performance in a particular quiz attempt. Data is presented in text and includes performance stats per attempt and per question category, such as the types of questions posed, known/problematic topics and misconceptions, not answered questions, performance feedback etc.
+
+Graphical Presentation
+
+In this section, students can view their performance in a particular quiz attempt. Attempt performance is presented in bar charts and includes detailed stats about current attempt and each question category.Student can also view which question types were used per question category presented in pie charts.
+
+Attempt Comparison
+
+In this section, students can compare their performance in a quiz attempt to the average performance of students in all quiz attempts.The average performance of students is calculated, considering the quiz grading method and data is presented in bar charts. If quiz grading method is Highest Grade the average student performance is calculated, selecting for each student the attempt with the highest grade(if there are more than one, select the most recent of them) and calculate the mean for the desired data stats. If quiz grading method is Average Grade the average student performance is calculated, calculating the mean of all student attempts, for the desired data stats . If quiz grading method is First Grade the average student performance is calculated, selecting the first attempt of each student and calculate the mean for the desired data stats. If is Last Grade the average student performance is calculated, selecting for each student the most recent attempt and calculate the mean for the desired data stats.
+
+Progress Comparison
+
+In this section, students can view their progress in all their attempts and to compare it to that of all students. Attempt scores for each student attempt and per question category are presented in bar charts. Their attempt progress is compared to average attempt score of each attempt and per question category, for all students.
Index: mod/quiz/openlearner/graphical/report.php
===================================================================
RCS file: mod/quiz/openlearner/graphical/report.php
diff -N mod/quiz/openlearner/graphical/report.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod/quiz/openlearner/graphical/report.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,570 @@
+uniqueid)) {
+ // this question has not yet been upgraded to the new model
+ quiz_upgrade_states($attempt);
+ }
+
+ // Only print headers if not asked to download data
+ if (!$download = optional_param('download', NULL)) {
+ $this->print_header_and_tabs($cm, $course, $quiz, $attemptno, $reportmode='graphical');
+ }
+
+ // Define some strings
+ $reporturl = $CFG->wwwroot.'/mod/quiz/openlearner.php';
+ $userid = get_field('quiz_attempts', 'userid', 'id', $attempt->uniqueid);
+ $login = get_field('user', 'username', 'id', $userid);
+ $firstname = get_field('user', 'firstname', 'id', $userid);
+ $lastname = get_field('user', 'lastname', 'id', $userid);
+
+ // create temp directory for page's images
+ $dir = $CFG->dataroot.'/openlearner/';
+ if (!file_exists($dir)) {
+ mkdir($dir);
+ }
+ $dir = $CFG->dataroot.'/openlearner/graphical/';
+ if (!file_exists($dir)) {
+ mkdir($dir);
+ }
+ $dir = $CFG->dataroot.'/openlearner/graphical/'.$USER->id.'/';
+ if (!file_exists($dir)) {
+ mkdir($dir);
+ }
+ $dir = $CFG->dataroot.'/openlearner/graphical/'.$USER->id.'/'.$userid.'/';
+ if (!file_exists($dir)) {
+ mkdir($dir);
+ }
+ $filename = $CFG->dataroot.'/openlearner/graphical/'.$USER->id.'/'.$userid.'/'.$attemptno.'_graph0.png';
+ $src = $CFG->wwwroot.'/mod/quiz/openlearner/pix.php/graphical/'.$USER->id.'/'.$userid.'/'.$attemptno.'_graph0.png';
+
+ $pagelist = quiz_questions_in_quiz($attempt->layout);
+ $pagequestions = explode(',', $pagelist);
+
+ // load the questions types needed by page
+ $sql = 'SELECT DISTINCT q.qtype FROM '.$CFG->prefix.'question q, '.$CFG->prefix.'quiz_question_instances i WHERE i.quiz = '.
+ $quiz->id.' AND q.id = i.question AND q.id in ('.$pagelist.')';
+
+ if (!$qtypes = get_records_sql($sql)) {
+ error('No questions types found');
+ }
+
+ foreach ($qtypes as $key => $value) {
+ $qtypes[$key]->length = 0;
+ }
+
+ // load the questions categories needed by page
+ $sql = 'SELECT name, info FROM '.$CFG->prefix.'question_categories WHERE id in ('.'SELECT DISTINCT q.category FROM '.
+ $CFG->prefix.'question q, '.$CFG->prefix.'quiz_question_instances i WHERE i.quiz = '.$quiz->id.
+
+ ' AND q.id = i.question AND q.id in ('.$pagelist.'))';
+ if (!$qcategories = get_records_sql($sql)) {
+ error('No questions categories found');
+ }
+
+ foreach ($qcategories as $key => $value) {
+ $qcategories[$key]->qtype = array();
+ $qcategories[$key]->known = array();
+ $qcategories[$key]->misconc = array();
+ $qcategories[$key]->problem = array();
+
+ foreach ($qtypes as $type) {
+ $qcategories[$key]->qtype[$type->qtype] = 0;
+ }
+
+ $qcategories[$key]->length = $qcategories[$key]->grade = $qcategories[$key]->maxgrade = $qcategories[$key]->percentage = 0;
+ $qcategories[$key]->right = $qcategories[$key]->partial = $qcategories[$key]->wrong = $qcategories[$key]->noanswer = 0;
+ }
+
+ // load all the questions needed by page
+ $sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
+ " FROM {$CFG->prefix}question q,".
+ " {$CFG->prefix}quiz_question_instances i".
+ " WHERE i.quiz = '$quiz->id' AND q.id = i.question".
+ " AND q.id IN ($pagelist)";
+ if (!$questions = get_records_sql($sql)) {
+ error('No questions found');
+ }
+
+ // Load the question options
+ if (!get_question_options($questions)) {
+ error('Could not load question options');
+ }
+
+ // Restore the question sessions to their most recent states
+ // creating new sessions where required
+ if (!$states = get_question_states($questions, $quiz, $attempt)) {
+ error('Could not restore question sessions');
+ }
+
+ $questionsno = count_records('question_sessions','attemptid',$attempt->uniqueid);
+ // create an object which holds data stats about current attempt
+ $totalstats = new stdClass;
+ $totalstats->percentage = round(($attempt->sumgrades/$quiz->sumgrades)*100, $quiz->decimalpoints);
+ $correct = $partial = $false = $penalty = $multiple = $noanswer = $known = $miscon = 0;
+
+ // Calculate results for each question category and type
+ foreach ($pagequestions as $i) {
+ if ($questions[$i]->qtype == 'description') {
+ --$questionsno;
+ continue;
+ }
+
+ ++$qtypes[$questions[$i]->qtype]->length;
+ $questions[$i]->category = get_field('question_categories', 'name', 'id', $questions[$i]->category);
+ ++$qcategories[$questions[$i]->category]->length;
+ $qcategories[$questions[$i]->category]->grade += $states[$i]->last_graded->raw_grade;
+ $qcategories[$questions[$i]->category]->maxgrade += $questions[$i]->maxgrade;
+ ++$qcategories[$questions[$i]->category]->qtype[$questions[$i]->qtype];
+
+ // check if the answer is correct
+ if ($states[$i]->last_graded->raw_grade >= $questions[$i]->maxgrade/1.01) { // We divide by 1.01 so that rounding errors dont matter.
+ ++$correct;
+ $qpenalty = ($questions[$i]->maxgrade) - (round($states[$i]->last_graded->grade, $quiz->decimalpoints));
+ $penalty += $qpenalty;
+ ++$qcategories[$questions[$i]->category]->right;
+
+ if ($quiz->optionflags & QUESTION_ADAPTIVE and $qpenalty) {
+ $qcategories[$questions[$i]->category]->misconc[] = $questions[$i]->name;
+ ++$miscon;
+ } else {
+ $qcategories[$questions[$i]->category]->known[] = $questions[$i]->name;
+ ++$known;
+
+ }
+
+ } else if ($states[$i]->last_graded->raw_grade > 0) { // check if the answer is partially correct
+ ++$partial;
+ ++$miscon;
+ ++$qcategories[$questions[$i]->category]->partial;
+ $qcategories[$questions[$i]->category]->misconc[] = $questions[$i]->name;
+ } else { // check if the answer is wrong
+ ++$false;
+ ++$qcategories[$questions[$i]->category]->wrong;
+ $qcategories[$questions[$i]->category]->problem[] = $questions[$i]->name;
+
+ // Count the not answered questions depending on their question type
+ $sql = 'SELECT id,answer FROM '.$CFG->prefix.'question_states'.' WHERE attempt = '.$attempt->uniqueid.' AND question = '.
+ $questions[$i]->id.' AND seq_number != 0 ORDER BY id DESC';
+
+ $answer = get_record_sql($sql,true);
+
+ if ($answer) {
+ if (($questions[$i]->qtype == 'match') or ($questions[$i]->qtype == 'randomsamatch') or ($questions[$i]->qtype == 'multianswer')) {
+ $answers = explode(',',$answer->answer);
+ $noanswerflag = 0;
+
+ foreach ($answers as $value) {
+ $items = explode('-',$value);
+
+ if ((($questions[$i]->qtype == 'match') or ($questions[$i]->qtype == 'randomsamatch')) and ($items[1] != '0')) {
+ ++$noanswerflag;
+ break;
+ } else if (($questions[$i]->qtype == 'multianswer') and ($items[1] != '')) {
+ ++$noanswerflag;
+ break;
+ }
+ }
+
+ if ($noanswerflag == 0) {
+ ++$noanswer;
+ ++$qcategories[$questions[$i]->category]->noanswer;
+ }
+ } else if ($questions[$i]->qtype == 'multichoice') {
+ $answers = explode(':',$answer->answer);
+
+ if ($answers[1] == '') {
+ ++$noanswer;
+ ++$qcategories[$questions[$i]->category]->noanswer;
+ }
+ } else if ($questions[$i]->qtype == 'calculated') {
+ $answers = explode('-',$answer->answer);
+
+ if ($answers[1] == '') {
+ ++$noanswer;
+ ++$qcategories[$questions[$i]->category]->noanswer;
+ }
+ } else {
+ if ($answer->answer == '') {
+ ++$noanswer;
+ ++$qcategories[$questions[$i]->category]->noanswer;
+ }
+ }
+ }
+ }
+ if ($states[$i]->last_graded->raw_grade > $states[$i]->last_graded->grade){
+ ++$multiple;
+ }
+ }
+
+ unset($pagequestions);
+ unset($states);
+ // calculate score for each category
+ foreach ($qcategories as $key => $value) {
+ $qcategories[$key]->percentage = round(($qcategories[$key]->grade/$qcategories[$key]->maxgrade)*100, $quiz->decimalpoints);
+ }
+
+ $totalstats->correct = round(($correct/$questionsno)*100, $quiz->decimalpoints);
+ $totalstats->partial = round(($partial/$questionsno)*100, $quiz->decimalpoints);
+ $totalstats->wrong = round(($false/$questionsno)*100, $quiz->decimalpoints);
+ $totalstats->noanswer = round(($noanswer/$questionsno)*100, $quiz->decimalpoints);
+ $totalstats->known = round(($known/$questionsno)*100, $quiz->decimalpoints);
+ $totalstats->miscon = round(($miscon/$questionsno)*100, $quiz->decimalpoints);
+
+ $totalstats->calculated = $totalstats->essay = $totalstats->match = $totalstats->multianswer = $totalstats->multichoice = 0;
+ $totalstats->numerical = $totalstats->qrandom = $totalstats->random = $totalstats->short = $totalstats->truefalse = 0;
+
+ // Now check if asked download of data
+ if ($download) {
+ $file = clean_filename("$course->shortname ".format_string($quiz->name,true)." $login ".$attempt->attempt.' graphical');
+ $sort = '';
+ }
+
+ $title = $firstname.' '.$lastname.' - '.$quiz->name.' ('.get_string('attempt', 'report_openlearner').
+ $attempt->attempt.')';
+ $html = ''.format_string($title).' ';
+
+ // count question types number
+ foreach ($qtypes as $currobj) {
+ switch ($currobj->qtype) {
+ case 'calculated':
+ $totalstats->calculated = $currobj->length;
+ break;
+
+ case 'essay':
+ $totalstats->essay = $currobj->length;
+ break;
+
+ case 'match':
+ $totalstats->match = $currobj->length;
+ break;
+
+ case 'multianswer':
+ $totalstats->multianswer = $currobj->length;
+ break;
+
+ case 'multichoice':
+ $totalstats->multichoice = $currobj->length;
+ break;
+
+ case 'numerical':
+ $totalstats->numerical = $currobj->length;
+ break;
+
+ case 'random':
+ $totalstats->qrandom = $currobj->length;
+ break;
+
+ case 'randomsamatch':
+ $totalstats->random = $currobj->length;
+ break;
+
+ case 'shortanswer':
+ $totalstats->short = $currobj->length;
+ break;
+
+ case 'truefalse':
+ $totalstats->truefalse = $currobj->length;
+ break;
+
+ default:
+ }
+ }
+
+ unset($qtypes);
+ // show data graphs for current attempt
+ graph($totalstats, $filename);
+ $html .= '';
+
+ // Show category stats if we have more than one category
+ if (count($qcategories) != 1) {
+ $html .= ''.format_string(get_string('analysis', 'report_openlearner')).
+ ' ';
+ $counter = 0;
+
+ foreach ($qcategories as $currobj) {
+ $html .= ''.get_string('category', 'quiz').': '.$currobj->name.' ';
+
+ if ($currobj->info != '') {
+ $html .= ''.format_string('('.$currobj->info.')').' ';
+ }
+
+ ++$counter;
+
+ $totalstats->percentage = $currobj->percentage;
+ $totalstats->correct = round(($currobj->right/$currobj->length)*100, $quiz->decimalpoints);
+ $totalstats->partial = round(($currobj->partial/$currobj->length)*100, $quiz->decimalpoints);
+ $totalstats->wrong = round(($currobj->wrong/$currobj->length)*100, $quiz->decimalpoints);
+ $totalstats->noanswer = round(($currobj->noanswer/$currobj->length)*100, $quiz->decimalpoints);
+ $totalstats->known = round((count($currobj->known)/$currobj->length)*100, $quiz->decimalpoints);
+ $totalstats->miscon = round((count($currobj->misconc)/$currobj->length)*100, $quiz->decimalpoints);
+
+ $totalstats->calculated = $totalstats->essay = $totalstats->match = $totalstats->multianswer = $totalstats->multichoice = 0;
+ $totalstats->numerical = $totalstats->qrandom = $totalstats->random = $totalstats->short = $totalstats->truefalse = 0;
+
+ // count question types number for each question category
+ foreach ($currobj->qtype as $key => $value) {
+ switch ($key) {
+ case 'calculated':
+ $totalstats->calculated = $value;
+ break;
+
+ case 'essay':
+ $totalstats->essay = $value;
+ break;
+
+ case 'match':
+ $totalstats->match = $value;
+ break;
+
+ case 'multianswer':
+ $totalstats->multianswer = $value;
+ break;
+
+ case 'multichoice':
+ $totalstats->multichoice = $value;
+ break;
+
+ case 'numerical':
+ $totalstats->numerical = $value;
+ break;
+
+ case 'random':
+ $totalstats->qrandom = $value;
+ break;
+
+ case 'randomsamatch':
+ $totalstats->random = $value;
+ break;
+
+ case 'shortanswer':
+ $totalstats->short = $value;
+ break;
+
+ case 'truefalse':
+ $totalstats->truefalse = $value;
+ break;
+
+ default:
+ }
+ }
+
+ $filename = $CFG->dataroot.'/openlearner/graphical/'.$USER->id.'/'.$userid.'/'.$attemptno.'_graph'.$counter.'.png';
+ $src = $CFG->wwwroot.'/mod/quiz/openlearner/pix.php/graphical/'.$USER->id.'/'.$userid.'/'.$attemptno.'_graph'.$counter.'.png';
+ // show data graphs for current category
+ graph($totalstats, $filename);
+ $html .= '';
+ }
+ }
+
+ unset($qcategories);
+ unset($totalstats);
+
+ if (!$download) {
+ echo $html;
+ echo '';
+ $options = array();
+ $options['q'] = $quiz->id;
+ $options['attempt'] = $attempt->uniqueid;
+ $options['mode'] = 'graphical';
+ $options['sesskey'] = sesskey();
+ $options['noheader'] = 'yes';
+ echo '| ';
+ $options['download'] = 'PDF';
+ print_single_button($reporturl, $options, get_string('downloadpdf', 'report_openlearner'));
+ echo ' | ';
+ helpbutton('graphicaldownload', get_string('graphicaldownload', 'report_openlearner'), 'quiz');
+ echo ' | ';
+
+ } else { // download report in PDF format
+ require_once($CFG->libdir.'/pdflib.php');
+
+ $file .= '.pdf';
+ // create new PDF document
+ $pdf = new pdf();
+ // set document information
+ $pdf->SetCreator(PDF_CREATOR);
+ $pdf->SetAuthor("$firstname $lastname");
+ $pdf->SetTitle("$quiz->name attempt #$attempt->attempt");
+ $pdf->SetSubject(get_string('graphical', 'report_openlearner'));
+ $pdf->SetKeywords('TCPDF, PDF, quiz, graphical, report');
+ // set default header data
+ $pdf->SetHeaderData('', 0, $course->fullname, '');
+ // set header and footer fonts
+ $pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
+ $pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
+ //set margins
+ $pdf->SetMargins(5, PDF_MARGIN_TOP, 5);
+ $pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
+ $pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
+ //set auto page breaks
+ $pdf->SetAutoPageBreak(TRUE, 80);
+ //set image scale factor
+ $pdf->setImageScale(2.18);
+ //set some language-dependent strings
+ $a = 1;
+ $pdf->setLanguageArray($a);
+ //initialize document
+ $pdf->AliasNbPages();
+ // add a page
+ $pdf->AddPage();
+ // set font
+ $pdf->SetFont('FreeSerif', '', 11);
+ // output the HTML content
+ $pdf->writeHTML($html);
+ //Close and output PDF document
+ $pdf->Output($file,'D');
+ }
+
+ return (true);
+ }
+}
+
+/**
+ * Outputs graph images based on calculated data for a specific student attempt
+ *
+ * @uses $CFG
+ * @param mixed $stats an object with data stats.
+ * @param string $filename the filename for a specific graph image.
+ */
+function graph(&$stats, $filename) {
+ global $CFG;
+
+ require_once("$CFG->libdir/chartlib.class.php");
+
+ $object = new MoodleImageGraph();
+ // create the graph
+ $graph =& $object->new_graph(array(950, 425));
+ // add a TrueType font
+ $font =& $object->set_font($graph, 'Verdana', 10);
+ $object->add_font($graph, $font);
+ // add background color
+ $object->set_bgcolor($graph, 'white');
+ $plotareaa = $object->get_plotarea();
+ $plotareab = $object->get_plotarea();
+ $legenda = $object->get_legend();
+ $legendb = $object->get_legend();
+ // create the plotarea
+ $object->set_plotarea($graph, get_string('stats','report_openlearner'), get_string('qtypes','report_openlearner'), 10, 10, 5, 5, 80, 83, 52, $plotareaa, $plotareab, $legenda, $legendb);
+ $object->set_legend_plotarea($plotareaa, $legenda);
+ $object->set_legend_plotarea($plotareab, $legendb);
+ // set axisX & axisY properties
+ $axisxa =& $object->get_axisX($plotareaa, false);
+ $axisya =& $object->get_axisY($plotareaa, 100, 10);
+ // create the dataset for the 1st plot
+ $data11 = array(array('x'=>1, 'y'=>$stats->percentage, 'id'=> 'score'), array('x'=>2, 'y'=>0, 'id'=> 'score'),
+ array('x'=>3, 'y'=>0, 'id'=> 'score'),array('x'=>4, 'y'=>0, 'id'=> 'score'),
+ array('x'=>5, 'y'=>0, 'id'=> 'score'));
+ $data12 = array(array('x'=>1, 'y'=>0, 'id'=> 'correct'), array('x'=>2, 'y'=>$stats->correct, 'id'=> 'correct'),
+ array('x'=>3, 'y'=>0, 'id'=> 'correct'), array('x'=>4, 'y'=>0, 'id'=> 'correct'),
+ array('x'=>5, 'y'=>0, 'id'=> 'correct'));
+ $data13 = array(array('x'=>1, 'y'=>0, 'id'=> 'partial'), array('x'=>2, 'y'=>$stats->partial, 'id'=> 'partial'),
+ array('x'=>3, 'y'=>0, 'id'=> 'partial'), array('x'=>4, 'y'=>0, 'id'=> 'partial'),
+ array('x'=>5, 'y'=>0, 'id'=> 'partial'));
+ $data14 = array(array('x'=>1, 'y'=>0, 'id'=> 'false'), array('x'=>2, 'y'=>0, 'id'=> 'false'),
+ array('x'=>3, 'y'=>$stats->wrong, 'id'=> 'false'), array('x'=>4, 'y'=>0, 'id'=> 'false'),
+ array('x'=>5, 'y'=>0, 'id'=> 'false'));
+ $data15 = array(array('x'=>1, 'y'=>0, 'id'=> 'noanswer'), array('x'=>2, 'y'=>0, 'id'=> 'noanswer'),
+ array('x'=>3, 'y'=>0, 'id'=> 'noanswer'), array('x'=>4, 'y'=>$stats->noanswer, 'id'=> 'noanswer'),
+ array('x'=>5, 'y'=>0, 'id'=> 'noanswer'));
+ $data16 = array(array('x'=>1, 'y'=>0, 'id'=> 'known'), array('x'=>2, 'y'=>0, 'id'=> 'known'),
+ array('x'=>3, 'y'=>0, 'id'=> 'known'), array('x'=>4, 'y'=>0, 'id'=> 'known'),
+ array('x'=>5, 'y'=>$stats->known, 'id'=> 'known'));
+ $data17 = array(array('x'=>1, 'y'=>0, 'id'=> 'miscon'), array('x'=>2, 'y'=>0, 'id'=> 'miscon'),
+ array('x'=>3, 'y'=>0, 'id'=> 'miscon'), array('x'=>4, 'y'=>0, 'id'=> 'miscon'),
+ array('x'=>5, 'y'=>$stats->miscon, 'id'=> 'miscon'));
+ $data18 = array(array('x'=>1, 'y'=>0, 'id'=> 'problem'), array('x'=>2, 'y'=>0, 'id'=> 'problem'),
+ array('x'=>3, 'y'=>0, 'id'=> 'problem'), array('x'=>4, 'y'=>0, 'id'=> 'problem'),
+ array('x'=>5, 'y'=>$stats->wrong, 'id'=> 'problem'));
+ $dataseta = $object->create_bardataset('score', get_string('score','report_openlearner'), $data11);
+ $object->set_bardataset('correct', get_string('correctanswers','report_openlearner'), $data12, $dataseta);
+ $object->set_bardataset('partial', get_string('partial','report_openlearner'), $data13, $dataseta);
+ $object->set_bardataset('false', get_string('false','report_openlearner'), $data14, $dataseta);
+ $object->set_bardataset('noanswer', get_string('noanswer','report_openlearner'), $data15, $dataseta);
+ $object->set_bardataset('known', get_string('knowntopics','report_openlearner'), $data16, $dataseta);
+ $object->set_bardataset('miscon', get_string('misconceptions','report_openlearner'), $data17, $dataseta);
+ $object->set_bardataset('problem', get_string('problematic','report_openlearner'), $data18, $dataseta);
+ // create the 1st plot as smoothed area chart using the 1st dataset
+ $plota =& $object->add_barplot($plotareaa, 'stacked', $dataseta);
+ // create a fill array
+ $fillarraya =& $object->create_farray();
+ $object->add_color($fillarraya, 'chartreuse', 'score');
+ $object->add_color($fillarraya, 'blue', 'correct');
+ $object->add_color($fillarraya, 'dodgerblue', 'partial');
+ $object->add_color($fillarraya, 'red', 'false');
+ $object->add_color($fillarraya, 'crimson', 'noanswer');
+ $object->add_color($fillarraya, 'ivory', 'known');
+ $object->add_color($fillarraya, 'silver', 'miscon');
+ $object->add_color($fillarraya, 'gray', 'problem');
+ // set a line color
+ $object->set_line_color($plota, 'gray');
+ // set a standard fill style
+ $object->set_fill_style($plota, $fillarraya);
+ // create a Y data value marker for the 1st plot
+ $markera =& $object->create_barmarker($plota, 0, 0);
+ $object->set_marker($plota, $markera, 10);
+ $object->set_data_selector($plota);
+ // create the dataset for 2nd plot
+ $datasetb =& $object->create_piedataset();
+ $object->set_piedataset(get_string('calculated','quiz'), $stats->calculated, 'calculated', $datasetb);
+ $object->set_piedataset(get_string('essay','quiz'), $stats->essay, 'essay', $datasetb);
+ $object->set_piedataset(get_string('match','quiz'), $stats->match, 'match', $datasetb);
+ $object->set_piedataset(get_string('multianswer','quiz'), $stats->multianswer, 'multianswer', $datasetb);
+ $object->set_piedataset(get_string('multichoice','quiz'), $stats->multichoice, 'multichoice', $datasetb);
+ $object->set_piedataset(get_string('numerical','quiz'), $stats->numerical, 'numerical', $datasetb);
+ $object->set_piedataset(get_string('random','quiz'), $stats->random, 'random', $datasetb);
+ $object->set_piedataset(get_string('short','quiz'), $stats->short, 'short', $datasetb);
+ $object->set_piedataset(get_string('truefalse','quiz'), $stats->truefalse, 'truefalse', $datasetb);
+ // create the 2nd plot as smoothed area chart using the 2nd dataset
+ $plotb =& $object->add_pieplot($plotareab, $datasetb);
+ // hide plot area axis
+ $object->hide_axis($plotareab);
+ // create a Y data value marker
+ $pointingmarker =& $object->create_piemarker($plotb, 20);
+ // and use the marker on the 2nd plot
+ $object->set_marker($plotb, $pointingmarker, 10);
+ $object->set_radius($plotb, 2);
+ // create a fill array
+ $fillarrayb =& $object->create_farray();
+ $object->add_color($fillarrayb, 'orchid','calculated');
+ $object->add_color($fillarrayb, 'turqoise','essay');
+ $object->add_color($fillarrayb, 'yellow','match');
+ $object->add_color($fillarrayb, 'pink','multianswer');
+ $object->add_color($fillarrayb, 'orange','multichoice');
+ $object->add_color($fillarrayb, 'limegreen','numerical');
+ $object->add_color($fillarrayb, 'aliceblue','random');
+ $object->add_color($fillarrayb, 'khaki','short');
+ $object->add_color($fillarrayb, 'aqua','truefalse');
+ // set a line color
+ $object->set_line_color($plotb, 'gray');
+ // set a standard fill style
+ $object->set_fill_style($plotb, $fillarrayb);
+ $object->set_plotoptions(&$plotb, 9, 90);
+ // output the graph
+ $object->draw($graph, $filename);
+}
+?>
Index: lib/pear/Image/Graph/Tool.php
===================================================================
RCS file: lib/pear/Image/Graph/Tool.php
diff -N lib/pear/Image/Graph/Tool.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Tool.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,291 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Tool.php,v 1.4 2005/09/14 20:27:24 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * This class contains a set of tool-functions.
+ *
+ * These functions are all to be called statically
+ *
+ * @category Images
+ * @package Image_Graph
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Tool
+{
+
+ /**
+ * Return the average of 2 points
+ *
+ * @param double P1 1st point
+ * @param double P2 2nd point
+ * @return double The average of P1 and P2
+ * @static
+ */
+ function mid($p1, $p2)
+ {
+ return ($p1 + $p2) / 2;
+ }
+
+ /**
+ * Mirrors P1 in P2 by a amount of Factor
+ *
+ * @param double $p1 1st point, point to mirror
+ * @param double $o2 2nd point, mirror point
+ * @param double $factor Mirror factor, 0 returns $p2, 1 returns a pure
+ * mirror, ie $p1 on the exact other side of $p2
+ * @return double $p1 mirrored in $p2 by Factor
+ * @static
+ */
+ function mirror($p1, $p2, $factor = 1)
+ {
+ return $p2 + $factor * ($p2 - $p1);
+ }
+
+ /**
+ * Calculates a Bezier control point, this function must be called for BOTH
+ * X and Y coordinates (will it work for 3D coordinates!?)
+ *
+ * @param double $p1 1st point
+ * @param double $p2 Point to
+ * @param double $factor Mirror factor, 0 returns P2, 1 returns a pure
+ * mirror, i.e. P1 on the exact other side of P2
+ * @return double P1 mirrored in P2 by Factor
+ * @static
+ */
+ function controlPoint($p1, $p2, $factor, $smoothFactor = 0.75)
+ {
+ $sa = Image_Graph_Tool::mirror($p1, $p2, $smoothFactor);
+ $sb = Image_Graph_Tool::mid($p2, $sa);
+
+ $m = Image_Graph_Tool::mid($p2, $factor);
+
+ $pC = Image_Graph_Tool::mid($sb, $m);
+
+ return $pC;
+ }
+
+ /**
+ * Calculates a Bezier point, this function must be called for BOTH X and Y
+ * coordinates (will it work for 3D coordinates!?)
+ *
+ * @param double $t A position between $p2 and $p3, value between 0 and 1
+ * @param double $p1 Point to use for calculating control points
+ * @param double $p2 Point 1 to calculate bezier curve between
+ * @param double $p3 Point 2 to calculate bezier curve between
+ * @param double $p4 Point to use for calculating control points
+ * @return double The bezier value of the point t between $p2 and $p3 using
+ * $p1 and $p4 to calculate control points
+ * @static
+ */
+ function bezier($t, $p1, $p2, $p3, $p4)
+ {
+ // (1-t)^3*p1 + 3*(1-t)^2*t*p2 + 3*(1-t)*t^2*p3 + t^3*p4
+ return pow(1 - $t, 3) * $p1 +
+ 3 * pow(1 - $t, 2) * $t * $p2 +
+ 3 * (1 - $t) * pow($t, 2) * $p3 +
+ pow($t, 3) * $p4;
+ }
+
+ /**
+ * For a given point (x,y) return a point rotated by a given angle aroung the center (xy,yc)
+ *
+ * @param int $x x coordinate of the point to rotate
+ * @param int $y y coordinate of the point to rotate
+ * @param int $xc x coordinate of the center of the rotation
+ * @param int $yc y coordinate of the center of the rotation
+ * @param int $angle angle of the rotation
+ * @return array the coordinate of the new point
+ * @static
+ */
+ function rotate($x, $y, $xc, $yc, $angle)
+ {
+ $cos = cos(deg2rad($angle));
+ $sin = sin(deg2rad($angle));
+ $xr= $x - $xc;
+ $yr= $y - $yc;
+ $x1= $xc + $cos * $xr - $sin * $yr;
+ $y1= $yc + $sin * $xr + $cos * $yr;
+ return array((int) $x1,(int) $y1);
+ }
+
+ /**
+ * If a number is close 0 zero (i.e. 0 within $decimal decimals) it is rounded down to zero
+ *
+ * @param double $value The value to round
+ * @param int $decimal The number of decimals
+ * @return double The value or zero if "close enough" to zero
+ * @static
+ */
+ function close2zero($value, $decimal)
+ {
+ if (abs($value) < pow(10, -$decimal)) {
+ return 0;
+ }
+ else {
+ return $value;
+ }
+ }
+
+ /**
+ * Calculate the dimensions and center point (of gravity) for an arc
+ *
+ * @param int $v1 The angle at which the arc starts
+ * @param int $v2 The angle at which the arc ends
+ * @return array An array with the dimensions in a fraction of a circle width radius 1 'rx', 'ry' and the
+ * center point of gravity ('cx', 'cy')
+ * @static
+ */
+ function calculateArcDimensionAndCenter($v1, $v2)
+ {
+ // $v2 always larger than $v1
+ $r1x = Image_Graph_Tool::close2zero(cos(deg2rad($v1)), 3);
+ $r2x = Image_Graph_Tool::close2zero(cos(deg2rad($v2)), 3);
+
+ $r1y = Image_Graph_Tool::close2zero(sin(deg2rad($v1)), 3);
+ $r2y = Image_Graph_Tool::close2zero(sin(deg2rad($v2)), 3);
+
+ // $rx = how many percent of the x-diameter of the entire ellipse does the arc x-diameter occupy: 1 entire width, 0 no width
+ // $cx = at what percentage of the diameter does the center lie
+
+ // if the arc passes through 0/360 degrees the "highest" of r1x and r2x is replaced by 1!
+ if ((($v1 <= 0) && ($v2 >= 0)) || (($v1 <= 360) && ($v2 >= 360))) {
+ $r1x = min($r1x, $r2x);
+ $r2x = 1;
+ }
+
+ // if the arc passes through 180 degrees the "lowest" of r1x and r2x is replaced by -1!
+ if ((($v1 <= 180) && ($v2 >= 180)) || (($v1 <= 540) && ($v2 >= 540))) {
+ $r1x = max($r1x, $r2x);
+ $r2x = -1;
+ }
+
+ if ($r1x >= 0) { // start between [270; 360] or [0; 90]
+ if ($r2x >= 0) {
+ $rx = max($r1x, $r2x) / 2;
+ $cx = 0; // center lies 0 percent along this "vector"
+ }
+ else {
+ $rx = abs($r1x - $r2x) / 2;
+ $cx = abs($r2x / 2) / $rx;
+ }
+ }
+ else { // start between ]90; 270[
+ if ($r2x < 0) {
+ $rx = max(abs($r1x), abs($r2x)) / 2;
+ $cx = $rx;
+ }
+ else {
+ $rx = abs($r1x - $r2x) / 2;
+ $cx = abs($r1x / 2) / $rx;
+ }
+ }
+
+ // $ry = how many percent of the y-diameter of the entire ellipse does the arc y-diameter occupy: 1 entire, 0 none
+ // $cy = at what percentage of the y-diameter does the center lie
+
+ // if the arc passes through 90 degrees the "lowest" of r1x and r2x is replaced by -1!
+ if ((($v1 <= 90) && ($v2 >= 90)) || (($v1 <= 450) && ($v2 >= 450))) {
+ $r1y = min($r1y, $r2y);
+ $r2y = 1;
+ }
+
+ // if the arc passes through 270 degrees the "highest" of r1y and r2y is replaced by -1!
+ if ((($v1 <= 270) && ($v2 >= 270)) || (($v1 <= 630) && ($v2 >= 630))) {
+ $r1y = max($r1y, $r2y);
+ $r2y = -1;
+ }
+
+ if ($r1y >= 0) { // start between [0; 180]
+ if ($r2y >= 0) {
+ $ry = max($r1y, $r2y) / 2;
+ $cy = 0; // center lies 0 percent along this "vector"
+ }
+ else {
+ $ry = abs($r1y - $r2y) / 2;
+ $cy = abs($r2y / 2) / $ry;
+ }
+ }
+ else { // start between ]180; 360[
+ if ($r2y < 0) {
+ $ry = max(abs($r1y), abs($r2y)) / 2;
+ $cy = $ry;
+ }
+ else {
+ $ry = abs($r1y - $r2y) / 2;
+ $cy = abs($r1y / 2) / $ry;
+ }
+ }
+
+ return array(
+ 'rx' => $rx,
+ 'cx' => $cx,
+ 'ry' => $ry,
+ 'cy' => $cy
+ );
+ }
+
+ /**
+ * Calculate linear regression on a dataset
+ * @param array $data The data to calculate regression upon
+ * @return array The slope and intersection of the "best-fit" line
+ * @static
+ */
+ function calculateLinearRegression(&$data)
+ {
+ $sumX = 0;
+ $sumY = 0;
+ foreach ($data as $point) {
+ $sumX += $point['X'];
+ $sumY += $point['Y'];
+ }
+ $meanX = $sumX / count($data);
+ $meanY = $sumY / count($data);
+
+ $sumXX = 0;
+ $sumYY = 0;
+ $sumXY = 0;
+ foreach ($data as $point) {
+ $sumXX += ($point['X'] - $meanX) * ($point['X'] - $meanX);
+ $sumYY += ($point['Y'] - $meanY) * ($point['Y'] - $meanY);
+ $sumXY += ($point['X'] - $meanX) * ($point['Y'] - $meanY);
+ }
+
+ $result = array();
+ $result['slope'] = ($sumXY / $sumXX);
+ $result['intersection'] = $meanY - ($result['slope'] * $meanX);
+ return $result;
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Marker/Circle.php
===================================================================
RCS file: lib/pear/Image/Graph/Marker/Circle.php
diff -N lib/pear/Image/Graph/Marker/Circle.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Marker/Circle.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,96 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Circle.php,v 1.6 2005/08/03 21:21:54 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Marker.php
+ */
+require_once 'Image/Graph/Marker.php';
+
+/**
+ * Data marker as circle (require GD2)
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Marker
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Marker_Circle extends Image_Graph_Marker
+{
+
+ /**
+ * The 'size' of the marker, the meaning depends on the specific Marker
+ * implementation
+ * @var int
+ * @access private
+ */
+ var $_size = 10;
+
+ /**
+ * Draw the marker on the canvas
+ *
+ * @param int $x The X (horizontal) position (in pixels) of the marker on
+ * the canvas
+ * @param int $y The Y (vertical) position (in pixels) of the marker on the
+ * canvas
+ * @param array $values The values representing the data the marker 'points' to
+ * @access private
+ */
+ function _drawMarker($x, $y, $values = false)
+ {
+ $this->_getFillStyle();
+ $this->_getLineStyle();
+
+ $dA = 2*pi()/($this->_size*2);
+ $angle = 0;
+ while ($angle < 2*pi()) {
+ $this->_canvas->addVertex(array('x' =>
+ $x + $this->_size*cos($angle), 'y' =>
+ $y - $this->_size*sin($angle)
+ ));
+ $angle += $dA;
+ }
+
+ $this->_canvas->addVertex(array('x' =>
+ $x + $this->_size*cos(0), 'y' =>
+ $y - $this->_size*sin(0)
+ ));
+
+ $this->_canvas->polygon(array('connect' => true));
+
+ parent::_drawMarker($x, $y, $values);
+ }
+
+}
+
+?>
Index: mod/quiz/openlearner/default.php
===================================================================
RCS file: mod/quiz/openlearner/default.php
diff -N mod/quiz/openlearner/default.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod/quiz/openlearner/default.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,49 @@
+id" or q=$quiz->id", "attempt=$attempt->uniqueid" and "mode=reportname".
+
+// Included by ../openlearner.php
+
+class openlearner_default_report {
+
+ function display($quiz=NULL, $cm=NULL, $course=NULL) { // This function just displays the report
+ return true;
+ }
+
+ function print_header_and_tabs($cm, $course, $quiz, $attemptno=0, $reportmode="overview", $meta=""){
+ global $CFG;
+
+ // Define some strings
+ $strquizzes = get_string("modulenameplural", "quiz");
+ $strquiz = get_string("modulename", "quiz");
+ /// Print the page header
+ $navigation = build_navigation('', $cm);
+ print_header_simple(format_string($quiz->name), "", $navigation,'', $meta, true, update_module_button($cm->id, $course->id, $strquiz), navmenu($course, $cm));
+ // Print the tabs
+ $currenttab = 'openlearner';
+ $mode = $reportmode;
+ require($CFG->dirroot . '/mod/quiz/tabs.php');
+ // change cron value for quiz module
+ $record = get_record_select('modules', "name = 'quiz'");
+ if ($record->cron == 0 and $record->visible == 1) {
+ $record->cron = 600;
+ update_record('modules', $record); // Cron should check this module every 10 minutes
+ }
+ $course_context = get_context_instance(CONTEXT_COURSE, $course->id);
+ if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
+ echo '';
+ }
+
+ }
+}
+?>
Index: lib/pear/Image/Graph/Constants.php
===================================================================
RCS file: lib/pear/Image/Graph/Constants.php
diff -N lib/pear/Image/Graph/Constants.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Constants.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,225 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Constants.php,v 1.7 2005/08/03 21:21:52 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Font.php
+ */
+require_once 'Image/Graph/Font.php';
+
+// Constant declarations
+
+/**
+ * Defines an X (horizontal) axis
+ */
+define('IMAGE_GRAPH_AXIS_X', 1);
+
+/**
+ * Defines an Y (vertical) axis
+ */
+define('IMAGE_GRAPH_AXIS_Y', 2);
+
+/**
+ * Defines an Y (vertical) axis
+ */
+define('IMAGE_GRAPH_AXIS_Y_SECONDARY', 3);
+
+/**
+ * Defines an horizontal (X) axis
+ */
+define('IMAGE_GRAPH_AXIS_HORIZONTAL', 1);
+
+/**
+ * Defines an vertical (Y) axis
+ */
+define('IMAGE_GRAPH_AXIS_VERTICAL', 2);
+
+/**
+ * Define if label should be shown for axis minimum value
+ */
+define('IMAGE_GRAPH_LABEL_MINIMUM', 1);
+
+/**
+ * Define if label should be shown for axis 0 (zero) value
+ */
+define('IMAGE_GRAPH_LABEL_ZERO', 2);
+
+/**
+ * Define if label should be shown for axis maximum value
+ */
+define('IMAGE_GRAPH_LABEL_MAXIMUM', 4);
+
+/**
+ * Defines a horizontal gradient fill
+ */
+define('IMAGE_GRAPH_GRAD_HORIZONTAL', 1);
+
+/**
+ * Defines a vertical gradient fill
+ */
+define('IMAGE_GRAPH_GRAD_VERTICAL', 2);
+
+/**
+ * Defines a horizontally mirrored gradient fill
+ */
+define('IMAGE_GRAPH_GRAD_HORIZONTAL_MIRRORED', 3);
+
+/**
+ * Defines a vertically mirrored gradient fill
+ */
+define('IMAGE_GRAPH_GRAD_VERTICAL_MIRRORED', 4);
+
+/**
+ * Defines a diagonal gradient fill from top-left to bottom-right
+ */
+define('IMAGE_GRAPH_GRAD_DIAGONALLY_TL_BR', 5);
+
+/**
+ * Defines a diagonal gradient fill from bottom-left to top-right
+ */
+define('IMAGE_GRAPH_GRAD_DIAGONALLY_BL_TR', 6);
+
+/**
+ * Defines a radial gradient fill
+ */
+define('IMAGE_GRAPH_GRAD_RADIAL', 7);
+
+/**
+ * Defines the default builtin font
+ */
+define('IMAGE_GRAPH_FONT', 1);
+
+/**
+ * Defines a X value should be used
+ */
+define('IMAGE_GRAPH_VALUE_X', 0);
+
+/**
+ * Defines a Y value should be used
+ */
+define('IMAGE_GRAPH_VALUE_Y', 1);
+
+/**
+ * Defines a min X% value should be used
+ */
+define('IMAGE_GRAPH_PCT_X_MIN', 2);
+
+/**
+ * Defines a max X% value should be used
+ */
+define('IMAGE_GRAPH_PCT_X_MAX', 3);
+
+/**
+ * Defines a min Y% value should be used
+ */
+define('IMAGE_GRAPH_PCT_Y_MIN', 4);
+
+/**
+ * Defines a max Y% value should be used
+ */
+define('IMAGE_GRAPH_PCT_Y_MAX', 5);
+
+/**
+ * Defines a total Y% value should be used
+ */
+define('IMAGE_GRAPH_PCT_Y_TOTAL', 6);
+
+/**
+ * Defines a ID value should be used
+ */
+define('IMAGE_GRAPH_POINT_ID', 7);
+
+/**
+ * Align text left
+ */
+define('IMAGE_GRAPH_ALIGN_LEFT', 0x1);
+
+/**
+ * Align text right
+ */
+define('IMAGE_GRAPH_ALIGN_RIGHT', 0x2);
+
+/**
+ * Align text center x (horizontal)
+ */
+define('IMAGE_GRAPH_ALIGN_CENTER_X', 0x4);
+
+/**
+ * Align text top
+ */
+define('IMAGE_GRAPH_ALIGN_TOP', 0x8);
+
+/**
+ * Align text bottom
+ */
+define('IMAGE_GRAPH_ALIGN_BOTTOM', 0x10);
+
+/**
+ * Align text center y (vertical)
+ */
+define('IMAGE_GRAPH_ALIGN_CENTER_Y', 0x20);
+
+/**
+ * Align text center (both x and y)
+ */
+define('IMAGE_GRAPH_ALIGN_CENTER', IMAGE_GRAPH_ALIGN_CENTER_X + IMAGE_GRAPH_ALIGN_CENTER_Y);
+
+/**
+ * Align text top left
+ */
+define('IMAGE_GRAPH_ALIGN_TOP_LEFT', IMAGE_GRAPH_ALIGN_TOP + IMAGE_GRAPH_ALIGN_LEFT);
+
+/**
+ * Align text top right
+ */
+define('IMAGE_GRAPH_ALIGN_TOP_RIGHT', IMAGE_GRAPH_ALIGN_TOP + IMAGE_GRAPH_ALIGN_RIGHT);
+
+/**
+ * Align text bottom left
+ */
+define('IMAGE_GRAPH_ALIGN_BOTTOM_LEFT', IMAGE_GRAPH_ALIGN_BOTTOM + IMAGE_GRAPH_ALIGN_LEFT);
+
+/**
+ * Align text bottom right
+ */
+define('IMAGE_GRAPH_ALIGN_BOTTOM_RIGHT', IMAGE_GRAPH_ALIGN_BOTTOM + IMAGE_GRAPH_ALIGN_RIGHT);
+
+/**
+ * Align vertical
+ */
+define('IMAGE_GRAPH_ALIGN_VERTICAL', IMAGE_GRAPH_ALIGN_TOP);
+
+/**
+ * Align horizontal
+ */
+define('IMAGE_GRAPH_ALIGN_HORIZONTAL', IMAGE_GRAPH_ALIGN_LEFT);
+
+// Error codes
+define('IMAGE_GRAPH_ERROR_GENERIC', 0);
+
+?>
Index: lib/pear/Image/Graph/Font.php
===================================================================
RCS file: lib/pear/Image/Graph/Font.php
diff -N lib/pear/Image/Graph/Font.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Font.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,158 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Font.php,v 1.8 2005/08/24 20:35:55 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Common.php
+ */
+require_once 'Image/Graph/Common.php';
+
+/**
+ * A font.
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Text
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ * @abstract
+ */
+class Image_Graph_Font extends Image_Graph_Common
+{
+
+ /**
+ * The name of the font
+ * @var string
+ * @access private
+ */
+ var $_name = false;
+
+ /**
+ * The angle of the output
+ * @var int
+ * @access private
+ */
+ var $_angle = false;
+
+ /**
+ * The size of the font
+ * @var int
+ * @access private
+ */
+ var $_size = 11;
+
+ /**
+ * The color of the font
+ * @var Color
+ * @access private
+ */
+ var $_color = 'black';
+
+ /**
+ * Image_Graph_Font [Constructor]
+ */
+ function Image_Graph_Font($name = false, $size = false)
+ {
+ parent::Image_Graph_Common();
+ if ($name !== false) {
+ $this->_name = $name;
+ }
+ if ($size !== false) {
+ $this->_size = $size;
+ }
+ }
+
+ /**
+ * Set the color of the font
+ *
+ * @param mixed $color The color object of the Font
+ */
+ function setColor($color)
+ {
+ $this->_color = $color;
+ }
+
+ /**
+ * Set the angle slope of the output font.
+ *
+ * 0 = normal, 90 = bottom and up, 180 = upside down, 270 = top and down
+ *
+ * @param int $angle The angle in degrees to slope the text
+ */
+ function setAngle($angle)
+ {
+ $this->_angle = $angle;
+ }
+
+ /**
+ * Set the size of the font
+ *
+ * @param int $size The size in pixels of the font
+ */
+ function setSize($size)
+ {
+ $this->_size = $size;
+ }
+
+ /**
+ * Get the font 'array'
+ *
+ * @return array The font 'summary' to pass to the canvas
+ * @access private
+ */
+ function _getFont($options = false)
+ {
+ if ($options === false) {
+ $options = array();
+ }
+
+ if ($this->_name !== false) {
+ $options['name'] = $this->_name;
+ }
+
+ if (!isset($options['color'])) {
+ $options['color'] = $this->_color;
+ }
+
+ if (!isset($options['size'])) {
+ $options['size'] = $this->_size;
+ }
+
+ if ((!isset($options['angle'])) && ($this->_angle !== false)) {
+ $options['angle'] = $this->_angle;
+ }
+ return $options;
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Dataset/Random.php
===================================================================
RCS file: lib/pear/Image/Graph/Dataset/Random.php
diff -N lib/pear/Image/Graph/Dataset/Random.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Dataset/Random.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,77 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Random.php,v 1.6 2005/08/24 20:35:57 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Dataset/Trivial.php
+ */
+require_once 'Image/Graph/Dataset/Trivial.php';
+
+/**
+ * Random data set, points are generated by random.
+ *
+ * This dataset is mostly (if not solely) used for demo-purposes.
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Dataset
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Dataset_Random extends Image_Graph_Dataset_Trivial
+{
+
+ /**
+ * RandomDataset [Constructor]
+ *
+ * @param int $count The number of points to create
+ * @param double $minimum The minimum value the random set can be
+ * @param double $maximum The maximum value the random set can be
+ * @param bool $includeZero Whether 0 should be included or not as an X
+ * value, may be omitted, default: false
+ */
+ function Image_Graph_Dataset_Random($count, $minimum, $maximum, $includeZero = false)
+ {
+ parent::Image_Graph_Dataset_Trivial();
+ $i = 0;
+ while ($i < $count) {
+ $this->addPoint(
+ $ixc = ($includeZero ? $i : $i +1),
+ rand($minimum, $maximum)
+ );
+ $i ++;
+ }
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Grid/Polar.php
===================================================================
RCS file: lib/pear/Image/Graph/Grid/Polar.php
diff -N lib/pear/Image/Graph/Grid/Polar.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Grid/Polar.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,111 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Polar.php,v 1.10 2005/08/24 20:36:04 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ * @since File available since Release 0.3.0dev2
+ */
+
+/**
+ * Include file Image/Graph/Grid.php
+ */
+require_once 'Image/Graph/Grid.php';
+
+/**
+ * Display a line grid on the plotarea.
+ *
+ * {@link Image_Graph_Grid}
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Grid
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ * @since Class available since Release 0.3.0dev2
+ */
+class Image_Graph_Grid_Polar extends Image_Graph_Grid
+{
+
+ /**
+ * GridLines [Constructor]
+ */
+ function Image_Graph_Grid_Polar()
+ {
+ parent::Image_Graph_Grid();
+ $this->_lineStyle = 'lightgrey';
+ }
+
+ /**
+ * Output the grid
+ *
+ * @return bool Was the output 'good' (true) or 'bad' (false).
+ * @access private
+ */
+ function _done()
+ {
+ if (parent::_done() === false) {
+ return false;
+ }
+
+ if (!$this->_primaryAxis) {
+ return false;
+ }
+
+ $this->_canvas->startGroup(get_class($this));
+
+ $value = false;
+
+ $p0 = array ('X' => '#min#', 'Y' => '#min#');
+ if ($this->_primaryAxis->_type == IMAGE_GRAPH_AXIS_Y) {
+ $p1 = array ('X' => '#min#', 'Y' => '#max#');
+ $r0 = abs($this->_pointY($p1) - $this->_pointY($p0));
+ } else {
+ $p1 = array ('X' => '#max#', 'Y' => '#min#');
+ $r0 = abs($this->_pointX($p1) - $this->_pointX($p0));
+ }
+
+ $cx = $this->_pointX($p0);
+ $cy = $this->_pointY($p0);
+
+ $span = $this->_primaryAxis->_axisSpan;
+
+ while (($value = $this->_primaryAxis->_getNextLabel($value)) !== false) {
+ $r = $r0 * ($value - $this->_primaryAxis->_getMinimum()) / $span;
+
+ $this->_getLineStyle();
+ $this->_canvas->ellipse(array('x' => $cx, 'y' => $cy, 'rx' => $r, 'ry' => $r));
+ }
+
+ $this->_canvas->endGroup();
+
+ return true;
+ }
+
+}
+?>
Index: lib/pear/Image/Graph/Dataset.php
===================================================================
RCS file: lib/pear/Image/Graph/Dataset.php
diff -N lib/pear/Image/Graph/Dataset.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Dataset.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,483 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Dataset.php,v 1.10 2005/08/24 20:35:55 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+
+/**
+ * Data set used to represent a data collection to plot in a chart
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Dataset
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ * @abstract
+ */
+class Image_Graph_Dataset
+{
+
+ /**
+ * The pointer of the data set
+ * @var int
+ * @access private
+ */
+ var $_posX = 0;
+
+ /**
+ * The minimum X value of the dataset
+ * @var int
+ * @access private
+ */
+ var $_minimumX = 0;
+
+ /**
+ * The maximum X value of the dataset
+ * @var int
+ * @access private
+ */
+ var $_maximumX = 0;
+
+ /**
+ * The minimum Y value of the dataset
+ * @var int
+ * @access private
+ */
+ var $_minimumY = 0;
+
+ /**
+ * The maximum Y value of the dataset
+ * @var int
+ * @access private
+ */
+ var $_maximumY = 0;
+
+ /**
+ * The number of points in the dataset
+ * @var int
+ * @access private
+ */
+ var $_count = 0;
+
+ /**
+ * The name of the dataset, used for legending
+ * @var string
+ * @access private
+ */
+ var $_name = '';
+
+ /**
+ * Image_Graph_Dataset [Constructor]
+ */
+ function Image_Graph_Dataset()
+ {
+ }
+
+ /**
+ * Sets the name of the data set, used for legending
+ *
+ * @param string $name The name of the dataset
+ */
+ function setName($name)
+ {
+ $this->_name = $name;
+ }
+
+ /**
+ * Add a point to the dataset
+ *
+ * $ID can contain either the ID of the point, i.e. 'DK', 123, 'George', etc. or it can contain
+ * values used for creation of the HTML image map. This is achieved using is an an associated array
+ * with the following values:
+ *
+ * 'url' The URL to create the link to
+ *
+ * 'alt' [optional] The alt text on the link
+ *
+ * 'target' [optional] The target of the link
+ *
+ * 'htmltags' [optional] An associated array with html tags (tag as key), fx. 'onMouseOver' => 'history.go(-1);', 'id' => 'thelink'
+ *
+ * @param int $x The X value to add
+ * @param int $y The Y value to add, can be omited
+ * @param var $ID The ID of the point
+ */
+ function addPoint($x, $y = false, $ID = false)
+ {
+ if ($y !== null) {
+ if (is_array($y)) {
+ $maxY = max($y);
+ $minY = min($y);
+ } else {
+ $maxY = $y;
+ $minY = $y;
+ }
+ }
+
+ if ($this->_count) {
+ $this->_minimumX = min($x, $this->_minimumX);
+ $this->_maximumX = max($x, $this->_maximumX);
+ if ($y !== null) {
+ $this->_minimumY = min($minY, $this->_minimumY);
+ $this->_maximumY = max($maxY, $this->_maximumY);
+ }
+ } else {
+ $this->_minimumX = $x;
+ $this->_maximumX = $x;
+ if ($y !== null) {
+ $this->_minimumY = $minY;
+ $this->_maximumY = $maxY;
+ }
+ }
+
+ $this->_count++;
+ }
+
+ /**
+ * The number of values in the dataset
+ *
+ * @return int The number of values in the dataset
+ */
+ function count()
+ {
+ return $this->_count;
+ }
+
+ /**
+ * Gets a X point from the dataset
+ *
+ * @param var $x The variable to return an X value from, fx in a vector
+ * function data set
+ * @return var The X value of the variable
+ * @access private
+ */
+ function _getPointX($x)
+ {
+ return $x;
+ }
+
+ /**
+ * Gets a Y point from the dataset
+ *
+ * @param var $x The variable to return an Y value from, fx in a vector
+ * function data set
+ * @return var The Y value of the variable
+ * @access private
+ */
+ function _getPointY($x)
+ {
+ return $x;
+ }
+
+ /**
+ * Gets a ID from the dataset
+ *
+ * @param var $x The variable to return an Y value from, fx in a vector
+ * function data set
+ * @return var The ID value of the variable
+ * @access private
+ */
+ function _getPointID($x)
+ {
+ return false;
+ }
+
+ /**
+ * Gets point data from the dataset
+ *
+ * @param var $x The variable to return an Y value from, fx in a vector
+ * function data set
+ * @return array The data for the point
+ * @access private
+ */
+ function _getPointData($x)
+ {
+ return false;
+ }
+
+ /**
+ * The minimum X value
+ *
+ * @return var The minimum X value
+ */
+ function minimumX()
+ {
+ return $this->_minimumX;
+ }
+
+ /**
+ * The maximum X value
+ *
+ * @return var The maximum X value
+ */
+ function maximumX()
+ {
+ return $this->_maximumX;
+ }
+
+ /**
+ * The minimum Y value
+ *
+ * @return var The minimum Y value
+ */
+ function minimumY()
+ {
+ return $this->_minimumY;
+ }
+
+ /**
+ * The maximum Y value
+ *
+ * @return var The maximum Y value
+ */
+ function maximumY()
+ {
+ return $this->_maximumY;
+ }
+
+ /**
+ * The first point
+ *
+ * @return array The last point
+ */
+ function first()
+ {
+ return array('X' => $this->minimumX(), 'Y' => $this->minimumY());
+ }
+
+ /**
+ * The last point
+ *
+ * @return array The first point
+ */
+ function last()
+ {
+ return array('X' => $this->maximumX(), 'Y' => $this->maximumY());
+ }
+
+ /**
+ * The minimum X value
+ *
+ * @param var $value The minimum X value
+ * @access private
+ */
+ function _setMinimumX($value)
+ {
+ $this->_minimumX = $value;
+ }
+
+ /**
+ * The maximum X value
+ *
+ * @param var $value The maximum X value
+ * @access private
+ */
+ function _setMaximumX($value)
+ {
+ $this->_maximumX = $value;
+ }
+
+ /**
+ * The minimum Y value
+ *
+ * @param var $value The minimum X value
+ * @access private
+ */
+ function _setMinimumY($value)
+ {
+ $this->_minimumY = $value;
+ }
+
+ /**
+ * The maximum Y value
+ *
+ * @param var $value The maximum X value
+ * @access private
+ */
+ function _setMaximumY($value)
+ {
+ $this->_maximumY = $value;
+ }
+
+ /**
+ * The interval between 2 adjacent X values
+ *
+ * @return var The interval
+ * @access private
+ */
+ function _stepX()
+ {
+ return 1;
+ }
+
+ /**
+ * The interval between 2 adjacent Y values
+ *
+ * @return var The interval
+ * @access private
+ */
+ function _stepY()
+ {
+ return 1;
+ }
+
+ /**
+ * Reset the intertal dataset pointer
+ *
+ * @return var The first X value
+ * @access private
+ */
+ function _reset()
+ {
+ $this->_posX = $this->_minimumX;
+ return $this->_posX;
+ }
+
+ /**
+ * Get a point close to the internal pointer
+ *
+ * @param int Step Number of points next to the internal pointer, negative
+ * Step is towards lower X values, positive towards higher X values
+ * @return array The point
+ * @access private
+ */
+ function _nearby($step = 0)
+ {
+ $x = $this->_getPointX($this->_posX + $this->_stepX() * $step);
+ $y = $this->_getPointY($this->_posX + $this->_stepX() * $step);
+ $ID = $this->_getPointID($this->_posX + $this->_stepX() * $step);
+ $data = $this->_getPointData($this->_posX + $this->_stepX() * $step);
+ if (($x === false) || ($y === false)) {
+ return false;
+ } else {
+ return array ('X' => $x, 'Y' => $y, 'ID' => $ID, 'data' => $data);
+ }
+ }
+
+ /**
+ * Get the next point the internal pointer refers to and advance the pointer
+ *
+ * @return array The next point
+ * @access private
+ */
+ function _next()
+ {
+ if ($this->_posX > $this->_maximumX) {
+ return false;
+ }
+
+ $x = $this->_getPointX($this->_posX);
+ $y = $this->_getPointY($this->_posX);
+ $ID = $this->_getPointID($this->_posX);
+ $data = $this->_getPointData($this->_posX);
+ $this->_posX += $this->_stepX();
+
+ return array ('X' => $x, 'Y' => $y, 'ID' => $ID, 'data' => $data);
+ }
+
+ /**
+ * Get the average of the dataset's Y points
+ *
+ * @return var The Y-average across the dataset
+ * @access private
+ */
+ function _averageY()
+ {
+ $posX = $this->_minimumX;
+ $count = 0;
+ $total = 0;
+ while ($posX < $this->_maximumX) {
+ $count ++;
+ $total += $this->_getPointY($posX);
+ $posX += $this->_stepX();
+ }
+
+ if ($count != 0) {
+ return $total / $count;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the median of the array passed Y points
+ *
+ * @param array $data The data-array to get the median from
+ * @param int $quartile The quartile to return the median from
+ * @return var The Y-median across the dataset from the specified quartile
+ * @access private
+ */
+ function _median($data, $quartile = 'second')
+ {
+ sort($data);
+ $point = (count($data) - 1) / 2;
+
+ if ($quartile == 'first') {
+ $lowPoint = 0;
+ $highPoint = floor($point);
+ } elseif ($quartile == 'third') {
+ $lowPoint = round($point);
+ $highPoint = count($data) - 1;
+ } else {
+ $lowPoint = 0;
+ $highPoint = count($data) - 1;
+ }
+
+ $point = ($lowPoint + $highPoint) / 2;
+
+ if (floor($point) != $point) {
+ $point = floor($point);
+ return ($data[$point] + $data[($point + 1)]) / 2;
+ } else {
+ return $data[$point];
+ }
+ }
+
+ /**
+ * Get the median of the datasets Y points
+ *
+ * @param int $quartile The quartile to return the median from
+ * @return var The Y-median across the dataset from the specified quartile
+ * @access private
+ */
+ function _medianY($quartile = 'second')
+ {
+ $pointsY = array();
+ $posX = $this->_minimumX;
+ while ($posX <= $this->_maximumX) {
+ $pointsY[] = $this->_getPointY($posX);
+ $posX += $this->_stepX();
+ }
+ return $this->_median($pointsY, $quartile);
+ }
+
+}
+?>
Index: lib/pear/Image/Graph/Layout/Vertical.php
===================================================================
RCS file: lib/pear/Image/Graph/Layout/Vertical.php
diff -N lib/pear/Image/Graph/Layout/Vertical.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Layout/Vertical.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,108 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Vertical.php,v 1.6 2005/02/21 20:49:55 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Layout/Horizontal.php
+ */
+require_once 'Image/Graph/Layout/Horizontal.php';
+
+/**
+ * Layout for displaying two elements on top of each other.
+ *
+ * This splits the area contained by this element in two on top of each other
+ * by a specified percentage (relative to the top). A layout can be nested.
+ * Fx. a {@link Image_Graph_Layout_Horizontal} can layout two VerticalLayout's to
+ * make a 2 by 2 matrix of 'element-areas'.
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Layout
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Layout_Vertical extends Image_Graph_Layout_Horizontal
+{
+
+ /**
+ * Gets the absolute size of one of the parts.
+ *
+ * @param string $part The name of the part - auto_part(1|2)
+ * @return int The number of pixels the edge should be pushed
+ * @since 0.3.0dev2
+ * @access private
+ */
+ function _getAbsolute(&$part)
+ {
+ $part1Size = $this->_part1->_getAutoSize();
+ $part2Size = $this->_part2->_getAutoSize();
+ $this->_percentage = false;
+ if (($part1Size !== false) and ($part2Size !== false)) {
+ $height = $this->_fillHeight() * $part1Size / ($part1Size + $part2Size);
+ } elseif ($part1Size !== false) {
+ $height = $part1Size;
+ } elseif ($part2Size !== false) {
+ $height = -$part2Size;
+ } else {
+ $height = $this->_fillHeight() / 2;
+ }
+
+ if ($part == 'auto_part2') {
+// $height = $this->_fillHeight() - $height;
+ }
+
+ return $height;
+ }
+
+ /**
+ * Splits the layout between the parts, by the specified percentage
+ *
+ * @access private
+ */
+ function _split()
+ {
+ if (($this->_part1) && ($this->_part2)) {
+ if ($this->_percentage !== false) {
+ $split1 = 100 - $this->_percentage;
+ $split2 = $this->_percentage;
+ $this->_part1->_push('bottom', "$split1%");
+ $this->_part2->_push('top', "$split2%");
+ } else {
+ $this->_part1->_push('bottom', 'auto_part1');
+ $this->_part2->_push('top', 'auto_part2');
+ }
+ }
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Title.php
===================================================================
RCS file: lib/pear/Image/Graph/Title.php
diff -N lib/pear/Image/Graph/Title.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Title.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,194 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Title.php,v 1.12 2005/08/24 20:35:56 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Layout.php
+ */
+require_once 'Image/Graph/Layout.php';
+
+/**
+ * Title
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Text
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Title extends Image_Graph_Layout
+{
+
+ /**
+ * The text to print
+ * @var string
+ * @access private
+ */
+ var $_text;
+
+ /**
+ * The font to use
+ * @var Font
+ * @access private
+ */
+ var $_font;
+
+ /**
+ * The alignment of the title
+ * @var int
+ * @access private
+ */
+ var $_alignment = IMAGE_GRAPH_ALIGN_CENTER_X;
+
+ /**
+ * Create the title.
+ *
+ * Pass a Image_Graph_Font object - preferably by-ref (&) as second
+ * parameter, the font size in pixels or an associated array with some or
+ * all of the followin keys:
+ *
+ * 'size' The size of the title
+ *
+ * 'angle' The angle at which to write the title (in degrees or 'vertical')
+ *
+ * 'color' The font-face color
+ *
+ * @param sting $text The text to represent the title
+ * @param mixed $fontOptions The font to use in the title
+ */
+ function Image_Graph_Title($text, $fontOptions = false)
+ {
+ parent::Image_Graph_Layout();
+ if (is_object($fontOptions)) {
+ $this->_font =& $fontOptions;
+ } else {
+ if (is_array($fontOptions)) {
+ $this->_fontOptions = $fontOptions;
+ } else {
+ $this->_fontOptions['size'] = $fontOptions;
+ }
+ }
+ $this->setText($text);
+ }
+
+ /**
+ * Set the text
+ *
+ * @param string $text The text to display
+ */
+ function setText($text)
+ {
+ $this->_text = $text;
+ }
+
+ /**
+ * Returns the calculated "auto" size
+ *
+ * @return int The calculated auto size
+ * @access private
+ */
+ function _getAutoSize()
+ {
+ if ($this->_defaultFontOptions !== false) {
+ $this->_canvas->setFont($this->_defaultFontOptions);
+ } else {
+ $this->_canvas->setFont($this->_getFont());
+ }
+
+ return $this->_canvas->textHeight($this->_text);
+ }
+
+ /**
+ * Set the alignment of the legend
+ *
+ * @param int $alignment The alignment
+ */
+ function setAlignment($alignment)
+ {
+ $this->_alignment = $alignment & 0x7;
+ }
+
+ /**
+ * Output the text
+ *
+ * @return bool Was the output 'good' (true) or 'bad' (false).
+ * @access private
+ */
+ function _done()
+ {
+ if ($this->_defaultFontOptions !== false) {
+ $this->_canvas->setFont($this->_defaultFontOptions);
+ } else {
+ $this->_canvas->setFont($this->_getFont());
+ }
+
+ if (is_a($this->_parent, 'Image_Graph_Plotarea')) {
+ $this->_setCoords(
+ $this->_parent->_left,
+ $this->_parent->_top,
+ $this->_parent->_right,
+ $this->_parent->_top + $this->_canvas->textHeight($this->_text)
+ );
+ } elseif (!is_a($this->_parent, 'Image_Graph_Layout')) {
+ $this->_setCoords(
+ $this->_parent->_fillLeft(),
+ $this->_parent->_fillTop(),
+ $this->_parent->_fillRight(),
+ $this->_parent->_fillTop() + $this->_canvas->textHeight($this->_text)
+ );
+ }
+
+ if (parent::_done() === false) {
+ return false;
+ }
+
+ if ($this->_alignment == IMAGE_GRAPH_ALIGN_CENTER_X) {
+ $x = ($this->_left + $this->_right) / 2;
+ } elseif ($this->_alignment == IMAGE_GRAPH_ALIGN_LEFT) {
+ $x = $this->_left;
+ } else {
+ $x = $this->_right;
+ }
+ $y = ($this->_top + $this->_bottom) / 2;
+
+ $this->write(
+ $x,
+ $y,
+ $this->_text,
+ $this->_alignment + IMAGE_GRAPH_ALIGN_CENTER_Y
+ );
+ return true;
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Axis.php
===================================================================
RCS file: lib/pear/Image/Graph/Axis.php
diff -N lib/pear/Image/Graph/Axis.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Axis.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,1690 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Axis.php,v 1.35 2006/02/28 22:48:07 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Plotarea/Element.php
+ */
+require_once 'Image/Graph/Plotarea/Element.php';
+
+/**
+ * Diplays a normal linear axis (either X- or Y-axis).
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Axis
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+ class Image_Graph_Axis extends Image_Graph_Plotarea_Element
+{
+
+ /**
+ * The type of the axis, possible values are:
+ *
+ * - IMAGE_GRAPH_AXIS_X / IMAGE_GRAPH_AXIS_HORIZONTAL
+ *
- IMAGE_GRAPH_AXIS_Y / IMAGE_GRAPH_AXIS_VERTICAL /
+ * IMAGE_GRAPH_AXIS_Y_SECONDARY
+ *
+ * @var int
+ * @access private
+ */
+ var $_type;
+
+ /**
+ * The minimum value the axis displays
+ * @var int
+ * @access private
+ */
+ var $_minimum = false;
+
+ /**
+ * The minimum value the axis has been explicitly set by the user
+ * @var bool
+ * @access private
+ */
+ var $_minimumSet = false;
+
+ /**
+ * The maximum value the axis displays
+ * @var int
+ * @access private
+ */
+ var $_maximum = false;
+
+ /**
+ * The maximum value the axis has been explicitly set by the user
+ * @var bool
+ * @access private
+ */
+ var $_maximumSet = false;
+
+ /**
+ * The value span of the axis.
+ * This is primarily included for performance reasons
+ * @var double
+ * @access private
+ */
+ var $_axisSpan = false;
+
+ /**
+ * The value span of the axis.
+ * This is primarily included for performance reasons
+ * @var double
+ * @access private
+ */
+ var $_axisValueSpan = false;
+
+ /**
+ * The axis padding.
+ * The index 'low' specifies the padding for the low axis values (when not
+ * inverted), i.e. to the left on an x-axis and on the bottom of an y-axis,
+ * vice versa for 'high'.
+ *
+ * Axis padding does not make sense on a normal linear y-axis with a 'y-min'
+ * of 0 since this corresponds to displaying a small part of the y-axis
+ * below 0!
+ *
+ * @var array
+ * @access private
+ */
+ var $_axisPadding = array('low' => 0, 'high' => 0);
+
+ /**
+ * The number of "pixels" representing 1 unit on the axis
+ *
+ * This is primarily included for performance reasons
+ * @var double
+ * @access private
+ */
+ var $_delta = false;
+
+ /**
+ * Specify if the axis should label the minimum value
+ * @var bool
+ * @access private
+ */
+ var $_showLabelMinimum = true;
+
+ /**
+ * Specify if the axis should label 0 (zero)
+ * @var bool
+ * @access private
+ */
+ var $_showLabelZero = false;
+
+ /**
+ * Specify if the axis should label the maximum value
+ * @var bool
+ * @access private
+ */
+ var $_showLabelMaximum = true;
+
+ /**
+ * Show arrow heads at the 'end' of the axis, default: false
+ * @var bool
+ * @access private
+ */
+ var $_showArrow = false;
+
+ /**
+ * Intersection data of axis
+ * @var array
+ * @access private
+ */
+ var $_intersect = array('value' => 'default', 'axis' => 'default');
+
+ /**
+ * The fixed size of the axis (i.e. width for y-axis, height for x-axis)
+ * @var mixed
+ * @access private
+ */
+ var $_fixedSize = false;
+
+ /**
+ * The label options
+ *
+ * Should text be shows, preferences for ticks. The indexes start at level
+ * 1, which is chosen for readability
+ * @var array
+ * @access private
+ */
+ var $_labelOptions = array(
+ 1 => array(
+ 'interval' => 1,
+ 'type' => 'auto',
+ 'tick' => array(
+ 'start' => -2,
+ 'end' => 2,
+ 'color' => false // default color
+ ),
+ 'showtext' => true,
+ 'showoffset' => false,
+ 'font' => array(),
+ 'offset' => 0,
+ 'position' => 'outside',
+ )
+ );
+
+ /**
+ * The labels that are shown.
+ *
+ * This is used to make values show only once...
+ * @access private
+ */
+ var $_labelText = array();
+
+ /**
+ * A data preprocessor for formatting labels, fx showing dates as a standard
+ * date instead of Unix time stamp
+ * @var Image_Graph_DatePreProcessor
+ * @access private
+ * @see Image_Graph_DataPreProcessor
+ */
+ var $_dataPreProcessor = null;
+
+ /**
+ * Point marked in the axis
+ * @var array
+ * @access private
+ */
+ var $_marks = array();
+
+ /**
+ * Specifies whether the values should be 'pushed' by 0.5
+ * @var bool
+ * @access private
+ */
+ var $_pushValues = false;
+
+ /**
+ * The title of this axis
+ * @var string
+ * @access private
+ */
+ var $_title = '';
+
+ /**
+ * The font used for the title of this axis
+ * @var Image_Graph_Font
+ * @access private
+ */
+ var $_titleFont = false;
+
+ /**
+ * Invert the axis (i.e. if an y-axis normally displays minimum values at
+ * the bottom, they are not displayed at the top
+ * @var bool
+ * @access private
+ * @since 0.3.0dev3
+ */
+ var $_invert = false;
+
+ /**
+ * Transpose the axis (i.e. is a normal y-axis transposed, so thats it's not show
+ * vertically as normally expected, but instead horizontally)
+ * @var bool
+ * @access private
+ */
+ var $_transpose = false;
+
+ /**
+ * Image_Graph_Axis [Constructor].
+ * Normally a manual creation should not be necessary, axis are created
+ * automatically by the {@link Image_Graph_Plotarea} constructor unless
+ * explicitly defined otherwise
+ *
+ * @param int $type The type (direction) of the Axis, use IMAGE_GRAPH_AXIS_X
+ * for an X-axis (default, may be omitted) and IMAGE_GRAPH_AXIS_Y for Y-
+ * axis)
+ */
+ function Image_Graph_Axis($type = IMAGE_GRAPH_AXIS_X)
+ {
+ parent::Image_Graph_Element();
+ $this->_type = $type;
+ $this->_fillStyle = 'black';
+ }
+
+ /**
+ * Push the values by 0.5 (for bar and step chart)
+ *
+ * @access private
+ */
+ function _pushValues()
+ {
+ $this->_pushValues = true;
+ }
+
+ /**
+ * Sets the axis padding for a given position ('low' or 'high')
+ * @param string $where The position
+ * @param int $value The number of pixels to "pad"
+ * @access private
+ */
+ function _setAxisPadding($where, $value)
+ {
+ $this->_axisPadding[$where] = $value;
+ }
+
+ /**
+ * Gets the font of the title.
+ *
+ * If not font has been set, the parent font is propagated through it's
+ * children.
+ *
+ * @return array An associated array used for canvas
+ * @access private
+ */
+ function _getTitleFont()
+ {
+ if ($this->_titleFont === false) {
+ if ($this->_defaultFontOptions !== false) {
+ return $this->_defaultFontOptions;
+ } else {
+ return $this->_getFont();
+ }
+ } else {
+ if (is_object($this->_titleFont)) {
+ return $this->_titleFont->_getFont();
+ } elseif (is_array($this->_titleFont)) {
+ return $this->_getFont($this->_titleFont);
+ } elseif (is_int($this->_titleFont)) {
+ return $this->_getFont(array('size' => $this->_titleFont));
+ }
+ }
+ return array();
+ }
+
+ /**
+ * Shows a label for the the specified values.
+ *
+ * Allowed values are combinations of:
+ *
+ * - IMAGE_GRAPH_LABEL_MINIMUM
+ *
- IMAGE_GRAPH_LABEL_ZERO
+ *
- IMAGE_GRAPH_LABEL_MAXIMUM
+ *
+ * By default none of these are shows on the axis
+ *
+ * @param int $value The values to show labels for
+ */
+ function showLabel($value)
+ {
+ $this->_showLabelMinimum = ($value & IMAGE_GRAPH_LABEL_MINIMUM);
+ $this->_showLabelZero = ($value & IMAGE_GRAPH_LABEL_ZERO);
+ $this->_showLabelMaximum = ($value & IMAGE_GRAPH_LABEL_MAXIMUM);
+ }
+
+ /**
+ * Sets a data preprocessor for formatting the axis labels
+ *
+ * @param Image_Graph_DataPreprocessor $dataPreProcessor The data preprocessor
+ * @see Image_Graph_DataPreprocessor
+ */
+ function setDataPreProcessor(& $dataPreProcessor)
+ {
+ $this->_dataPreProcessor =& $dataPreProcessor;
+ }
+
+ /**
+ * Gets the minimum value the axis will show
+ *
+ * @return double The minumum value
+ * @access private
+ */
+ function _getMinimum()
+ {
+ return $this->_minimum;
+ }
+
+ /**
+ * Gets the maximum value the axis will show
+ *
+ * @return double The maximum value
+ * @access private
+ */
+ function _getMaximum()
+ {
+ return $this->_maximum;
+ }
+
+ /**
+ * Sets the minimum value the axis will show
+ *
+ * @param double $minimum The minumum value to use on the axis
+ * @access private
+ */
+ function _setMinimum($minimum)
+ {
+ if ($this->_minimum === false) {
+ $this->forceMinimum($minimum, false);
+ } else {
+ $this->forceMinimum(min($this->_minimum, $minimum), false);
+ }
+ }
+
+ /**
+ * Sets the maximum value the axis will show
+ *
+ * @param double $maximum The maximum value to use on the axis
+ * @access private
+ */
+ function _setMaximum($maximum)
+ {
+ if ($this->_maximum === false) {
+ $this->forceMaximum($maximum, false);
+ } else {
+ $this->forceMaximum(max($this->_maximum, $maximum), false);
+ }
+ }
+
+ /**
+ * Forces the minimum value of the axis
+ *
+ * @param double $minimum The minumum value to use on the axis
+ * @param bool $userEnforce This value should not be set, used internally
+ */
+ function forceMinimum($minimum, $userEnforce = true)
+ {
+ if (($userEnforce) || (!$this->_minimumSet)) {
+ $this->_minimum = $minimum;
+ $this->_minimumSet = $userEnforce;
+ }
+ $this->_calcLabelInterval();
+ }
+
+ /**
+ * Forces the maximum value of the axis
+ *
+ * @param double $maximum The maximum value to use on the axis
+ * @param bool $userEnforce This value should not be set, used internally
+ */
+ function forceMaximum($maximum, $userEnforce = true)
+ {
+ if (($userEnforce) || (!$this->_maximumSet)) {
+ $this->_maximum = $maximum;
+ $this->_maximumSet = $userEnforce;
+ }
+ $this->_calcLabelInterval();
+ }
+
+ /**
+ * Show an arrow head on the 'end' of the axis
+ */
+ function showArrow()
+ {
+ $this->_showArrow = true;
+ }
+
+ /**
+ * Do not show an arrow head on the 'end' of the axis (default)
+ */
+ function hideArrow()
+ {
+ $this->_showArrow = false;
+ }
+
+ /**
+ * Return the label distance.
+ *
+ * @param int $level The label level to return the distance of
+ * @return int The distance between 2 adjacent labels
+ * @access private
+ */
+ function _labelDistance($level = 1)
+ {
+ $l1 = $this->_getNextLabel(false, $level);
+ $l2 = $this->_getNextLabel($l1, $level);;
+ return abs($this->_point($l2) - $this->_point($l1));
+ }
+
+ /**
+ * Sets an interval for when labels are shown on the axis.
+ *
+ * By default 'auto' is used, forcing the axis to calculate a approximate
+ * best label interval to be used. Specify an array to use user-defined
+ * values for labels.
+ *
+ * @param mixed $labelInterval The interval with which labels are shown
+ * @param int $level The label level to set the interval on
+ */
+ function setLabelInterval($labelInterval = 'auto', $level = 1)
+ {
+ if (!isset($this->_labelOptions[$level])) {
+ $this->_labelOptions[$level] = array();
+ }
+
+ if ($labelInterval === 'auto') {
+ $this->_labelOptions[$level]['type'] = 'auto';
+ $this->_calcLabelInterval();
+ } else {
+ $this->_labelOptions[$level]['type'] = 'manual';
+ $this->_labelOptions[$level]['interval'] = $labelInterval;
+ }
+ }
+
+ /**
+ * Sets options for the label at a specific level.
+ *
+ * Possible options are:
+ *
+ * 'showtext' true or false whether label text should be shown or not
+ *
+ * 'showoffset' should the label be shown at an offset, i.e. should the
+ * label be shown at a position so that it does not overlap with prior
+ * levels. Only applies to multilevel labels with text
+ *
+ * 'font' The font options as an associated array
+ *
+ * 'position' The position at which the labels are written ('inside' or
+ * 'outside' the axis). NB! This relative position only applies to the
+ * default location of the axis, i.e. if an x-axis is inverted then
+ * 'outside' still refers to the "left" side of a normal y-axis (since this
+ * is normally 'outside') but the actual output will be labels on the
+ * "inside"!
+ *
+ * 'format' To format the label text according to a sprintf statement
+ *
+ * 'dateformat' To format the label as a date, fx. j. M Y = 29. Jun 2005
+ *
+ * @param string $option The label option name (see detailed description
+ * for possible values)
+ * @param mixed $value The value for the option
+ * @param int $level The label level to set the interval on
+ */
+ function setLabelOption($option, $value, $level = 1)
+ {
+ if (!isset($this->_labelOptions[$level])) {
+ $this->_labelOptions[$level] = array('type' => 'auto');
+ }
+
+ $this->_labelOptions[$level][$option] = $value;
+ }
+
+ /**
+ * Sets options for the label at a specific level.
+ *
+ * The possible options are specified in {@link Image_Graph_Axis::
+ * setLabelOption()}.
+ *
+ * @param array $options An assciated array with label options
+ * @param int $level The label level to set the interval on
+ */
+ function setLabelOptions($options, $level = 1)
+ {
+ if (is_array($options)) {
+ if (isset($this->_labelOptions[$level])) {
+ $this->_labelOptions[$level] = array_merge($this->_labelOptions[$level], $options);
+ } else {
+ $this->_labelOptions[$level] = $options;
+ }
+
+ }
+ }
+
+ /**
+ * Sets the title of this axis.
+ *
+ * This is used as an alternative (maybe better) method, than using layout's
+ * for axis-title generation.
+ *
+ * To use the current propagated font, but just set it vertically, simply
+ * pass 'vertical' as second parameter for vertical alignment down-to-up or
+ * 'vertical2' for up-to-down alignment.
+ *
+ * @param string $title The title of this axis
+ * @param Image_Graph_Font $font The font used for the title
+ * @since 0.3.0dev2
+ */
+ function setTitle($title, $font = false)
+ {
+ $this->_title = $title;
+ if ($font === 'vertical') {
+ $this->_titleFont = array('vertical' => true, 'angle' => 90);
+ } elseif ($font === 'vertical2') {
+ $this->_titleFont = array('vertical' => true, 'angle' => 270);
+ } else {
+ $this->_titleFont =& $font;
+ }
+ }
+
+ /**
+ * Sets a fixed "size" for the axis.
+ *
+ * If the axis is any type of y-axis the size relates to the width of the
+ * axis, if an x-axis is concerned the size is the height.
+ *
+ * @param int $size The fixed size of the axis
+ * @since 0.3.0dev5
+ */
+ function setFixedSize($size)
+ {
+ $this->_fixedSize = $size;
+ }
+
+ /**
+ * Preprocessor for values, ie for using logarithmic axis
+ *
+ * @param double $value The value to preprocess
+ * @return double The preprocessed value
+ * @access private
+ */
+ function _value($value)
+ {
+ return $value - $this->_getMinimum() + ($this->_pushValues ? 0.5 : 0);
+ }
+
+ /**
+ * Apply the dataset to the axis
+ *
+ * @param Image_Graph_Dataset $dataset The dataset
+ * @access private
+ */
+ function _applyDataset(&$dataset)
+ {
+ if ($this->_type == IMAGE_GRAPH_AXIS_X) {
+ $this->_setMinimum($dataset->minimumX());
+ $this->_setMaximum($dataset->maximumX());
+ } else {
+ $this->_setMinimum($dataset->minimumY());
+ $this->_setMaximum($dataset->maximumY());
+ }
+ }
+
+ /**
+ * Get the pixel position represented by a value on the canvas
+ *
+ * @param double $value the value to get the pixel-point for
+ * @return double The pixel position along the axis
+ * @access private
+ */
+ function _point($value)
+ {
+ if ((($this->_type == IMAGE_GRAPH_AXIS_X) && (!$this->_transpose)) ||
+ (($this->_type != IMAGE_GRAPH_AXIS_X) && ($this->_transpose)))
+ {
+ if ($this->_invert) {
+ return max($this->_left, $this->_right - $this->_axisPadding['high'] - $this->_delta * $this->_value($value));
+ } else {
+ return min($this->_right, $this->_left + $this->_axisPadding['low'] + $this->_delta * $this->_value($value));
+ }
+ } else {
+ if ($this->_invert) {
+ return min($this->_bottom, $this->_top + $this->_axisPadding['high'] + $this->_delta * $this->_value($value));
+ } else {
+ return max($this->_top, $this->_bottom - $this->_axisPadding['low'] - $this->_delta * $this->_value($value));
+ }
+ }
+ }
+
+
+ /**
+ * Get the axis intersection pixel position
+ *
+ * This is only to be called prior to output! I.e. between the user
+ * invokation of Image_Graph::done() and any actual output is performed.
+ * This is because it can change the axis range.
+ *
+ * @param double $value the intersection value to get the pixel-point for
+ * @return double The pixel position along the axis
+ * @access private
+ */
+ function _intersectPoint($value)
+ {
+
+ if (($value === 'min') || ($value < $this->_getMinimum())) {
+ if ($this->_type == IMAGE_GRAPH_AXIS_X) {
+ if ($this->_invert) {
+ return ($this->_transpose ? $this->_top : $this->_right);
+ } else {
+ return ($this->_transpose ? $this->_bottom : $this->_left);
+ }
+ } else {
+ if ($this->_invert) {
+ return ($this->_transpose ? $this->_right : $this->_top);
+ } else {
+ return ($this->_transpose ? $this->_left : $this->_bottom);
+ }
+ }
+ } elseif (($value === 'max') || ($value > $this->_getMaximum())) {
+ if ($this->_type == IMAGE_GRAPH_AXIS_X) {
+ if ($this->_invert) {
+ return ($this->_transpose ? $this->_bottom : $this->_left);
+ } else {
+ return ($this->_transpose ? $this->_top : $this->_right);
+ }
+ } else {
+ if ($this->_invert) {
+ return ($this->_transpose ? $this->_left : $this->_bottom);
+ } else {
+ return ($this->_transpose ? $this->_right : $this->_top);
+ }
+ }
+ }
+
+ return $this->_point($value);
+ }
+
+ /**
+ * Calculate the delta value (the number of pixels representing one unit
+ * on the axis)
+ *
+ * @return double The label interval
+ * @access private
+ */
+ function _calcDelta()
+ {
+ if ($this->_axisValueSpan == 0) {
+ $this->_delta = false;
+ } elseif ($this->_type == IMAGE_GRAPH_AXIS_X) {
+ $this->_delta = (($this->_transpose ? $this->height() : $this->width()) - ($this->_axisPadding['low'] + $this->_axisPadding['high'])) / ($this->_axisValueSpan + ($this->_pushValues ? 1 : 0));
+ } else {
+ $this->_delta = (($this->_transpose ? $this->width() : $this->height()) - ($this->_axisPadding['low'] + $this->_axisPadding['high'])) / ($this->_axisValueSpan + ($this->_pushValues ? 1 : 0));
+ }
+ }
+
+ /**
+ * Calculate the label interval
+ *
+ * If explicitly defined this will be calucated to an approximate best.
+ *
+ * @return double The label interval
+ * @access private
+ */
+ function _calcLabelInterval()
+ {
+ $min = $this->_getMinimum();
+ $max = $this->_getMaximum();
+
+ $this->_axisValueSpan = $this->_axisSpan = abs($max - $min);
+
+ if ((!empty($min)) && (!empty($max)) && ($min > $max)) {
+ $this->_labelOptions[1]['interval'] = 1;
+ return true;
+ }
+
+ $span = 0;
+ foreach($this->_labelOptions as $level => $labelOptions) {
+ if ((!isset($labelOptions['type'])) || ($labelOptions['type'] !== 'auto')) {
+ $span = false;
+ } elseif ($level == 1) {
+ $span = $this->_axisValueSpan;
+ } else {
+ $l1 = $this->_getNextLabel(false, $level - 1);
+ $l2 = $this->_getNextLabel($l1, $level - 1);
+ if ((!is_numeric($l1)) || (!is_numeric($l2))) {
+ $span == false;
+ } else {
+ $span = $l2 - $l1;
+ }
+ }
+
+ if ($span !== false) {
+ $interval = pow(10, floor(log10($span)));
+
+ if ($interval == 0) {
+ $interval = 1;
+ }
+
+ if ((($span) / $interval) < 3) {
+ $interval = $interval / 4;
+ } elseif ((($span) / $interval) < 5) {
+ $interval = $interval / 2;
+ } elseif ((($span) / $interval) > 10) {
+ $interval = $interval * 2;
+ }
+
+ if (($interval -floor($interval) == 0.5) && ($interval != 0.5)) {
+ $interval = floor($interval);
+ }
+
+ // just to be 100% sure that an interval of 0 is not returned some
+ // additional checks are performed
+ if ($interval == 0) {
+ $interval = ($span) / 5;
+ }
+
+ if ($interval == 0) {
+ $interval = 1;
+ }
+
+ $this->_labelOptions[$level]['interval'] = $interval;
+ }
+ }
+ }
+
+ /**
+ * Get next label point
+ *
+ * @param doubt $currentLabel The current label, if omitted or false, the
+ * first is returned
+ * @param int $level The label level to get the next label from
+ * @return double The next label point
+ * @access private
+ */
+ function _getNextLabel($currentLabel = false, $level = 1)
+ {
+ if (!isset($this->_labelOptions[$level])) {
+ return false;
+ }
+
+ if (is_array($this->_labelOptions[$level]['interval'])) {
+ if ($currentLabel === false) {
+ reset($this->_labelOptions[$level]['interval']);
+ }
+
+ if (list(, $label) = each($this->_labelOptions[$level]['interval'])) {
+ return $label;
+ } else {
+ return false;
+ }
+ } else {
+ $li = $this->_labelInterval($level);
+ if (($this->_axisSpan == 0) || ($this->_axisValueSpan == 0) ||
+ ($li == 0)
+ ) {
+ return false;
+ }
+
+ $labelInterval = $this->_axisSpan / ($this->_axisValueSpan / $li);
+
+ if ($labelInterval == 0) {
+ return false;
+ }
+
+ if ($currentLabel === false) {
+ $label = ((int) ($this->_getMinimum() / $labelInterval)) *
+ $labelInterval - $labelInterval;
+ while ($label < $this->_getMinimum()) {
+ $label += $labelInterval;
+ }
+ return $label;
+ } else {
+ if ($currentLabel + $labelInterval > $this->_getMaximum()) {
+ return false;
+ } else {
+ return $currentLabel + $labelInterval;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the interval with which labels are shown on the axis.
+ *
+ * If explicitly defined this will be calucated to an approximate best.
+ *
+ * @param int $level The label level to get the label interval for
+ * @return double The label interval
+ * @access private
+ */
+ function _labelInterval($level = 1)
+ {
+ if ((!isset($this->_labelOptions[$level])) ||
+ (!isset($this->_labelOptions[$level]['interval']))
+ ) {
+ return 1;
+ }
+
+ return (is_array($this->_labelOptions[$level]['interval'])
+ ? 1
+ : $this->_labelOptions[$level]['interval']
+ );
+ }
+
+ /**
+ * Get the size in pixels of the axis.
+ *
+ * For an x-axis this is the width of the axis including labels, and for an
+ * y-axis it is the corrresponding height
+ *
+ * @return int The size of the axis
+ * @access private
+ */
+ function _size()
+ {
+ if (!$this->_visible) {
+ return 0;
+ }
+
+ if ($this->_fixedSize !== false) {
+ return $this->_fixedSize;
+ }
+
+ krsort($this->_labelOptions);
+
+ $totalMaxSize = 0;
+
+ foreach ($this->_labelOptions as $level => $labelOptions) {
+ if ((isset($labelOptions['showoffset'])) && ($labelOptions['showoffset'] === true)) {
+ $this->_labelOptions[$level]['offset'] += $totalMaxSize;
+ } elseif (!isset($this->_labelOptions[$level]['offset'])) {
+ $this->_labelOptions[$level]['offset'] = 0;
+ }
+ if (
+ (isset($labelOptions['showtext'])) &&
+ ($labelOptions['showtext'] === true) &&
+ (
+ (!isset($labelOptions['position'])) ||
+ ($labelOptions['position'] == 'outside')
+ )
+ ) {
+ if (isset($labelOptions['font'])) {
+ $font = $this->_getFont($labelOptions['font']);
+ } else {
+ if ($this->_defaultFontOptions !== false) {
+ $font = $this->_defaultFontOptions;
+ } else {
+ $font = $this->_getFont();
+ }
+ }
+ $this->_canvas->setFont($font);
+
+ $value = false;
+ $maxSize = 0;
+ while (($value = $this->_getNextLabel($value, $level)) !== false) {
+ if ((abs($value) > 0.0001) && ($value > $this->_getMinimum()) &&
+ ($value < $this->_getMaximum()))
+ {
+ if (is_object($this->_dataPreProcessor)) {
+ $labelText = $this->_dataPreProcessor->_process($value);
+ } elseif (isset($labelOptions['format'])) {
+ $labelText = sprintf($labelOptions['format'], $value);
+ } elseif (isset($labelOptions['dateformat'])) {
+ $labelText = date($labelOptions['dateformat'], $value);
+ } else {
+ $labelText = $value;
+ }
+
+ if ((($this->_type == IMAGE_GRAPH_AXIS_X) && (!$this->_transpose)) ||
+ (($this->_type != IMAGE_GRAPH_AXIS_X) && ($this->_transpose)))
+ {
+ $maxSize = max($maxSize, $this->_canvas->textHeight($labelText));
+ } else {
+ $maxSize = max($maxSize, $this->_canvas->textWidth($labelText));
+ }
+ }
+ }
+ if ((isset($labelOptions['showoffset'])) && ($labelOptions['showoffset'] === true)) {
+ $totalMaxSize += $maxSize;
+ } else {
+ $totalMaxSize = max($totalMaxSize, $maxSize);
+ }
+ }
+ }
+
+ if ($this->_title) {
+ $this->_canvas->setFont($this->_getTitleFont());
+
+ if ((($this->_type == IMAGE_GRAPH_AXIS_X) && (!$this->_transpose)) ||
+ (($this->_type != IMAGE_GRAPH_AXIS_X) && ($this->_transpose)))
+ {
+ $totalMaxSize += $this->_canvas->textHeight($this->_title);
+ } else {
+ $totalMaxSize += $this->_canvas->textWidth($this->_title);
+ }
+ $totalMaxSize += 10;
+ }
+
+ return $totalMaxSize + 3;
+ }
+
+ /**
+ * Adds a mark to the axis at the specified value
+ *
+ * @param double $value The value
+ * @param double $value2 The second value (for a ranged mark)
+ */
+ function addMark($value, $value2 = false, $text = false)
+ {
+ if ($value2 === false) {
+ $this->_marks[] = $value;
+ } else {
+ $this->_marks[] = array($value, $value2);
+ }
+ }
+
+ /**
+ * Is the axis numeric or not?
+ *
+ * @return bool True if numeric, false if not
+ * @access private
+ */
+ function _isNumeric()
+ {
+ return true;
+ }
+
+ /**
+ * Set the major tick appearance.
+ *
+ * The positions are specified in pixels relative to the axis, meaning that
+ * a value of -1 for start will draw the major tick 'line' starting at 1
+ * pixel outside (negative) value the axis (i.e. below an x-axis and to the
+ * left of a normal y-axis).
+ *
+ * @param int $start The start position relative to the axis
+ * @param int $end The end position relative to the axis
+ * @param int $level The label level to set the tick options for
+ * @since 0.3.0dev2
+ */
+ function setTickOptions($start, $end, $level = 1)
+ {
+ if (!isset($this->_labelOptions[$level])) {
+ $this->_labelOptions[$level] = array();
+ }
+
+ $this->_labelOptions[$level]['tick'] = array(
+ 'start' => $start,
+ 'end' => $end
+ );
+ }
+
+ /**
+ * Invert the axis direction
+ *
+ * If the minimum values are normally displayed fx. at the bottom setting
+ * axis inversion to true, will cause the minimum values to be displayed at
+ * the top and maximum at the bottom.
+ *
+ * @param bool $invert True if the axis is to be inverted, false if not
+ * @since 0.3.0dev3
+ */
+ function setInverted($invert)
+ {
+ $this->_invert = $invert;
+ }
+
+ /**
+ * Set axis intersection.
+ *
+ * Sets the value at which the axis intersects other axis, fx. at which Y-
+ * value the x-axis intersects the y-axis (normally at 0).
+ *
+ * Possible values are 'default', 'min', 'max' or a number between axis min
+ * and max (the value will automatically be limited to this range).
+ *
+ * For a coordinate system with 2 y-axis, the x-axis can either intersect
+ * the primary or the secondary y-axis. To make the x-axis intersect the
+ * secondary y-axis at a given value pass IMAGE_GRAPH_AXIS_Y_SECONDARY as
+ * second parameter.
+ *
+ * @param mixed $intersection The value at which the axis intersect the
+ * 'other' axis
+ * @param mixed $axis The axis to intersect. Only applies to x-axis with
+ * both a primary and secondary y-axis available.
+ * @since 0.3.0dev2
+ */
+ function setAxisIntersection($intersection, $axis = 'default')
+ {
+ if ($axis == 'x') {
+ $axis = IMAGE_GRAPH_AXIS_X;
+ } elseif ($axis == 'y') {
+ $axis = IMAGE_GRAPH_AXIS_Y;
+ } elseif ($axis == 'ysec') {
+ $axis = IMAGE_GRAPH_AXIS_Y_SECONDARY;
+ }
+ $this->_intersect = array(
+ 'value' => $intersection,
+ 'axis' => $axis
+ );
+ }
+
+ /**
+ * Get axis intersection data.
+ *
+ * @return array An array with the axis intersection data.
+ * @since 0.3.0dev2
+ * @access private
+ */
+ function _getAxisIntersection()
+ {
+ $value = $this->_intersect['value'];
+ $axis = $this->_intersect['axis'];
+ if (($this->_type == IMAGE_GRAPH_AXIS_Y)
+ || ($this->_type == IMAGE_GRAPH_AXIS_Y_SECONDARY)
+ ) {
+ $axis = IMAGE_GRAPH_AXIS_X;
+ } elseif ($axis == 'default') {
+ $axis = IMAGE_GRAPH_AXIS_Y;
+ }
+
+ if ($value === 'default') {
+ switch ($this->_type) {
+ case IMAGE_GRAPH_AXIS_Y:
+ $value = 'min';
+ break;
+ case IMAGE_GRAPH_AXIS_Y_SECONDARY:
+ $value = 'max';
+ break;
+ case IMAGE_GRAPH_AXIS_X:
+ $value = 0;
+ break;
+ }
+ }
+
+ return array('value' => $value, 'axis' => $axis);
+ }
+
+ /**
+ * Resets the elements
+ *
+ * @access private
+ */
+ function _reset()
+ {
+ parent::_reset();
+ $this->_labelText = array();
+ }
+
+ /**
+ * Output an axis tick mark.
+ *
+ * @param int $value The value to output
+ * @param int $level The label level to draw the tick for
+ * @access private
+ */
+ function _drawTick($value, $level = 1)
+ {
+ if (isset($this->_labelOptions[$level])) {
+ $labelOptions = $this->_labelOptions[$level];
+ $labelPosition = $this->_point($value);
+
+ if (isset($labelOptions['offset'])) {
+ $offset = $labelOptions['offset'];
+ } else {
+ $offset = 0;
+ }
+
+ if ((isset($labelOptions['showtext'])) && ($labelOptions['showtext'] === true)) {
+ if (is_object($this->_dataPreProcessor)) {
+ $labelText = $this->_dataPreProcessor->_process($value);
+ } elseif (isset($labelOptions['format'])) {
+ $labelText = sprintf($labelOptions['format'], $value);
+ } elseif (isset($labelOptions['dateformat'])) {
+ $labelText = date($labelOptions['dateformat'], $value);
+ } else {
+ $labelText = $value;
+ }
+
+ if (!in_array($labelText, $this->_labelText)) {
+ $this->_labelText[] = $labelText;
+
+ if (isset($labelOptions['font'])) {
+ $font = $this->_getFont($labelOptions['font']);
+ } else {
+ if ($this->_defaultFontOptions !== false) {
+ $font = $this->_defaultFontOptions;
+ } else {
+ $font = $this->_getFont();
+ }
+ }
+ $this->_canvas->setFont($font);
+
+ if (
+ (isset($labelOptions['position'])) &&
+ ($labelOptions['position'] == 'inside')
+ ) {
+ $labelInside = true;
+ } else {
+ $labelInside = false;
+ }
+
+ if ($this->_type == IMAGE_GRAPH_AXIS_Y) {
+ if ($this->_transpose) {
+ if ($labelInside) {
+ $this->write(
+ $labelPosition,
+ $this->_top - 3 - $offset,
+ $labelText,
+ IMAGE_GRAPH_ALIGN_BOTTOM | IMAGE_GRAPH_ALIGN_CENTER_X,
+ $font
+ );
+ } else {
+ $this->write(
+ $labelPosition,
+ $this->_top + 6 + $offset + $font['size'] * (substr_count($labelText, "\n") + 1),
+ $labelText,
+ IMAGE_GRAPH_ALIGN_BOTTOM | IMAGE_GRAPH_ALIGN_CENTER_X,
+ $font
+ );
+ }
+ }
+ else {
+ if ($labelInside) {
+ $this->write(
+ $this->_right + 3 + $offset,
+ $labelPosition,
+ $labelText,
+ IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_LEFT,
+ $font
+ );
+ } else {
+ $this->write(
+ $this->_right - 3 - $offset,
+ $labelPosition,
+ $labelText,
+ IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_RIGHT,
+ $font
+ );
+ }
+ }
+ } elseif ($this->_type == IMAGE_GRAPH_AXIS_Y_SECONDARY) {
+ if ($this->_transpose) {
+ if ($labelInside) {
+ $this->write(
+ $labelPosition,
+ $this->_bottom + 6 + $offset + $font['size'] * (substr_count($labelText, "\n") + 1),
+ $labelText,
+ IMAGE_GRAPH_ALIGN_BOTTOM | IMAGE_GRAPH_ALIGN_CENTER_X,
+ $font
+ );
+ } else {
+ $this->write(
+ $labelPosition,
+ $this->_bottom - 3 - $offset,
+ $labelText,
+ IMAGE_GRAPH_ALIGN_BOTTOM | IMAGE_GRAPH_ALIGN_CENTER_X,
+ $font
+ );
+ }
+ }
+ else {
+ if ($labelInside) {
+ $this->write(
+ $this->_left - 3 - $offset,
+ $labelPosition,
+ $labelText,
+ IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_RIGHT,
+ $font
+ );
+ } else {
+ $this->write(
+ $this->_left + 3 + $offset,
+ $labelPosition,
+ $labelText,
+ IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_LEFT,
+ $font
+ );
+ }
+ }
+ } else {
+ if ($this->_transpose) {
+ if ($labelInside) {
+ $this->write(
+ $this->_right + 3 + $offset,
+ $labelPosition,
+ $labelText,
+ IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_LEFT,
+ $font
+ );
+ } else {
+ $this->write(
+ $this->_right - 3 - $offset,
+ $labelPosition,
+ $labelText,
+ IMAGE_GRAPH_ALIGN_CENTER_Y | IMAGE_GRAPH_ALIGN_RIGHT,
+ $font
+ );
+ }
+ }
+ else {
+ if ($labelInside === true) {
+ $this->write(
+ $labelPosition,
+ $this->_top - 3 - $offset,
+ $labelText,
+ IMAGE_GRAPH_ALIGN_CENTER_X | IMAGE_GRAPH_ALIGN_BOTTOM,
+ $font
+ );
+ } else {
+ $this->write(
+ $labelPosition,
+ $this->_top + 6 + $offset + $font['size'] * (substr_count($labelText, "\n") + 1),
+ $labelText,
+ IMAGE_GRAPH_ALIGN_CENTER_X | IMAGE_GRAPH_ALIGN_BOTTOM,
+ $font
+ );
+ }
+ }
+ }
+ }
+ }
+
+ $tickColor = false;
+ if (isset($this->_labelOptions[$level]['tick'])) {
+ if (isset($this->_labelOptions[$level]['tick']['start'])) {
+ $tickStart = $this->_labelOptions[$level]['tick']['start'];
+ } else {
+ $tickStart = false;
+ }
+
+ if (isset($this->_labelOptions[$level]['tick']['end'])) {
+ $tickEnd = $this->_labelOptions[$level]['tick']['end'];
+ } else {
+ $tickEnd = false;
+ }
+
+ if ((isset($this->_labelOptions[$level]['tick']['color'])) && ($this->_labelOptions[$level]['tick']['color'] !== false)) {
+ $tickColor = $this->_labelOptions[$level]['tick']['color'];
+ }
+ }
+
+ if ($tickStart === false) {
+ $tickStart = -2;
+ }
+
+ if ($tickEnd === false) {
+ $tickEnd = 2;
+ }
+
+ if ($tickColor !== false) {
+ $this->_canvas->setLineColor($tickColor);
+ }
+ else {
+ $this->_getLineStyle();
+ }
+
+ if ($this->_type == IMAGE_GRAPH_AXIS_Y) {
+ if ($tickStart === 'auto') {
+ $tickStart = -$offset;
+ }
+ if ($this->_transpose) {
+ $this->_canvas->line(
+ array(
+ 'x0' => $labelPosition,
+ 'y0' => $this->_top + $tickStart,
+ 'x1' => $labelPosition,
+ 'y1' => $this->_top + $tickEnd
+ )
+ );
+ }
+ else {
+ $this->_canvas->line(
+ array(
+ 'x0' => $this->_right + $tickStart,
+ 'y0' => $labelPosition,
+ 'x1' => $this->_right + $tickEnd,
+ 'y1' => $labelPosition
+ )
+ );
+ }
+ } elseif ($this->_type == IMAGE_GRAPH_AXIS_Y_SECONDARY) {
+ if ($tickStart === 'auto') {
+ $tickStart = $offset;
+ }
+ if ($this->_transpose) {
+ $this->_canvas->line(
+ array(
+ 'x0' => $labelPosition,
+ 'y0' => $this->_bottom - $tickStart,
+ 'x1' => $labelPosition,
+ 'y1' => $this->_bottom - $tickEnd
+ )
+ );
+ }
+ else {
+ $this->_canvas->line(
+ array(
+ 'x0' => $this->_left - $tickStart,
+ 'y0' => $labelPosition,
+ 'x1' => $this->_left - $tickEnd,
+ 'y1' => $labelPosition
+ )
+ );
+ }
+ } else {
+ if ($tickStart === 'auto') {
+ $tickStart = $offset;
+ }
+ if ($this->_transpose) {
+ $this->_canvas->line(
+ array(
+ 'x0' => $this->_right + $tickStart,
+ 'y0' => $labelPosition,
+ 'x1' => $this->_right + $tickEnd,
+ 'y1' => $labelPosition
+ )
+ );
+ }
+ else {
+ $this->_canvas->line(
+ array(
+ 'x0' => $labelPosition,
+ 'y0' => $this->_top - $tickStart,
+ 'x1' => $labelPosition,
+ 'y1' => $this->_top - $tickEnd
+ )
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Draws axis lines.
+ *
+ * @access private
+ */
+ function _drawAxisLines()
+ {
+ if ($this->_type == IMAGE_GRAPH_AXIS_X) {
+ $this->_getLineStyle();
+ $this->_getFillStyle();
+
+ if ($this->_transpose) {
+ $data = array(
+ 'x0' => $this->_right,
+ 'y0' => $this->_top,
+ 'x1' => $this->_right,
+ 'y1' => $this->_bottom
+ );
+ } else {
+ $data = array(
+ 'x0' => $this->_left,
+ 'y0' => $this->_top,
+ 'x1' => $this->_right,
+ 'y1' => $this->_top
+ );
+ }
+
+ if ($this->_showArrow) {
+ if ($this->_getMaximum() <= 0) {
+ $data['end0'] = 'arrow2';
+ $data['size0'] = 7;
+ }
+ else {
+ $data['end1'] = 'arrow2';
+ $data['size1'] = 7;
+ }
+ }
+
+ $this->_canvas->line($data);
+
+ if ($this->_title) {
+ if (!$this->_transpose) {
+ $y = $this->_bottom;
+ $x = $this->_left + $this->width() / 2;
+ $this->write($x, $y, $this->_title, IMAGE_GRAPH_ALIGN_CENTER_X + IMAGE_GRAPH_ALIGN_BOTTOM, $this->_getTitleFont());
+ }
+ else {
+ $y = $this->_top + $this->height() / 2;
+ $x = $this->_left;
+ $this->write($x, $y, $this->_title, IMAGE_GRAPH_ALIGN_LEFT + IMAGE_GRAPH_ALIGN_CENTER_Y, $this->_getTitleFont());
+ }
+ }
+ } elseif ($this->_type == IMAGE_GRAPH_AXIS_Y_SECONDARY) {
+ $this->_getLineStyle();
+ $this->_getFillStyle();
+
+ if ($this->_transpose) {
+ $data = array(
+ 'x0' => $this->_left,
+ 'y0' => $this->_bottom,
+ 'x1' => $this->_right,
+ 'y1' => $this->_bottom
+ );
+ } else {
+ $data = array(
+ 'x0' => $this->_left,
+ 'y0' => $this->_bottom,
+ 'x1' => $this->_left,
+ 'y1' => $this->_top
+ );
+ }
+ if ($this->_showArrow) {
+ if ($this->_getMaximum() <= 0) {
+ $data['end0'] = 'arrow2';
+ $data['size0'] = 7;
+ }
+ else {
+ $data['end1'] = 'arrow2';
+ $data['size1'] = 7;
+ }
+ }
+ $this->_canvas->line($data);
+
+ if ($this->_title) {
+ if ($this->_transpose) {
+ $y = $this->_top;
+ $x = $this->_left + $this->width() / 2;
+ $this->write($x, $y, $this->_title, IMAGE_GRAPH_ALIGN_CENTER_X + IMAGE_GRAPH_ALIGN_TOP, $this->_getTitleFont());
+ }
+ else {
+ $y = $this->_top + $this->height() / 2;
+ $x = $this->_right;
+ $this->write($x, $y, $this->_title, IMAGE_GRAPH_ALIGN_RIGHT + IMAGE_GRAPH_ALIGN_CENTER_Y, $this->_getTitleFont());
+ }
+ }
+ } else {
+ $this->_getLineStyle();
+ $this->_getFillStyle();
+
+ if ($this->_transpose) {
+ $data = array(
+ 'x0' => $this->_left,
+ 'y0' => $this->_top,
+ 'x1' => $this->_right,
+ 'y1' => $this->_top
+ );
+ } else {
+ $data = array(
+ 'x0' => $this->_right,
+ 'y0' => $this->_bottom,
+ 'x1' => $this->_right,
+ 'y1' => $this->_top
+ );
+ }
+ if ($this->_showArrow) {
+ if ($this->_getMaximum() <= 0) {
+ $data['end0'] = 'arrow2';
+ $data['size0'] = 7;
+ }
+ else {
+ $data['end1'] = 'arrow2';
+ $data['size1'] = 7;
+ }
+ }
+ $this->_canvas->line($data);
+
+ if ($this->_title) {
+ if ($this->_transpose) {
+ $y = $this->_bottom;
+ $x = $this->_left + $this->width() / 2;
+ $this->write($x, $y, $this->_title, IMAGE_GRAPH_ALIGN_CENTER_X + IMAGE_GRAPH_ALIGN_BOTTOM, $this->_getTitleFont());
+ }
+ else {
+ $y = $this->_top + $this->height() / 2;
+ $x = $this->_left;
+ $this->write($x, $y, $this->_title, IMAGE_GRAPH_ALIGN_LEFT + IMAGE_GRAPH_ALIGN_CENTER_Y, $this->_getTitleFont());
+ }
+ }
+ }
+ }
+
+ /**
+ * Causes the object to update all sub elements coordinates
+ *
+ * (Image_Graph_Common, does not itself have coordinates, this is basically
+ * an abstract method)
+ *
+ * @access private
+ */
+ function _updateCoords()
+ {
+ parent::_updateCoords();
+ $this->_calcDelta();
+ }
+
+ /**
+ * Output the axis
+ *
+ * @return bool Was the output 'good' (true) or 'bad' (false).
+ * @access private
+ */
+ function _done()
+ {
+ $this->_canvas->startGroup(get_class($this));
+
+ if (parent::_done() === false) {
+ return false;
+ }
+
+ $this->_drawAxisLines();
+
+ $this->_canvas->startGroup(get_class($this) . '_ticks');
+ ksort($this->_labelOptions);
+ foreach ($this->_labelOptions as $level => $labelOption) {
+ $value = false;
+ while (($value = $this->_getNextLabel($value, $level)) !== false) {
+ if ((((abs($value) > 0.0001) || ($this->_showLabelZero)) &&
+ (($value > $this->_getMinimum()) || ($this->_showLabelMinimum)) &&
+ (($value < $this->_getMaximum()) || ($this->_showLabelMaximum))) &&
+ ($value >= $this->_getMinimum()) && ($value <= $this->_getMaximum())
+ ) {
+ $this->_drawTick($value, $level);
+ }
+ }
+ }
+ $this->_canvas->endGroup();
+
+ $tickStart = -3;
+ $tickEnd = 2;
+
+ foreach ($this->_marks as $mark) {
+ if (is_array($mark)) {
+ if ($this->_type == IMAGE_GRAPH_AXIS_X) {
+ if ($this->_transpose) {
+ $x0 = $this->_right + $tickStart;
+ $y0 = $this->_point($mark[1]);
+ $x1 = $this->_right + $tickEnd;
+ $y1 = $this->_point($mark[0]);
+ }
+ else {
+ $x0 = $this->_point($mark[0]);
+ $y0 = $this->_top + $tickStart;
+ $x1 = $this->_point($mark[1]);
+ $y1 = $this->_top + $tickEnd;
+ }
+ } elseif ($this->_type == IMAGE_GRAPH_AXIS_Y) {
+ if ($this->_transpose) {
+ $x0 = $this->_point($mark[0]);
+ $y0 = $this->_top + $tickStart;
+ $x1 = $this->_point($mark[1]);
+ $y1 = $this->_top + $tickEnd;
+ }
+ else {
+ $x0 = $this->_right + $tickStart;
+ $y0 = $this->_point($mark[1]);
+ $x1 = $this->_right + $tickEnd;
+ $y1 = $this->_point($mark[0]);
+ }
+ } elseif ($this->_type == IMAGE_GRAPH_AXIS_Y_SECONDARY) {
+ if ($this->_transpose) {
+ $x0 = $this->_point($mark[0]);
+ $y0 = $this->_bottom + $tickStart;
+ $x1 = $this->_point($mark[1]);
+ $y1 = $this->_bottom + $tickEnd;
+ }
+ else {
+ $x0 = $this->_left + $tickStart;
+ $y0 = $this->_point($mark[1]);
+ $x1 = $this->_left + $tickEnd;
+ $y1 = $this->_point($mark[0]);
+ }
+ }
+ $this->_getFillStyle();
+ $this->_getLineStyle();
+ $this->_canvas->rectangle(array('x0' => $x0, 'y0' => $y0, 'x1' => $x1, 'y1' => $y1));
+ } else {
+ if ($this->_type == IMAGE_GRAPH_AXIS_X) {
+ if ($this->_transpose) {
+ $x0 = $this->_right + 5;
+ $y0 = $this->_point($mark);
+ $x1 = $this->_right + 15;
+ $y1 = $y0;
+ }
+ else {
+ $x0 = $this->_point($mark);
+ $y0 = $this->_top - 5;
+ $x1 = $x0;
+ $y1 = $this->_top - 15;
+ }
+ } elseif ($this->_type == IMAGE_GRAPH_AXIS_Y) {
+ if ($this->_transpose) {
+ $x0 = $this->_point($mark);
+ $y0 = $this->_top - 5;
+ $x1 = $x0;
+ $y1 = $this->_top - 15;
+ }
+ else {
+ $x0 = $this->_right + 5;
+ $y0 = $this->_point($mark);
+ $x1 = $this->_right + 15;
+ $y1 = $y0;
+ }
+ } elseif ($this->_type == IMAGE_GRAPH_AXIS_Y_SECONDARY) {
+ if ($this->_transpose) {
+ $x0 = $this->_point($mark);
+ $y0 = $this->_bottom + 5;
+ $x1 = $x0;
+ $y1 = $this->_bottom + 15;
+ }
+ else {
+ $x0 = $this->_left - 5;
+ $y0 = $this->_point($mark);
+ $x1 = $this->_left - 15;
+ $y1 = $y0;
+ }
+ }
+ $this->_getFillStyle();
+ $this->_getLineStyle();
+ $this->_canvas->line(
+ array(
+ 'x0' => $x0,
+ 'y0' => $y0,
+ 'x1' => $x1,
+ 'y1' => $y1,
+ 'end0' => 'arrow2',
+ 'size0' => 5
+ )
+ );
+ }
+ }
+ $this->_canvas->endGroup();
+
+ return true;
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/Line/Dotted.php
===================================================================
RCS file: lib/pear/Image/Graph/Line/Dotted.php
diff -N lib/pear/Image/Graph/Line/Dotted.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/Line/Dotted.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,67 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: Dotted.php,v 1.6 2005/08/24 20:35:52 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Include file Image/Graph/Line/Formatted.php
+ */
+require_once 'Image/Graph/Line/Formatted.php';
+
+/**
+ * Dotted line style.
+ *
+ * This style displays as a short line with a shorter space afterwards, i.e
+ * 1px color1, 1px color2, 1px color1, etc.
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage Line
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_Line_Dotted extends Image_Graph_Line_Formatted
+{
+
+ /**
+ * DottedLine [Constructor]
+ *
+ * @param mixed $color1 The color representing the dots
+ * @param mixed $color2 The color representing the spaces
+ */
+ function Image_Graph_Line_Dotted($color1, $color2)
+ {
+ parent::Image_Graph_Line_Formatted(array ($color1, $color2));
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/DataSelector.php
===================================================================
RCS file: lib/pear/Image/Graph/DataSelector.php
diff -N lib/pear/Image/Graph/DataSelector.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/DataSelector.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,67 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: DataSelector.php,v 1.7 2005/08/24 20:35:56 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Filter used for selecting which data to show as markers
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage DataSelector
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ */
+class Image_Graph_DataSelector
+{
+
+ /**
+ * Image_Graph_DataSelector [Constructor]
+ */
+ function Image_Graph_DataSelector()
+ {
+ }
+
+ /**
+ * Check if a specified value should be 'selected', ie shown as a marker
+ *
+ * @param array $values The values to check
+ * @return bool True if the Values should cause a marker to be shown, false if not
+ * @access private
+ */
+ function _select($values)
+ {
+ return true;
+ }
+
+}
+
+?>
Index: lib/pear/Image/Graph/DataPreprocessor.php
===================================================================
RCS file: lib/pear/Image/Graph/DataPreprocessor.php
diff -N lib/pear/Image/Graph/DataPreprocessor.php
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Graph/DataPreprocessor.php 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,74 @@
+
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version CVS: $Id: DataPreprocessor.php,v 1.7 2005/08/24 20:35:55 nosey Exp $
+ * @link http://pear.php.net/package/Image_Graph
+ */
+
+/**
+ * Data preprocessor used for preformatting a data.
+ *
+ * A data preprocessor is used in cases where a value from a dataset or label must be
+ * displayed in another format or way than entered. This could for example be the need
+ * to display X-values as a date instead of 1, 2, 3, .. or even worse unix-timestamps.
+ * It could also be when a {@link Image_Graph_Marker_Value} needs to display values as percentages
+ * with 1 decimal digit instead of the default formatting (fx. 12.01271 -> 12.0%).
+ *
+ * @category Images
+ * @package Image_Graph
+ * @subpackage DataPreprocessor
+ * @author Jesper Veggerby
+ * @copyright Copyright (C) 2003, 2004 Jesper Veggerby Hansen
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/Image_Graph
+ * @abstract
+ */
+class Image_Graph_DataPreprocessor
+{
+
+ /**
+ * Image_Graph_DataPreprocessor [Constructor].
+ */
+ function Image_Graph_DataPreprocessor()
+ {
+ }
+
+ /**
+ * Process the value
+ *
+ * @param var $value The value to process/format
+ * @return string The processed value
+ * @access private
+ */
+ function _process($value)
+ {
+ return $value;
+ }
+
+}
+
+?>
Index: lib/pear/Image/Canvas/Fonts/arial.ttf
===================================================================
RCS file: lib/pear/Image/Canvas/Fonts/arial.ttf
diff -N lib/pear/Image/Canvas/Fonts/arial.ttf
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/pear/Image/Canvas/Fonts/arial.ttf 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,7969 @@
+ pDSIG$=ùç ? |GDEF^#]r u ¦GSUBÕðÝÌ uÀ ?JSTFm*i l LTSH€eú< |