Subversion Repositories Applications.gtt

Rev

Rev 60 | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php
/*
 * This work is hereby released into the Public Domain.
 * To view a copy of the public domain dedication,
 * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
 * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
 *
 */

require_once dirname(__FILE__)."/Component.class.php";
 
 
/**
 * Graph using X and Y axis
 *
 * @package Artichow
 */
abstract class awPlot extends awComponent {
        
        /**
         * Values for Y axis
         *
         * @var array
         */
        protected $datay;

        /**
         * Values for X axis
         *
         * @var array
         */
        protected $datax;
        
        /**
         * Grid properties
         *
         * @var Grid
         */
        public $grid;
        
        /**
         * X axis
         *
         * @var Axis
         */
        public $xAxis;
        
        /**
         * Y axis
         *
         * @var Axis
         */
        public $yAxis;
        
        /**
         * Position of X axis
         *
         * @var int
         */
        protected $xAxisPosition = awPlot::BOTTOM;
        
        /**
         * Set X axis on zero ?
         *
         * @var bool
         */
        protected $xAxisZero = TRUE;
        
        /**
         * Set Y axis on zero ?
         *
         * @var bool
         */
        protected $yAxisZero = FALSE;
        
        /**
         * Position of Y axis
         *
         * @var int
         */
        protected $yAxisPosition = awPlot::LEFT;
        
        /**
         * Change min value for Y axis
         *
         * @var mixed
         */
        private $yMin = NULL;
        
        /**
         * Change max value for Y axis
         *
         * @var mixed
         */
        private $yMax = NULL;
        
        /**
         * Change min value for X axis
         *
         * @var mixed
         */
        private $xMin = NULL;
        
        /**
         * Change max value for X axis
         *
         * @var mixed
         */
        private $xMax = NULL;
        
        /**
         * Left axis
         *
         * @var int
         */
        const LEFT = 'left';
        
        /**
         * Right axis
         *
         * @var int
         */
        const RIGHT = 'right';
        
        /**
         * Top axis
         *
         * @var int
         */
        const TOP = 'top';
        
        /**
         * Bottom axis
         *
         * @var int
         */
        const BOTTOM = 'bottom';
        
        /**
         * Both left/right or top/bottom axis
         *
         * @var int
         */
        const BOTH = 'both';
        
        /**
         * Build the plot
         *
         */
        public function __construct() {
        
                parent::__construct();
                
                $this->grid = new awGrid;
                $this->grid->setBackgroundColor(new awWhite);

                $this->padding->add(20, 0, 0, 20);
                
                $this->xAxis = new awAxis;
                $this->xAxis->addTick('major', new awTick(0, 5));
                $this->xAxis->addTick('minor', new awTick(0, 3));
                $this->xAxis->setTickStyle(awTick::OUT);
                $this->xAxis->label->setFont(new awTuffy(7));
                
                $this->yAxis = new awAxis;
                $this->yAxis->auto(TRUE);
                $this->yAxis->addTick('major', new awTick(0, 5));
                $this->yAxis->addTick('minor', new awTick(0, 3));
                $this->yAxis->setTickStyle(awTick::OUT);
                $this->yAxis->setNumberByTick('minor', 'major', 3);
                $this->yAxis->label->setFont(new awTuffy(7));
                $this->yAxis->title->setAngle(90);
                
        }
        
        /**
         * Get plot values
         *
         * @return array
         */
        public function getValues() {
                return $this->datay;
        }
        
        /**
         * Reduce number of values in the plot
         *
         * @param int $number Reduce number of values to $number
         */
        public function reduce($number) {
                
                $count = count($this->datay);
                $ratio = ceil($count / $number);
                
                if($ratio > 1) {
                
                        $tmpy = $this->datay;
                        $datay = array();
                        
                        $datax = array();
                        $cbLabel = $this->xAxis->label->getCallbackFunction();
                        
                        for($i = 0; $i < $count; $i += $ratio) {
                        
                                $slice = array_slice($tmpy, $i, $ratio);
                                $datay[] = array_sum($slice) / count($slice);
                                
                                // Reduce data on X axis if needed
                                if($cbLabel !== NULL) {
                                        $datax[] = $cbLabel($i + round($ratio / 2));
                                }
                                
                        }
                        
                        $this->setValues($datay);
                        
                        if($cbLabel !== NULL) {
                                $this->xAxis->setLabelText($datax);
                        }
                        
                        
                }
                
        }
        
        /**
         * Count values in the plot
         *
         * @return int
         */
        public function getXAxisNumber() {
                list($min, $max) = $this->xAxis->getRange();
                return ($max - $min + 1);
        }
        
        /**
         * Change X axis
         *
         * @param int $axis
         */
        public function setXAxis($axis) {
                $this->xAxisPosition = $axis;
        }
        
        /**
         * Get X axis
         *
         * @return int
         */
        public function getXAxis() {
                return $this->xAxisPosition;
        }
        
        /**
         * Set X axis on zero
         *
         * @param bool $zero
         */
        public function setXAxisZero($zero) {
                $this->xAxisZero = (bool)$zero;
        }
        
        /**
         * Set Y axis on zero
         *
         * @param bool $zero
         */
        public function setYAxisZero($zero) {
                $this->yAxisZero = (bool)$zero;
        }
        
        /**
         * Change Y axis
         *
         * @param int $axis
         */
        public function setYAxis($axis) {
                $this->yAxisPosition = $axis;
        }
        
        /**
         * Get Y axis
         *
         * @return int
         */
        public function getYAxis() {
                return $this->yAxisPosition;
        }
        
        /**
         * Change min value for Y axis
         * Set NULL for auto selection.
         *
         * @param float $value
         */
        public function setYMin($value) {
                $this->yMin = $value;
                $this->yAxis->auto(FALSE);
                $this->updateAxis();
        }
        
        /**
         * Change max value for Y axis
         * Set NULL for auto selection.
         *
         * @param float $value
         */
        public function setYMax($value) {
                $this->yMax = $value;
                $this->yAxis->auto(FALSE);
                $this->updateAxis();
        }
        
        /**
         * Change min value for X axis
         * Set NULL for auto selection.
         *
         * @param float $value
         */
        public function setXMin($value) {
                $this->xMin = $value;
                $this->updateAxis();
        }
        
        /**
         * Change max value for X axis
         * Set NULL for auto selection.
         *
         * @param float $value
         */
        public function setXMax($value) {
                $this->xMax = $value;
                $this->updateAxis();
        }
        
        /**
         * Get min value for Y axis
         *
         * @return float $value
         */
        public function getYMin() {
                if($this->auto) {
                        if(is_null($this->yMin)) {
                                $min = array_min($this->datay);
                                if($min > 0) {
                                        return 0;
                                }
                        }
                }
                return is_null($this->yMin) ? array_min($this->datay) : (float)$this->yMin;
        }
        
        /**
         * Get max value for Y axis
         *
         * @return float $value
         */
        public function getYMax() {
                if($this->auto) {
                        if(is_null($this->yMax)) {
                                $max = array_max($this->datay);
                                if($max < 0) {
                                        return 0;
                                }
                        }
                }
                return is_null($this->yMax) ? array_max($this->datay) : (float)$this->yMax;
        }
        
        /**
         * Get min value for X axis
         *
         * @return float $value
         */
        public function getXMin() {
                return floor(is_null($this->xMin) ? array_min($this->datax) : $this->xMin);
        }
        
        /**
         * Get max value for X axis
         *
         * @return float $value
         */
        public function getXMax() {
                return (ceil(is_null($this->xMax) ? array_max($this->datax) : (float)$this->xMax)) + ($this->getXCenter() ? 1 : 0);
        }
        
        /**
         * Get min value with spaces for Y axis
         *
         * @return float $value
         */
        public function getRealYMin() {
                $min = $this->getYMin();
                if($this->space->bottom !== NULL) {
                        $interval = ($this->getYMax() - $min) * $this->space->bottom / 100;
                        return $min - $interval;
                } else {
                        return is_null($this->yMin) ? $min : (float)$this->yMin;
                }
        }
        
        /**
         * Get max value with spaces for Y axis
         *
         * @return float $value
         */
        public function getRealYMax() {
                $max = $this->getYMax();
                if($this->space->top !== NULL) {
                        $interval = ($max - $this->getYMin()) * $this->space->top / 100;
                        return $max + $interval;
                } else {
                        return is_null($this->yMax) ? $max : (float)$this->yMax;
                }
        }
        
        public function init(awDriver $driver) {
                
                list($x1, $y1, $x2, $y2) = $this->getPosition();
                
                // Get space informations
                list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1);
                
                $this->xAxis->setPadding($leftSpace, $rightSpace);
                
                if($this->space->bottom > 0 or $this->space->top > 0) {
                
                        list($min, $max) = $this->yAxis->getRange();
                        $interval = $max - $min;
                        
                        $this->yAxis->setRange(
                                $min - $interval * $this->space->bottom / 100,
                                $max + $interval * $this->space->top / 100
                        );
                        
                }
                
                // Auto-scaling mode
                $this->yAxis->autoScale();
                
                // Number of labels is not specified
                if($this->yAxis->getLabelNumber() === NULL) {
                        $number = round(($y2 - $y1) / 75) + 2;
                        $this->yAxis->setLabelNumber($number);
                }
                
                $this->xAxis->line->setX($x1, $x2);
                $this->yAxis->line->setY($y2, $y1);
                
                // Set ticks
                
                $this->xAxis->tick('major')->setNumber($this->getXAxisNumber());
                $this->yAxis->tick('major')->setNumber($this->yAxis->getLabelNumber());
                
                                
                // Center X axis on zero
                if($this->xAxisZero) {
                        $this->xAxis->setYCenter($this->yAxis, 0);
                }
                
                // Center Y axis on zero
                if($this->yAxisZero) {
                        $this->yAxis->setXCenter($this->xAxis, 0);
                }
                
                // Set axis labels
                $labels = array();
                
                list($xMin, $xMax) = $this->xAxis->getRange();
                for($i = $xMin; $i <= $xMax; $i++) {
                        $labels[] = $i;
                }
                $this->xAxis->label->set($labels);
        
                parent::init($driver);
                
                list($x1, $y1, $x2, $y2) = $this->getPosition();
                
                list($leftSpace, $rightSpace) = $this->getSpace($x2 - $x1, $y2 - $y1);
                
                // Create the grid
                $this->createGrid();
        
                // Draw the grid
                $this->grid->setSpace($leftSpace, $rightSpace, 0, 0);
                $this->grid->draw($driver, $x1, $y1, $x2, $y2);
                
        }
        
        public function drawEnvelope(awDriver $driver) {
                
                list($x1, $y1, $x2, $y2) = $this->getPosition();
                
                if($this->getXCenter()) {
                        $size = $this->xAxis->getDistance(0, 1);
                        $this->xAxis->label->move($size / 2, 0);
                        $this->xAxis->label->hideLast(TRUE);
                }
                
                // Draw top axis
                if($this->xAxisPosition === awPlot::TOP or $this->xAxisPosition === awPlot::BOTH) {
                        $top = clone $this->xAxis;
                        if($this->xAxisZero === FALSE) {
                                $top->line->setY($y1, $y1);
                        }
                        $top->label->setAlign(NULL, awLabel::TOP);
                        $top->label->move(0, -3);
                        $top->title->move(0, -25);
                        $top->draw($driver);
                }
                
                // Draw bottom axis
                if($this->xAxisPosition === awPlot::BOTTOM or $this->xAxisPosition === awPlot::BOTH) {
                        $bottom = clone $this->xAxis;
                        if($this->xAxisZero === FALSE) {
                                $bottom->line->setY($y2, $y2);
                        }
                        $bottom->label->setAlign(NULL, awLabel::BOTTOM);
                        $bottom->label->move(0, 3);
                        $bottom->reverseTickStyle();
                        $bottom->title->move(0, 25);
                        $bottom->draw($driver);
                }
                
                // Draw left axis
                if($this->yAxisPosition === awPlot::LEFT or $this->yAxisPosition === awPlot::BOTH) {
                        $left = clone $this->yAxis;
                        if($this->yAxisZero === FALSE) {
                                $left->line->setX($x1, $x1);
                        }
                        $left->label->setAlign(awLabel::RIGHT);
                        $left->label->move(-6, 0);
                        $left->title->move(-25, 0);
                        $left->draw($driver);
                }
                
                // Draw right axis
                if($this->yAxisPosition === awPlot::RIGHT or $this->yAxisPosition === awPlot::BOTH) {
                        $right = clone $this->yAxis;
                        if($this->yAxisZero === FALSE) {
                                $right->line->setX($x2, $x2);
                        }
                        $right->label->setAlign(awLabel::LEFT);
                        $right->label->move(6, 0);
                        $right->reverseTickStyle();
                        $right->title->move(25, 0);
                        $right->draw($driver);
                }
        
        }
        
        protected function createGrid() {
                
                $max = $this->getRealYMax();
                $min = $this->getRealYMin();

                $number = $this->yAxis->getLabelNumber() - 1;
                
                if($number < 1) {
                        return;
                }
                
                // Horizontal lines of the grid
                
                $h = array();
                for($i = 0; $i <= $number; $i++) {
                        $h[] = $i / $number;
                }
                
                // Vertical lines
        
                $major = $this->yAxis->tick('major');
                $interval = $major->getInterval();
                $number = $this->getXAxisNumber() - 1;
                
                $w = array();
                
                if($number > 0) {
                        
                        for($i = 0; $i <= $number; $i++) {
                                if($i%$interval === 0) {
                                        $w[] = $i / $number;
                                }
                        }
                        
                }
        
                $this->grid->setGrid($w, $h);
        
        }
        
        /**
         * Change values of Y axis
         * This method ignores not numeric values
         *
         * @param array $datay
         * @param array $datax
         */
        public function setValues($datay, $datax = NULL) {
        
                $this->checkArray($datay);
                
                foreach($datay as $key => $value) {
                        unset($datay[$key]);
                        $datay[(int)$key] = $value;
                }
                
                if($datax === NULL) {
                        $datax = array();
                        for($i = 0; $i < count($datay); $i++) {
                                $datax[] = $i;
                        }
                } else {
                        foreach($datax as $key => $value) {
                                unset($datax[$key]);
                                $datax[(int)$key] = $value;
                        }
                }
                
                $this->checkArray($datax);
                
                if(count($datay) === count($datax)) {
                
                        // Set values
                        $this->datay = $datay;
                        $this->datax = $datax;
                        // Update axis with the new awvalues
                        $this->updateAxis();
                } else {
                        awImage::drawError("Class Plot: Plots must have the same number of X and Y points.");
                }
                
        }
        
        /**
         * Return begin and end values
         *
         * @return array
         */
        protected function getLimit() {
        
                $i = 0;
                while(array_key_exists($i, $this->datay) and $this->datay[$i] === NULL) {
                        $i++;
                }
                $start = $i;
                $i = count($this->datay) - 1;
                while(array_key_exists($i, $this->datay) and $this->datay[$i] === NULL) {
                        $i--;
                }
                $stop = $i;
                
                return array($start, $stop);
                
        }
        
        /**
         * Return TRUE if labels must be centered on X axis, FALSE otherwise
         *
         * @return bool
         */
        abstract public function getXCenter();
        
        private function updateAxis() {
        
                $this->xAxis->setRange(
                        $this->getXMin(),
                        $this->getXMax()
                );
                $this->yAxis->setRange(
                        $this->getRealYMin(),
                        $this->getRealYMax()
                );
                
        }
        
        private function checkArray(&$array) {
        
                if(is_array($array) === FALSE) {
                        awImage::drawError("Class Plot: You tried to set a value that is not an array.");
                }
                
                foreach($array as $key => $value) {
                        if(is_numeric($value) === FALSE and is_null($value) === FALSE) {
                                awImage::drawError("Class Plot: Expected numeric values for the plot.");
                        }
                }
                
                if(count($array) < 1) {
                        awImage::drawError("Class Plot: Your plot must have at least 1 value.");
                }
        
        }

}

registerClass('Plot', TRUE);

class awPlotAxis {

        /**
         * Left axis
         *
         * @var Axis
         */
        public $left;

        /**
         * Right axis
         *
         * @var Axis
         */
        public $right;

        /**
         * Top axis
         *
         * @var Axis
         */
        public $top;

        /**
         * Bottom axis
         *
         * @var Axis
         */
        public $bottom;

        /**
         * Build the group of axis
         */
        public function __construct() {
        
                $this->left = new awAxis;
                $this->left->auto(TRUE);
                $this->left->label->setAlign(awLabel::RIGHT);
                $this->left->label->move(-6, 0);
                $this->yAxis($this->left);
                $this->left->setTickStyle(awTick::OUT);
                $this->left->title->move(-25, 0);
                
                $this->right = new awAxis;
                $this->right->auto(TRUE);
                $this->right->label->setAlign(awLabel::LEFT);
                $this->right->label->move(6, 0);
                $this->yAxis($this->right);
                $this->right->setTickStyle(awTick::IN);
                $this->right->title->move(25, 0);
                
                $this->top = new awAxis;
                $this->top->label->setAlign(NULL, awLabel::TOP);
                $this->top->label->move(0, -3);
                $this->xAxis($this->top);
                $this->top->setTickStyle(awTick::OUT);
                $this->top->title->move(0, -25);
                
                $this->bottom = new awAxis;
                $this->bottom->label->setAlign(NULL, awLabel::BOTTOM);
                $this->bottom->label->move(0, 3);
                $this->xAxis($this->bottom);
                $this->bottom->setTickStyle(awTick::IN);
                $this->bottom->title->move(0, 25);
        
        }
        
        protected function xAxis(awAxis $axis) {
        
                $axis->addTick('major', new awTick(0, 5));
                $axis->addTick('minor', new awTick(0, 3));
                $axis->label->setFont(new awTuffy(7));
                
        }
        
        protected function yAxis(awAxis $axis) {
        
                $axis->addTick('major', new awTick(0, 5));
                $axis->addTick('minor', new awTick(0, 3));
                $axis->setNumberByTick('minor', 'major', 3);
                $axis->label->setFont(new awTuffy(7));
                $axis->title->setAngle(90);
                
        }

}

registerClass('PlotAxis');

/**
 * A graph with axis can contain some groups of components
 *
 * @package Artichow
 */
class awPlotGroup extends awComponentGroup {
        
        /**
         * Grid properties
         *
         * @var Grid
         */
        public $grid;
        
        /**
         * Left, right, top and bottom axis
         *
         * @var PlotAxis
         */
        public $axis;
        
        /**
         * Set the X axis on zero
         *
         * @var bool
         */
        protected $xAxisZero = TRUE;
        
        /**
         * Set the Y axis on zero
         *
         * @var bool
         */
        protected $yAxisZero = FALSE;
        
        /**
         * Real axis used for Y axis
         *
         * @var string
         */
        private $yRealAxis = awPlot::LEFT;
        
        /**
         * Real axis used for X axis
         *
         * @var string
         */
        private $xRealAxis = awPlot::BOTTOM;
        
        /**
         * Change min value for Y axis
         *
         * @var mixed
         */
        private $yMin = NULL;
        
        /**
         * Change max value for Y axis
         *
         * @var mixed
         */
        private $yMax = NULL;
        
        /**
         * Change min value for X axis
         *
         * @var mixed
         */
        private $xMin = NULL;
        
        /**
         * Change max value for X axis
         *
         * @var mixed
         */
        private $xMax = NULL;
        
        /**
         * Build the PlotGroup
         *
         */
        public function __construct() {
        
                parent::__construct();
                
                $this->grid = new awGrid;
                $this->grid->setBackgroundColor(new awWhite);
                
                $this->axis = new awPlotAxis;
                
        }
        
        /**
         * Set the X axis on zero or not
         *
         * @param bool $zero
         */
        public function setXAxisZero($zero) {
                $this->xAxisZero = (bool)$zero;
        }
        
        /**
         * Set the Y axis on zero or not
         *
         * @param bool $zero
         */
        public function setYAxisZero($zero) {
                $this->yAxisZero = (bool)$zero;
        }
        
        /**
         * Change min value for Y axis
         * Set NULL for auto selection.
         *
         * @param float $value
         */
        public function setYMin($value) {
                $this->axis->left->auto(FALSE);
                $this->axis->right->auto(FALSE);
                $this->yMin = $value;
        }
        
        /**
         * Change max value for Y axis
         * Set NULL for auto selection.
         *
         * @param float $value
         */
        public function setYMax($value) {
                $this->axis->left->auto(FALSE);
                $this->axis->right->auto(FALSE);
                $this->yMax = $value;
        }
        
        /**
         * Change min value for X axis
         * Set NULL for auto selection.
         *
         * @param float $value
         */
        public function setXMin($value) {
                $this->xMin = $value;
        }
        
        /**
         * Change max value for X axis
         * Set NULL for auto selection.
         *
         * @param float $value
         */
        public function setXMax($value) {
                $this->xMax = $value;
        }
        
        /**
         * Get min value for X axis
         *
         * @return float $value
         */
        public function getXMin() {
                
                return $this->getX('min');
                
        }
        
        /**
         * Get max value for X axis
         *
         * @return float $value
         */
        public function getXMax() {
        
                return $this->getX('max');
                
        }
        
        private function getX($type) {
        
                switch($type) {
                        case 'max' :
                                if($this->xMax !== NULL) {
                                        return $this->xMax;
                                }
                                break;
                        case 'min' :
                                if($this->xMin !== NULL) {
                                        return $this->xMin;
                                }
                                break;
                }
                
                $value = NULL;
                $get = 'getX'.ucfirst($type);
                
                for($i = 0; $i < count($this->components); $i++) {
                
                        $component = $this->components[$i];
                
                        if($value === NULL) {
                                $value = $component->$get();
                        } else {
                                $value = $type($value, $component->$get());
                        }
                        
                }
                
                return $value;
        
        }
        
        /**
         * Get min value with spaces for Y axis
         *
         * @param string $axis Axis name
         * @return float $value
         */
        public function getRealYMin($axis = NULL) {
        
                if($axis === NULL) {
                        return NULL;
                }
                
                $min = $this->getRealY('min', $axis);
                $max = $this->getRealY('max', $axis);
                
                if($this->space->bottom !== NULL) {
                        $interval = ($min - $max) * $this->space->bottom / 100;
                        return $min + $interval;
                } else {
                        return $min;
                }
                
        }
        
        /**
         * Get max value with spaces for Y axis
         *
         * @param string $axis Axis name
         * @return float $value
         */
        public function getRealYMax($axis = NULL) {
        
                if($axis === NULL) {
                        return NULL;
                }
                
                $min = $this->getRealY('min', $axis);
                $max = $this->getRealY('max', $axis);
                
                if($this->space->top !== NULL) {
                        $interval = ($max - $min) * $this->space->top / 100;
                        return $max + $interval;
                } else {
                        return $max;
                }
                
        }
        
        private function getRealY($type, $axis) {
        
                switch($type) {
                        case 'max' :
                                if($this->yMax !== NULL) {
                                        return $this->yMax;
                                }
                                break;
                        case 'min' :
                                if($this->yMin !== NULL) {
                                        return $this->yMin;
                                }
                                break;
                }
                
                $value = NULL;
                $get = 'getY'.ucfirst($type);
                
                for($i = 0; $i < count($this->components); $i++) {
                
                        $component = $this->components[$i];
                        
                        switch($axis) {
                        
                                case awPlot::LEFT :
                                case awPlot::RIGHT :
                                        $test = ($component->getYAxis() === $axis);
                                        break;
                                default :
                                        $test = FALSE;
                        
                        }
                        
                        if($test) {
                                
                                $auto = $component->yAxis->isAuto();
                                $this->axis->{$axis}->auto($auto);
                                
                                if($value === NULL) {
                                        $value = $component->$get();
                                } else {
                                        $value = $type($value, $component->$get());
                                }
                                
                        }
                        
                }
                
                return $value;
        
        }
        
        public function init(awDriver $driver) {
                
                list($x1, $y1, $x2, $y2) = $this->getPosition();
                
                // Get PlotGroup space
                list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1);
                
                // Count values in the group
                $values = $this->getXAxisNumber();
                
                // Init the PlotGroup
                $this->axis->top->line->setX($x1, $x2);
                $this->axis->bottom->line->setX($x1, $x2);
                $this->axis->left->line->setY($y2, $y1);
                $this->axis->right->line->setY($y2, $y1);
                
                $this->axis->top->setPadding($leftSpace, $rightSpace);
                $this->axis->bottom->setPadding($leftSpace, $rightSpace);
                
                $xMin = $this->getXMin();
                $xMax = $this->getXMax();
                
                $this->axis->top->setRange($xMin, $xMax);
                $this->axis->bottom->setRange($xMin, $xMax);
                
                for($i = 0; $i < count($this->components); $i++) {
                
                        
                        $component = $this->components[$i];
                        
                                                $component->auto($this->auto);
                        
                        // Copy space to the component
                        
                        $component->setSpace($this->space->left, $this->space->right, $this->space->top, $this->space->bottom);
                        
                        $component->xAxis->setPadding($leftSpace, $rightSpace);
                        $component->xAxis->line->setX($x1, $x2);
                        
                        $component->yAxis->line->setY($y2, $y1);
                        
                }
                
                // Set Y axis range
                foreach(array('left', 'right') as $axis) {
                
                        if($this->isAxisUsed($axis)) {
                        
                                $min = $this->getRealYMin($axis);
                                $max = $this->getRealYMax($axis);
                                
                                $interval = $max - $min;
                                
                                $this->axis->{$axis}->setRange(
                                        $min - $interval * $this->space->bottom / 100,
                                        $max + $interval * $this->space->top / 100
                                );
                
                                // Auto-scaling mode
                                if($this->axis->{$axis}->isAuto()) {
                                        $this->axis->{$axis}->autoScale();
                                }
                                
                        }
                        
                }
                
                if($this->axis->left->getLabelNumber() === NULL) {
                        $number = round(($y2 - $y1) / 75) + 2;
                        $this->axis->left->setLabelNumber($number);
                }
                
                if($this->axis->right->getLabelNumber() === NULL) {
                        $number = round(($y2 - $y1) / 75) + 2;
                        $this->axis->right->setLabelNumber($number);
                }
                
                // Center labels on X axis if needed
                $test = array(awPlot::TOP => FALSE, awPlot::BOTTOM => FALSE);
                
                for($i = 0; $i < count($this->components); $i++) {
                
                        
                        $component = $this->components[$i];
                        
                                                
                        if($component->getValues() !== NULL) {
                                
                                $axis = $component->getXAxis();
                                
                                if($test[$axis] === FALSE) {
                
                                        // Center labels for bar plots
                                        if($component->getXCenter()) {
                                                $size = $this->axis->{$axis}->getDistance(0, 1);
                                                $this->axis->{$axis}->label->move($size / 2, 0);
                                                $this->axis->{$axis}->label->hideLast(TRUE);
                                                $test[$axis] = TRUE;
                                        }
                                        
                                }
                                
                        }
                        
                        
                }
                
                // Set axis labels
                $labels = array();
                for($i = $xMin; $i <= $xMax; $i++) {
                        $labels[] = $i;
                }
                if($this->axis->top->label->count() === 0) {
                        $this->axis->top->label->set($labels);
                }
                if($this->axis->bottom->label->count() === 0) {
                        $this->axis->bottom->label->set($labels);
                }
                
                // Set ticks
                
                $this->axis->top->tick('major')->setNumber($values);
                $this->axis->bottom->tick('major')->setNumber($values);
                $this->axis->left->tick('major')->setNumber($this->axis->left->getLabelNumber());
                $this->axis->right->tick('major')->setNumber($this->axis->right->getLabelNumber());
                
                                
                // Set X axis on zero
                if($this->xAxisZero) {
                        $axis = $this->selectYAxis();
                        $this->axis->bottom->setYCenter($axis, 0);
                        $this->axis->top->setYCenter($axis, 0);
                }
                
                // Set Y axis on zero
                if($this->yAxisZero) {
                        $axis = $this->selectXAxis();
                        $this->axis->left->setXCenter($axis, 1);
                        $this->axis->right->setXCenter($axis, 1);
                }
                
                parent::init($driver);
                
                list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1);
                
                // Create the grid
                $this->createGrid();
        
                // Draw the grid
                $this->grid->setSpace($leftSpace, $rightSpace, 0, 0);
                $this->grid->draw($driver, $x1, $y1, $x2, $y2);
                
        }
        
        public function drawComponent(awDriver $driver, $x1, $y1, $x2, $y2, $aliasing) {
                
                $xMin = $this->getXMin();
                $xMax = $this->getXMax();
        
                $maxLeft = $this->getRealYMax(awPlot::LEFT);
                $maxRight = $this->getRealYMax(awPlot::RIGHT);
                
                $minLeft = $this->getRealYMin(awPlot::LEFT);
                $minRight = $this->getRealYMin(awPlot::RIGHT);
        
                foreach($this->components as $component) {
                
                        $min = $component->getYMin();
                        $max = $component->getYMax();
                        
                        // Set component minimum and maximum
                        if($component->getYAxis() === awPlot::LEFT) {
                        
                                list($min, $max) = $this->axis->left->getRange();
                        
                                $component->setYMin($min);
                                $component->setYMax($max);
                                
                        } else {
                        
                                list($min, $max) = $this->axis->right->getRange();
                                
                                $component->setYMin($min);
                                $component->setYMax($max);
                                
                        }
                        
                        $component->setXAxisZero($this->xAxisZero);
                        $component->setYAxisZero($this->yAxisZero);
                        
                        $component->xAxis->setRange($xMin, $xMax);
                
                        $component->drawComponent(
                                $driver,
                                $x1, $y1,
                                $x2, $y2,
                                $aliasing
                        );
                        
                        $component->setYMin($min);
                        $component->setYMax($max);
                        
                }
                
        }
        
        public function drawEnvelope(awDriver $driver) {
                
                list($x1, $y1, $x2, $y2) = $this->getPosition();
                
                // Hide unused axis
                foreach(array(awPlot::LEFT, awPlot::RIGHT, awPlot::TOP, awPlot::BOTTOM) as $axis) {
                        if($this->isAxisUsed($axis) === FALSE) {
                                $this->axis->{$axis}->hide(TRUE);
                        }
                }
                
                // Draw top axis
                $top = $this->axis->top;
                if($this->xAxisZero === FALSE) {
                        $top->line->setY($y1, $y1);
                }
                $top->draw($driver);
                
                // Draw bottom axis
                $bottom = $this->axis->bottom;
                if($this->xAxisZero === FALSE) {
                        $bottom->line->setY($y2, $y2);
                }
                $bottom->draw($driver);
                
                // Draw left axis
                $left = $this->axis->left;
                if($this->yAxisZero === FALSE) {
                        $left->line->setX($x1, $x1);
                }
                $left->draw($driver);
                
                // Draw right axis
                $right = $this->axis->right;
                if($this->yAxisZero === FALSE) {
                        $right->line->setX($x2, $x2);
                }
                $right->draw($driver);
        
        }
        
        /**
         * Is the specified axis used ?
         *
         * @param string $axis Axis name
         * @return bool
         */
        protected function isAxisUsed($axis) {
        
                for($i = 0; $i < count($this->components); $i++) {
                
                        $component = $this->components[$i];
                        
                        switch($axis) {
                        
                                case awPlot::LEFT :
                                case awPlot::RIGHT :
                                        if($component->getYAxis() === $axis) {
                                                return TRUE;
                                        }
                                        break;
                        
                                case awPlot::TOP :
                                case awPlot::BOTTOM :
                                        if($component->getXAxis() === $axis) {
                                                return TRUE;
                                        }
                                        break;
                        
                        }
                        
                }
                
                return FALSE;
        
        }
        
        protected function createGrid() {
                
                $max = $this->getRealYMax(awPlot::LEFT);
                $min = $this->getRealYMin(awPlot::RIGHT);
                
                // Select axis (left if possible, right otherwise)
                $axis = $this->selectYAxis();
        
                $number = $axis->getLabelNumber() - 1;
                
                if($number < 1) {
                        return;
                }
                
                // Horizontal lines of grid
                
                $h = array();
                for($i = 0; $i <= $number; $i++) {
                        $h[] = $i / $number;
                }
                
                // Vertical lines
        
                $major = $axis->tick('major');
                $interval = $major->getInterval();
                $number = $this->getXAxisNumber() - 1;
                
                $w = array();
                
                if($number > 0) {
                        
                        for($i = 0; $i <= $number; $i++) {
                                if($i%$interval === 0) {
                                        $w[] = $i / $number;
                                }
                        }
                        
                }
        
                $this->grid->setGrid($w, $h);
        
        }
        
        protected function selectYAxis(){
                
                // Select axis (left if possible, right otherwise)
                if($this->isAxisUsed(awPlot::LEFT)) {
                        $axis = $this->axis->left;
                } else {
                        $axis = $this->axis->right;
                }
                
                return $axis;
                
        }
        
        protected function selectXAxis(){
                
                // Select axis (bottom if possible, top otherwise)
                if($this->isAxisUsed(awPlot::BOTTOM)) {
                        $axis = $this->axis->bottom;
                } else {
                        $axis = $this->axis->top;
                }
                
                return $axis;
                
        }
        
        protected function getXAxisNumber() {
                $offset = $this->components[0];
                $max = $offset->getXAxisNumber();
                for($i = 1; $i < count($this->components); $i++) {
                        $offset = $this->components[$i];
                        $max = max($max, $offset->getXAxisNumber());
                }
                return $max;
        }

}

registerClass('PlotGroup');
?>