Subversion Repositories Applications.gtt

Rev

Rev 61 | 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__)."/../Graph.class.php";

/**
 * Handle axis
 *
 * @package Artichow
 */
class awAxis {

        /**
         * Axis line
         *
         * @var Line
         */
        public $line;

        /**
         * Axis labels
         *
         * @var Label
         */
        public $label;

        /**
         * Axis title
         *
         * @var Label
         */
        public $title;

        /**
         * Title position
         *
         * @var float
         */
        protected $titlePosition = 0.5;

        /**
         * Labels number
         *
         * @var int
         */
        protected $labelNumber;

        /**
         * Axis ticks
         *
         * @var array
         */
        protected $ticks = array();

        /**
         * Axis and ticks color
         *
         * @var Color
         */
        protected $color;

        /**
         * Axis left and right padding
         *
         * @var Side
         */
        protected $padding;

        /**
         * Axis range
         *
         * @var array
         */
        protected $range;

        /**
         * Hide axis
         *
         * @var bool
         */
        protected $hide = FALSE;

        /**
         * Auto-scaling mode
         *
         * @var bool
         */
        protected $auto = TRUE;

        /**
         * Axis range callback function
         *
         * @var array
         */
        protected $rangeCallback = array(
                'toValue' => 'toProportionalValue',
                'toPosition' => 'toProportionalPosition'
        );

        /**
         * Build the axis
         *
         * @param float $min Begin of the range of the axis
         * @param float $max End of the range of the axis
         */
        public function __construct($min = NULL, $max = NULL) {

                $this->line = new awVector(
                        new awPoint(0, 0),
                        new awPoint(0, 0)
                );

                $this->label = new awLabel;
                $this->padding = new awSide;

                $this->title = new awLabel(
                        NULL,
                        NULL,
                        NULL,
                        0
                );

                $this->setColor(new awBlack);

                if($min !== NULL and $max !== NULL) {
                        $this->setRange($min, $max);
                }

        }

        /**
         * Enable/disable auto-scaling mode
         *
         * @param bool $auto
         */
        public function auto($auto) {
                $this->auto = (bool)$auto;
        }

        /**
         * Get auto-scaling mode status
         *
         * @return bool
         */
        public function isAuto() {
                return $this->auto;
        }

        /**
         * Hide axis
         *
         * @param bool $hide
         */
        public function hide($hide = TRUE) {
                $this->hide = (bool)$hide;
        }

        /**
         * Show axis
         *
         * @param bool $show
         */
        public function show($show = TRUE) {
                $this->hide = !(bool)$show;
        }

        /**
         * Return a tick object from its name
         *
         * @param string $name Tick object name
         * @return Tick
         */
        public function tick($name) {
                
                return array_key_exists($name, $this->ticks) ? $this->ticks[$name] : NULL;
                
                        }

        /**
         * Add a tick object
         *
         * @param string $name Tick object name
         * @param awTick $tick Tick object
         */
        public function addTick($name, awTick $tick) {
                
                $this->ticks[$name] = $tick;
                
                        }

        /**
         * Delete a tick object
         *
         * @param string $name Tick object name
         */
        public function deleteTick($name) {
                if(array_key_exists($name, $this->ticks)) {
                        unset($this->ticks[$name]);
                }
        }

        /**
         * Hide all ticks
         *
         * @param bool $hide Hide or not ?
         */
        public function hideTicks($hide = TRUE) {
                
                foreach($this->ticks as $tick) {
                        $tick->hide($hide);
                }
                
                        }

        /**
         * Change ticks style
         *
         * @param int $style Ticks style
         */
        public function setTickStyle($style) {
                
                foreach($this->ticks as $tick) {
                        $tick->setStyle($style);
                }
                
                        }

        /**
         * Change ticks interval
         *
         * @param int $interval Ticks interval
         */
        public function setTickInterval($interval) {
                
                foreach($this->ticks as $tick) {
                        $tick->setInterval($interval);
                }
                
                        }

        /**
         * Change number of ticks relative to others ticks
         *
         * @param awTick $to Change number of theses ticks
         * @param awTick $from Ticks reference
         * @param float $number Number of ticks by the reference
         */
        public function setNumberByTick($to, $from, $number) {
                $this->ticks[$to]->setNumberByTick($this->ticks[$from], $number);
        }

        /**
         * Reverse ticks style
         */
        public function reverseTickStyle() {
                
                foreach($this->ticks as $tick) {
                        if($tick->getStyle() === awTick::IN) {
                                $tick->setStyle(awTick::OUT);
                        } else if($tick->getStyle() === awTick::OUT) {
                                $tick->setStyle(awTick::IN);
                        }
                }
                
                        }

        /**
         * Change interval of labels
         *
         * @param int $interval Interval
         */
        public function setLabelInterval($interval) {
                $this->auto(FALSE);
                $this->setTickInterval($interval);
                $this->label->setInterval($interval);
        }

        /**
         * Change number of labels
         *
         * @param int $number Number of labels to display (can be NULL)
         */
        public function setLabelNumber($number) {
                $this->auto(FALSE);
                $this->labelNumber = is_null($number) ? NULL : (int)$number;
        }

        /**
         * Get number of labels
         *
         * @return int
         */
        public function getLabelNumber() {
                return $this->labelNumber;
        }

        /**
         * Change precision of labels
         *
         * @param int $precision Precision
         */
        public function setLabelPrecision($precision) {
                $this->auto(FALSE);
                $function = 'axis'.time().'_'.(microtime() * 1000000);
                eval('function '.$function.'($value) {
                        return sprintf("%.'.(int)$precision.'f", $value);
                }');
                $this->label->setCallbackFunction($function);
        }

        /**
         * Change text of labels
         *
         * @param array $texts Some texts
         */
        public function setLabelText($texts) {
                if(is_array($texts)) {
                        $this->auto(FALSE);
                        $function = 'axis'.time().'_'.(microtime() * 1000000);
                        eval('function '.$function.'($value) {
                                $texts = '.var_export($texts, TRUE).';
                                return isset($texts[$value]) ? $texts[$value] : \'?\';
                        }');
                        $this->label->setCallbackFunction($function);
                }
        }

        /**
         * Get the position of a point
         *
         * @param awAxis $xAxis X axis
         * @param awAxis $yAxis Y axis
         * @param awPoint $p Position of the point
         * @return Point Position on the axis
         */
        public static function toPosition(awAxis $xAxis, awAxis $yAxis, awPoint $p) {

                $p1 = $xAxis->getPointFromValue($p->x);
                $p2 = $yAxis->getPointFromValue($p->y);

                return new awPoint(
                        round($p1->x),
                        round($p2->y)
                );

        }

        /**
         * Change title alignment
         *
         * @param int $alignment New Alignment
         */
        public function setTitleAlignment($alignment) {

                switch($alignment) {

                        case awLabel::TOP :
                                $this->setTitlePosition(1);
                                $this->title->setAlign(NULL, awLabel::BOTTOM);
                                break;

                        case awLabel::BOTTOM :
                                $this->setTitlePosition(0);
                                $this->title->setAlign(NULL, awLabel::TOP);
                                break;

                        case awLabel::LEFT :
                                $this->setTitlePosition(0);
                                $this->title->setAlign(awLabel::LEFT);
                                break;

                        case awLabel::RIGHT :
                                $this->setTitlePosition(1);
                                $this->title->setAlign(awLabel::RIGHT);
                                break;

                }

        }

        /**
         * Change title position on the axis
         *
         * @param float $position A new awposition between 0 and 1
         */
        public function setTitlePosition($position) {
                $this->titlePosition = (float)$position;
        }

        /**
         * Change axis and axis title color
         *
         * @param awColor $color
         */
        public function setColor(awColor $color) {
                $this->color = $color;
                $this->title->setColor($color);
        }

        /**
         * Change axis padding
         *
         * @param int $left Left padding in pixels
         * @param int $right Right padding in pixels
         */
        public function setPadding($left, $right) {
                $this->padding->set($left, $right);
        }

        /**
         * Get axis padding
         *
         * @return Side
         */
        public function getPadding() {
                return $this->padding;
        }

        /**
         * Change axis range
         *
         * @param float $min
         * @param float $max
         */
        public function setRange($min, $max) {
                if($min !== NULL) {
                        $this->range[0] = (float)$min;
                }
                if($max !== NULL) {
                        $this->range[1] = (float)$max;
                }
        }

        /**
         * Get axis range
         *
         * @return array
         */
        public function getRange() {
                return $this->range;
        }

        /**
         * Change axis range callback function
         *
         * @param string $toValue Transform a position between 0 and 1 to a value
         * @param string $toPosition Transform a value to a position between 0 and 1 on the axis
         */
        public function setRangeCallback($toValue, $toPosition) {
                $this->rangeCallback = array(
                        'toValue' => (string)$toValue,
                        'toPosition' => (string)$toPosition
                );
        }

        /**
         * Center X values of the axis
         *
         * @param awAxis $axis An axis
         * @param float $value The reference value on the axis
         */
        public function setXCenter(awAxis $axis, $value) {

                // Check vector angle
                if($this->line->isVertical() === FALSE) {
                        awImage::drawError("Class Axis: setXCenter() can only be used on vertical axes.");
                }

                $p = $axis->getPointFromValue($value);

                $this->line->setX(
                        $p->x,
                        $p->x
                );

        }

        /**
         * Center Y values of the axis
         *
         * @param awAxis $axis An axis
         * @param float $value The reference value on the axis
         */
        public function setYCenter(awAxis $axis, $value) {

                // Check vector angle
                if($this->line->isHorizontal() === FALSE) {
                        awImage::drawError("Class Axis: setYCenter() can only be used on horizontal axes.");
                }

                $p = $axis->getPointFromValue($value);

                $this->line->setY(
                        $p->y,
                        $p->y
                );

        }

        /**
         * Get the distance between to values on the axis
         *
         * @param float $from The first value
         * @param float $to The last value
         * @return Point
         */
        public function getDistance($from, $to) {

                $p1 = $this->getPointFromValue($from);
                $p2 = $this->getPointFromValue($to);

                return $p1->getDistance($p2);

        }

        /**
         * Get a point on the axis from a value
         *
         * @param float $value
         * @return Point
         */
        protected function getPointFromValue($value) {

                $callback = $this->rangeCallback['toPosition'];

                list($min, $max) = $this->range;
                $position = $callback($value, $min, $max);

                return $this->getPointFromPosition($position);

        }

        /**
         * Get a point on the axis from a position
         *
         * @param float $position A position between 0 and 1
         * @return Point
         */
        protected function getPointFromPosition($position) {

                $vector = $this->getVector();

                $angle = $vector->getAngle();
                $size = $vector->getSize();

                return $vector->p1->move(
                        cos($angle) * $size * $position,
                        -1 * sin($angle) * $size * $position
                );

        }

        /**
         * Draw axis
         *
         * @param awDriver $driver A driver
         */
        public function draw(awDriver $driver) {

                if($this->hide) {
                        return;
                }

                $vector = $this->getVector();

                // Draw axis ticks
                $this->drawTicks($driver, $vector);

                // Draw axis line
                $this->line($driver);

                // Draw labels
                $this->drawLabels($driver);

                // Draw axis title
                $p = $this->getPointFromPosition($this->titlePosition);
                $this->title->draw($driver, $p);

        }

        public function autoScale() {

                if($this->isAuto() === FALSE) {
                        return;
                }

                list($min, $max) = $this->getRange();
                $interval = $max - $min;

                if($interval > 0) {
                        $partMax = $max / $interval;
                        $partMin = $min / $interval;
                } else {
                        $partMax = 0;
                        $partMin = 0;
                }

                $difference = log($interval) / log(10);
                $difference = floor($difference);

                $pow = pow(10, $difference);

                if($pow > 0) {
                        $intervalNormalize = $interval / $pow;
                } else {
                        $intervalNormalize = 0;
                }

                if($difference <= 0) {

                        $precision = $difference * -1 + 1;

                        if($intervalNormalize > 2) {
                                $precision--;
                        }

                } else {
                        $precision = 0;
                }

                if($min != 0 and $max != 0) {
                        $precision++;
                }

                if($this->label->getCallbackFunction() === NULL) {
                        $this->setLabelPrecision($precision);
                }

                if($intervalNormalize <= 1.5) {
                        $intervalReal = 1.5;
                        $labelNumber = 4;
                } else if($intervalNormalize <= 2) {
                        $intervalReal = 2;
                        $labelNumber = 5;
                } else if($intervalNormalize <= 3) {
                        $intervalReal = 3;
                        $labelNumber = 4;
                } else if($intervalNormalize <= 4) {
                        $intervalReal = 4;
                        $labelNumber = 5;
                } else if($intervalNormalize <= 5) {
                        $intervalReal = 5;
                        $labelNumber = 6;
                } else if($intervalNormalize <= 8) {
                        $intervalReal = 8;
                        $labelNumber = 5;
                } else if($intervalNormalize <= 10) {
                        $intervalReal = 10;
                        $labelNumber = 6;
                }

                if($min == 0) {

                        $this->setRange(
                                $min,
                                $intervalReal * $pow
                        );

                } else if($max == 0) {

                        $this->setRange(
                                $intervalReal * $pow * -1,
                                0
                        );

                }

                $this->setLabelNumber($labelNumber);

        }

        protected function line(awDriver $driver) {

                $driver->line(
                        $this->color,
                        $this->line
                );

        }

        protected function drawTicks(awDriver $driver, awVector $vector) {

                foreach($this->ticks as $tick) {
                        $tick->setColor($this->color);
                        $tick->draw($driver, $vector);
                }

        }

        protected function drawLabels($driver) {

                if($this->labelNumber !== NULL) {
                        list($min, $max) = $this->range;
                        $number = $this->labelNumber - 1;
                        if($number < 1) {
                                return;
                        }
                        $function = $this->rangeCallback['toValue'];
                        $labels = array();
                        for($i = 0; $i <= $number; $i++) {
                                $labels[] = $function($i / $number, $min, $max);
                        }
                        $this->label->set($labels);
                }

                $labels = $this->label->count();

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

                        $p = $this->getPointFromValue($this->label->get($i));
                        $this->label->draw($driver, $p, $i);

                }

        }

        protected function getVector() {

                $angle = $this->line->getAngle();

                // Compute paddings
                $vector = new awVector(
                        $this->line->p1->move(
                                cos($angle) * $this->padding->left,
                                -1 * sin($angle) * $this->padding->left
                        ),
                        $this->line->p2->move(
                                -1 * cos($angle) * $this->padding->right,
                                -1 * -1 * sin($angle) * $this->padding->right
                        )
                );

                return $vector;

        }

        public function __clone() {

                $this->label = clone $this->label;
                $this->line = clone $this->line;
                $this->title = clone $this->title;

                foreach($this->ticks as $name => $tick) {
                        $this->ticks[$name] = clone $tick;
                }

        }

}

registerClass('Axis');

function toProportionalValue($position, $min, $max) {
        return $min + ($max - $min) * $position;
}

function toProportionalPosition($value, $min, $max) {
        if($max - $min == 0) {
                return 0;
        }
        return ($value - $min) / ($max - $min);
}
?>