Subversion Repositories Applications.gtt

Compare Revisions

Ignore whitespace Rev 59 → Rev 60

/trunk/classes/artichow/inc/Axis.class.php
New file
0,0 → 1,769
<?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);
}
?>