blob: afccc6a216d54b95695c4fba8922a32be30fc795 [file] [log] [blame]
<?php
/**
* File containing the abstract ezcGraphAxisLabelRenderer class
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package Graph
* @version //autogentag//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*/
/**
* Abstract class to render labels and grids on axis. Will be extended to
* make it possible using different algorithms for rendering axis labels.
*
* Implements basic methods to render the grid and steps on a axis.
*
* @property bool $majorStepCount
* Count of major steps.
* @property bool $minorStepCount
* Count of minor steps.
* @property int $majorStepSize
* Size of major steps.
* @property int $minorStepSize
* Size of minor steps.
* @property bool $innerStep
* Indicates if steps are shown on the inner side of axis.
* @property bool $outerStep
* Indicates if steps are shown on the outer side of axis.
* @property bool $outerGrid
* Indicates if the grid is shown on the outer side of axis.
* @property bool $showLabels
* Indicates if the labels should be shown
* @property int $labelPadding
* Padding of labels.
*
* @version //autogentag//
* @package Graph
*/
abstract class ezcGraphAxisLabelRenderer extends ezcBaseOptions
{
/**
* Driver to render axis labels
*
* @var ezcGraphDriver
*/
protected $driver;
/**
* Constructor
*
* @param array $options Default option array
* @return void
* @ignore
*/
public function __construct( array $options = array() )
{
$this->properties['majorStepCount'] = false;
$this->properties['minorStepCount'] = false;
$this->properties['majorStepSize'] = 3;
$this->properties['minorStepSize'] = 1;
$this->properties['innerStep'] = true;
$this->properties['outerStep'] = false;
$this->properties['outerGrid'] = false;
$this->properties['showLabels'] = true;
$this->properties['labelPadding'] = 2;
parent::__construct( $options );
}
/**
* __set
*
* @param mixed $propertyName
* @param mixed $propertyValue
* @throws ezcBaseValueException
* If a submitted parameter was out of range or type.
* @throws ezcBasePropertyNotFoundException
* If a the value for the property options is not an instance of
* @return void
* @ignore
*/
public function __set( $propertyName, $propertyValue )
{
switch ( $propertyName )
{
case 'driver':
if ( $propertyValue instanceof ezcGraphDriver )
{
$this->properties['driver'] = $propertyValue;
}
else
{
throw new ezcGraphInvalidDriverException( $propertyValue );
}
break;
case 'majorStepCount':
if ( ( $propertyValue !== false ) &&
!is_numeric( $propertyValue ) ||
( $propertyValue < 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
}
$this->properties['majorStepCount'] = (int) $propertyValue;
break;
case 'minorStepCount':
if ( ( $propertyValue !== false ) &&
!is_numeric( $propertyValue ) ||
( $propertyValue < 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
}
$this->properties['minorStepCount'] = (int) $propertyValue;
break;
case 'majorStepSize':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue < 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
}
$this->properties['majorStepSize'] = (int) $propertyValue;
break;
case 'minorStepSize':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue < 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
}
$this->properties['minorStepSize'] = (int) $propertyValue;
break;
case 'innerStep':
if ( !is_bool( $propertyValue ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
}
$this->properties['innerStep'] = (bool) $propertyValue;
break;
case 'outerStep':
if ( !is_bool( $propertyValue ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
}
$this->properties['outerStep'] = (bool) $propertyValue;
break;
case 'outerGrid':
if ( !is_bool( $propertyValue ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
}
$this->properties['outerGrid'] = (bool) $propertyValue;
break;
case 'showLabels':
if ( !is_bool( $propertyValue ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
}
$this->properties['showLabels'] = (bool) $propertyValue;
break;
case 'labelPadding':
if ( !is_numeric( $propertyValue ) ||
( $propertyValue < 0 ) )
{
throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
}
$this->properties['labelPadding'] = (int) $propertyValue;
break;
default:
throw new ezcBasePropertyNotFoundException( $propertyName );
}
}
/**
* Checks for the cutting point of two lines.
*
* The lines are given by a start position and the direction of the line,
* both as instances of {@link ezcGraphCoordinate}. If no cutting point
* could be calculated, because the lines are parallel the function will
* return false. Otherwise the factor returned can be used to calculate the
* cutting point using the following equatation:
* point = $aStart + $factor * $aDir;
*
* We return the factor instead of the resulting point because it can be
* easily determined from the factor if the cutting point is in "behind"
* the line starting point, or if the distance to the cutting point is
* bigger then the direction vector is long ( $factor > 1 ).
*
* @param ezcGraphCoordinate $aStart
* @param ezcGraphCoordinate $aDir
* @param ezcGraphCoordinate $bStart
* @param ezcGraphCoordinate $bDir
* @return mixed
*/
public function determineLineCuttingPoint( ezcGraphCoordinate $aStart, ezcGraphCoordinate $aDir, ezcGraphCoordinate $bStart, ezcGraphCoordinate $bDir )
{
// Check if lines are parallel
if ( ( ( abs( $aDir->x ) < .000001 ) && ( abs( $bDir->x ) < .000001 ) ) ||
( ( abs( $aDir->y ) < .000001 ) && ( abs( $bDir->y ) < .000001 ) ) ||
( ( abs( $aDir->x * $bDir->x * $aDir->y * $bDir->y ) > .000001 ) &&
( abs( ( $aDir->x / $aDir->y ) - ( $bDir->x / $bDir->y ) ) < .000001 )
)
)
{
return false;
}
// Use ? : to prevent division by zero
$denominator =
( abs( $aDir->y ) > .000001 ? $bDir->y / $aDir->y : .0 ) -
( abs( $aDir->x ) > .000001 ? $bDir->x / $aDir->x : .0 );
// Solve equatation
if ( abs( $denominator ) < .000001 )
{
return - (
( abs( $aDir->y ) > .000001 ? $bStart->y / $aDir->y : .0 ) -
( abs( $aDir->y ) > .000001 ? $aStart->y / $aDir->y : .0 ) -
( abs( $aDir->x ) > .000001 ? $bStart->x / $aDir->x : .0 ) +
( abs( $aDir->x ) > .000001 ? $aStart->x / $aDir->x : .0 )
);
}
else
{
return - (
( abs( $aDir->y ) > .000001 ? $bStart->y / $aDir->y : .0 ) -
( abs( $aDir->y ) > .000001 ? $aStart->y / $aDir->y : .0 ) -
( abs( $aDir->x ) > .000001 ? $bStart->x / $aDir->x : .0 ) +
( abs( $aDir->x ) > .000001 ? $aStart->x / $aDir->x : .0 )
) / $denominator;
}
}
/**
* Draw single step on a axis
*
* Draws a step on a axis at the current position
*
* @param ezcGraphRenderer $renderer Renderer to draw the step with
* @param ezcGraphCoordinate $position Position of step
* @param ezcGraphCoordinate $direction Direction of axis
* @param int $axisPosition Position of axis
* @param int $size Step size
* @param ezcGraphColor $color Color of axis
* @return void
*/
public function drawStep( ezcGraphRenderer $renderer, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, $axisPosition, $size, ezcGraphColor $color )
{
if ( ! ( $this->innerStep || $this->outerStep ) )
{
return false;
}
$drawStep = false;
if ( ( ( $axisPosition === ezcGraph::CENTER ) && $this->innerStep ) ||
( ( $axisPosition === ezcGraph::BOTTOM ) && $this->outerStep ) ||
( ( $axisPosition === ezcGraph::TOP ) && $this->innerStep ) ||
( ( $axisPosition === ezcGraph::RIGHT ) && $this->outerStep ) ||
( ( $axisPosition === ezcGraph::LEFT ) && $this->innerStep ) )
{
// Turn direction vector to left by 90 degrees and multiply
// with major step size
$stepStart = new ezcGraphCoordinate(
$position->x + $direction->y * $size,
$position->y - $direction->x * $size
);
$drawStep = true;
}
else
{
$stepStart = $position;
}
if ( ( ( $axisPosition === ezcGraph::CENTER ) && $this->innerStep ) ||
( ( $axisPosition === ezcGraph::BOTTOM ) && $this->innerStep ) ||
( ( $axisPosition === ezcGraph::TOP ) && $this->outerStep ) ||
( ( $axisPosition === ezcGraph::RIGHT ) && $this->innerStep ) ||
( ( $axisPosition === ezcGraph::LEFT ) && $this->outerStep ) )
{
// Turn direction vector to right by 90 degrees and multiply
// with major step size
$stepEnd = new ezcGraphCoordinate(
$position->x - $direction->y * $size,
$position->y + $direction->x * $size
);
$drawStep = true;
}
else
{
$stepEnd = $position;
}
if ( $drawStep )
{
$renderer->drawStepLine(
$stepStart,
$stepEnd,
$color
);
}
}
/**
* Draw non-rectangular grid lines grid
*
* Draws a grid line at the current position, for non-rectangular axis.
*
* @param ezcGraphRenderer $renderer Renderer to draw the grid with
* @param ezcGraphBoundings $boundings Boundings of axis
* @param ezcGraphCoordinate $position Position of step
* @param ezcGraphCoordinate $direction Direction of axis
* @param ezcGraphColor $color Color of axis
* @return void
*/
protected function drawNonRectangularGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color )
{
// Direction of grid line is direction of axis turned right by 90
// degrees
$gridDirection = new ezcGraphCoordinate(
$direction->y,
- $direction->x
);
$cuttingPoints = array();
foreach ( array( // Bounding lines
array(
'start' => new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
'dir' => new ezcGraphCoordinate( 0, $boundings->y1 - $boundings->y0 )
),
array(
'start' => new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
'dir' => new ezcGraphCoordinate( $boundings->x1 - $boundings->x0, 0 )
),
array(
'start' => new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
'dir' => new ezcGraphCoordinate( 0, $boundings->y0 - $boundings->y1 )
),
array(
'start' => new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
'dir' => new ezcGraphCoordinate( $boundings->x0 - $boundings->x1, 0 )
),
) as $boundingLine )
{
// Test for cutting points with bounding lines, where cutting
// position is between 0 and 1, which means, that the line is hit
// on the bounding box rectangle. Use these points as a start and
// ending point for the grid lines. There should *always* be
// exactly two points returned.
$cuttingPosition = $this->determineLineCuttingPoint(
$boundingLine['start'],
$boundingLine['dir'],
$position,
$gridDirection
);
if ( $cuttingPosition === false )
{
continue;
}
$cuttingPosition = abs( $cuttingPosition );
if ( ( $cuttingPosition >= 0 ) &&
( $cuttingPosition <= 1 ) )
{
$cuttingPoints[] = new ezcGraphCoordinate(
$boundingLine['start']->x + $cuttingPosition * $boundingLine['dir']->x,
$boundingLine['start']->y + $cuttingPosition * $boundingLine['dir']->y
);
}
}
if ( count( $cuttingPoints ) < 2 )
{
// This should not happpen
return false;
}
// Finally draw grid line
$renderer->drawGridLine(
$cuttingPoints[0],
$cuttingPoints[1],
$color
);
}
/**
* Draw rectangular grid
*
* Draws a grid line at the current position for rectangular directed axis.
*
* Method special for rectangularly directed axis to minimize the floating
* point calculation inaccuracies. Those are not necessary for rectangles,
* while for non-rectangular directed axis.
*
* @param ezcGraphRenderer $renderer Renderer to draw the grid with
* @param ezcGraphBoundings $boundings Boundings of axis
* @param ezcGraphCoordinate $position Position of step
* @param ezcGraphCoordinate $direction Direction of axis
* @param ezcGraphColor $color Color of axis
* @return void
*/
protected function drawRectangularGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color )
{
if ( abs( $direction->x ) < .00001 )
{
$renderer->drawGridLine(
new ezcGraphCoordinate(
$boundings->x0,
$position->y
),
new ezcGraphCoordinate(
$boundings->x1,
$position->y
),
$color
);
}
else
{
$renderer->drawGridLine(
new ezcGraphCoordinate(
$position->x,
$boundings->y0
),
new ezcGraphCoordinate(
$position->x,
$boundings->y1
),
$color
);
}
}
/**
* Draw grid
*
* Draws a grid line at the current position
*
* @param ezcGraphRenderer $renderer Renderer to draw the grid with
* @param ezcGraphBoundings $boundings Boundings of axis
* @param ezcGraphCoordinate $position Position of step
* @param ezcGraphCoordinate $direction Direction of axis
* @param ezcGraphColor $color Color of axis
* @return void
*/
protected function drawGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color )
{
// Check if the axis direction is rectangular
if ( ( abs( $direction->x ) < .00001 ) ||
( abs( $direction->y ) < .00001 ) )
{
return $this->drawRectangularGrid( $renderer, $boundings, $position, $direction, $color );
}
else
{
return $this->drawNonRectangularGrid( $renderer, $boundings, $position, $direction, $color );
}
}
/**
* Modify chart boundings
*
* Optionally modify boundings of chart data
*
* @param ezcGraphBoundings $boundings Current boundings of chart
* @param ezcGraphCoordinate $direction Direction of the current axis
* @return ezcGraphBoundings Modified boundings
*/
public function modifyChartBoundings( ezcGraphBoundings $boundings, ezcGraphCoordinate $direction )
{
return $boundings;
}
/**
* Modify chart data position
*
* Optionally additionally modify the coodinate of a data point
*
* @param ezcGraphCoordinate $coordinate Data point coordinate
* @return ezcGraphCoordinate Modified coordinate
*/
public function modifyChartDataPosition( ezcGraphCoordinate $coordinate )
{
return $coordinate;
}
/**
* Get axis space values
*
* Get axis space values, depending on passed parameters. If
* $innerBoundings is given it will be used to caclulat the axis spaces
* available for label rendering. If not given the legacy method will be
* used, which uses the xAxisSpace and yAxisSpace values calcualted by the
* renderer.
*
* Returns an array( $xSpace, $ySpace ), containing the irespective size in
* pixels. Additionally calculates the grid boundings passed by reference.
*
* @param ezcGraphRenderer $renderer
* @param ezcGraphBoundings $boundings
* @param mixed $innerBoundings
* @return array
*/
protected function getAxisSpace( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphChartElementAxis $axis, $innerBoundings, &$gridBoundings )
{
if ( $innerBoundings !== null )
{
$gridBoundings = clone $innerBoundings;
$xSpace = abs( $axis->position === ezcGraph::LEFT ? $innerBoundings->x0 - $boundings->x0 : $boundings->x1 - $innerBoundings->x1 );
$ySpace = abs( $axis->position === ezcGraph::TOP ? $innerBoundings->y0 - $boundings->y0 : $boundings->y1 - $innerBoundings->y1 );
}
else
{
$gridBoundings = new ezcGraphBoundings(
$boundings->x0 + ( $xSpace = abs( $renderer->xAxisSpace ) ),
$boundings->y0 + ( $ySpace = abs( $renderer->yAxisSpace ) ),
$boundings->x1 - $xSpace,
$boundings->y1 - $ySpace
);
}
if ( $this->outerGrid )
{
$gridBoundings = $boundings;
}
return array( $xSpace, $ySpace );
}
/**
* Render Axis labels
*
* Render labels for an axis.
*
* @param ezcGraphRenderer $renderer Renderer used to draw the chart
* @param ezcGraphBoundings $boundings Boundings of the axis
* @param ezcGraphCoordinate $start Axis starting point
* @param ezcGraphCoordinate $end Axis ending point
* @param ezcGraphChartElementAxis $axis Axis instance
* @return void
*/
abstract public function renderLabels(
ezcGraphRenderer $renderer,
ezcGraphBoundings $boundings,
ezcGraphCoordinate $start,
ezcGraphCoordinate $end,
ezcGraphChartElementAxis $axis
);
}
?>