blob: afa1e21bbc181c2afdec0c0ac8d9f0a1891f96c0 [file] [log] [blame]
<?php
/**
* File containing the abstract ezcGraphRenderer 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//
* @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*/
/**
* Abstract class to transform the basic chart components. To be extended by
* three- and twodimensional renderers.
*
* @version //autogentag//
* @package Graph
*/
abstract class ezcGraphRenderer
{
/**
* Driver used to render results
*
* @var ezcGraphDriver
*/
protected $driver;
/**
* Axis space used for the x axis
*
* @var float
*/
protected $xAxisSpace = false;
/**
* Axis space used for the y axis
*
* @var float
*/
protected $yAxisSpace = false;
/**
* Context sensitive references to chart elements to use for referencing
* image elements depending on the output driver, like image maps, etc.
*
* @var array
*/
protected $elements = array();
/**
* Set renderers driver
*
* This method is used internally to set the direver used inside the chart
* class in the renderer. If you want to change the driver used for your
* chart, you should do this using the chart driver property, like:
*
* <code>
* $chart = new ezcGraphPieChart();
* $chart->driver = new ezcGraphSvgDriver();
* </code>
*
* @param ezcGraphDriver $driver Output driver
* @return void
*/
public function setDriver( ezcGraphDriver $driver )
{
$this->driver = $driver;
}
/**
* Adds a element reference for context
*
* @param ezcGraphContext $context Dataoint context
* @param mixed $reference Driver dependant reference
* @return void
*/
protected function addElementReference( ezcGraphContext $context, $reference )
{
$this->elements['data'][$context->dataset][$context->datapoint][] = $reference;
}
/**
* Return all chart element references
*
* Returns element references for the data sets in the chart, so the
* created graphic may be enhanced later.
*
* The resulting array looks like:
* <code>
* array (
* legend_url => array (
* $name => $url | null,
* ...
* ),
* legend => array (
* $name => $data,
* ...
* )
* data => array (
* $dataset => array (
* $name => $data,
* ...
* ),
* ...
* )
* )
* </code>
*
* The legend elements won't show up in the array, if there is no legend
* redered. The URLs are only available, if the url property has been set
* on the respective dataset.
*
* The data assigned to the legends and data elements is completely direver
* dependent. In the SVG and Flash driver there will jsut be some IDs,
* which allow you to reference the affected elements or element groups
* inside the flash or SVG file.
*
* For bitmap formats, like in the Cairo or GD driver, $data will be an
* array of ezcGraphCoordinate objects, which roughly describe the outline
* of the referenced element. For circles and alike the resolution of this
* outline can be configured in the respective driver.
*
* @return array
*/
public function getElementReferences()
{
return $this->elements;
}
/**
* __get
*
* @param string $propertyName
* @throws ezcBasePropertyNotFoundException
* If a the value for the property options is not an instance of
* @return mixed
* @ignore
*/
public function __get( $propertyName )
{
switch ( $propertyName )
{
case 'xAxisSpace':
case 'yAxisSpace':
return $this->$propertyName;
case 'elements':
return $this->elements;
default:
throw new ezcBasePropertyNotFoundException( $propertyName );
}
}
/**
* Draw pie segment
*
* Draws a single pie segment
*
* @param ezcGraphBoundings $boundings Chart boundings
* @param ezcGraphContext $context Context of call
* @param ezcGraphColor $color Color of pie segment
* @param float $startAngle Start angle
* @param float $endAngle End angle
* @param mixed $label Label of pie segment
* @param bool $moveOut Move out from middle for hilighting
* @return void
*/
abstract public function drawPieSegment(
ezcGraphBoundings $boundings,
ezcGraphContext $context,
ezcGraphColor $color,
$startAngle = .0,
$endAngle = 360.,
$label = false,
$moveOut = false
);
/**
* Draw bar
*
* Draws a bar as a data element in a line chart
*
* @param ezcGraphBoundings $boundings Chart boundings
* @param ezcGraphContext $context Context of call
* @param ezcGraphColor $color Color of line
* @param ezcGraphCoordinate $position Position of data point
* @param float $stepSize Space which can be used for bars
* @param int $dataNumber Number of dataset
* @param int $dataCount Count of datasets in chart
* @param int $symbol Symbol to draw for line
* @param float $axisPosition Position of axis for drawing filled lines
* @return void
*/
abstract public function drawBar(
ezcGraphBoundings $boundings,
ezcGraphContext $context,
ezcGraphColor $color,
ezcGraphCoordinate $position,
$stepSize,
$dataNumber = 1,
$dataCount = 1,
$symbol = ezcGraph::NO_SYMBOL,
$axisPosition = 0.
);
/**
* Draw data line
*
* Draws a line as a data element in a line chart
*
* @param ezcGraphBoundings $boundings Chart boundings
* @param ezcGraphContext $context Context of call
* @param ezcGraphColor $color Color of line
* @param ezcGraphCoordinate $start Starting point
* @param ezcGraphCoordinate $end Ending point
* @param int $dataNumber Number of dataset
* @param int $dataCount Count of datasets in chart
* @param int $symbol Symbol to draw for line
* @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor
* @param ezcGraphColor $fillColor Color to fill line with
* @param float $axisPosition Position of axis for drawing filled lines
* @param float $thickness Line thickness
* @return void
*/
abstract public function drawDataLine(
ezcGraphBoundings $boundings,
ezcGraphContext $context,
ezcGraphColor $color,
ezcGraphCoordinate $start,
ezcGraphCoordinate $end,
$dataNumber = 1,
$dataCount = 1,
$symbol = ezcGraph::NO_SYMBOL,
ezcGraphColor $symbolColor = null,
ezcGraphColor $fillColor = null,
$axisPosition = 0.,
$thickness = 1.
);
/**
* Draws a highlight textbox for a datapoint.
*
* A highlight textbox for line and bar charts means a box with the current
* value in the graph.
*
* @param ezcGraphBoundings $boundings Chart boundings
* @param ezcGraphContext $context Context of call
* @param ezcGraphCoordinate $end Ending point
* @param float $axisPosition Position of axis for drawing filled lines
* @param int $dataNumber Number of dataset
* @param int $dataCount Count of datasets in chart
* @param ezcGraphFontOptions $font Font used for highlight string
* @param string $text Acutual value
* @param int $size Size of highlight text
* @param ezcGraphColor $markLines
* @param int $xOffset
* @param int $yOffset
* @param float $stepSize
* @param int $type
* @return void
*/
abstract public function drawDataHighlightText(
ezcGraphBoundings $boundings,
ezcGraphContext $context,
ezcGraphCoordinate $end,
$axisPosition = 0.,
$dataNumber = 1,
$dataCount = 1,
ezcGraphFontOptions $font,
$text,
$size,
ezcGraphColor $markLines = null,
$xOffset = 0,
$yOffset = 0,
$stepSize = 0.,
$type = ezcGraph::LINE
);
/**
* Draw legend
*
* Will draw a legend in the bounding box
*
* @param ezcGraphBoundings $boundings Bounding of legend
* @param ezcGraphChartElementLegend $legend Legend to draw
* @param int $type Type of legend: Protrait or landscape
* @return void
*/
abstract public function drawLegend(
ezcGraphBoundings $boundings,
ezcGraphChartElementLegend $legend,
$type = ezcGraph::VERTICAL
);
/**
* Draw box
*
* Box are wrapping each major chart element and draw border, background
* and title to each chart element.
*
* Optionally a padding and margin for each box can be defined.
*
* @param ezcGraphBoundings $boundings Boundings of the box
* @param ezcGraphColor $background Background color
* @param ezcGraphColor $borderColor Border color
* @param int $borderWidth Border width
* @param int $margin Margin
* @param int $padding Padding
* @param mixed $title Title of the box
* @param int $titleSize Size of title in the box
* @return ezcGraphBoundings Remaining inner boundings
*/
abstract public function drawBox(
ezcGraphBoundings $boundings,
ezcGraphColor $background = null,
ezcGraphColor $borderColor = null,
$borderWidth = 0,
$margin = 0,
$padding = 0,
$title = false,
$titleSize = 16
);
/**
* Draw text
*
* Draws the provided text in the boundings
*
* @param ezcGraphBoundings $boundings Boundings of text
* @param string $text Text
* @param int $align Alignement of text
* @param ezcGraphRotation $rotation
* @return void
*/
abstract public function drawText(
ezcGraphBoundings $boundings,
$text,
$align = ezcGraph::LEFT,
ezcGraphRotation $rotation = null
);
/**
* Draw axis
*
* Draws an axis form the provided start point to the end point. A specific
* angle of the axis is not required.
*
* For the labeleing of the axis a sorted array with major steps and an
* array with minor steps is expected, which are build like this:
* array(
* array(
* 'position' => (float),
* 'label' => (string),
* )
* )
* where the label is optional.
*
* The label renderer class defines how the labels are rendered. For more
* documentation on this topic have a look at the basic label renderer
* class.
*
* Additionally it can be specified if a major and minor grid are rendered
* by defining a color for them. The axis label is used to add a caption
* for the axis.
*
* @param ezcGraphBoundings $boundings Boundings of axis
* @param ezcGraphCoordinate $start Start point of axis
* @param ezcGraphCoordinate $end Endpoint of axis
* @param ezcGraphChartElementAxis $axis Axis to render
* @param ezcGraphAxisLabelRenderer $labelClass Used label renderer
* @return void
*/
abstract public function drawAxis(
ezcGraphBoundings $boundings,
ezcGraphCoordinate $start,
ezcGraphCoordinate $end,
ezcGraphChartElementAxis $axis,
ezcGraphAxisLabelRenderer $labelClass = null
);
/**
* Draw axis arrow head
*
* Draw an arrow head at the specified position using specified size
* and direction of the error head. Repsects the axisEndStyle option in
* the base renderer options class.
*
* @param ezcGraphCoordinate $position
* @param ezcGraphVector $direction
* @param float $size
* @param ezcGraphColor $color
* @return void
*/
protected function drawAxisArrowHead( ezcGraphCoordinate $position, ezcGraphVector $direction, $size, ezcGraphColor $color )
{
$orthogonalDirection = clone $direction;
$orthogonalDirection->rotateClockwise();
if ( $this->options->axisEndStyle === ezcGraph::ARROW )
{
$this->driver->drawPolygon(
array(
new ezcGraphCoordinate(
$position->x,
$position->y
),
new ezcGraphCoordinate(
$position->x
- $orthogonalDirection->x * $size / 2
+ $direction->x * $size,
$position->y
- $orthogonalDirection->y * $size / 2
+ $direction->y * $size
),
new ezcGraphCoordinate(
$position->x
+ $orthogonalDirection->x * $size / 2
+ $direction->x * $size,
$position->y
+ $orthogonalDirection->y * $size / 2
+ $direction->y * $size
),
),
$color,
true
);
}
elseif ( $this->options->axisEndStyle !== ezcGraph::NO_SYMBOL )
{
$topLeft = new ezcGraphCoordinate(
$position->x
+ $orthogonalDirection->x * $size / 2
+ $direction->x * $size,
$position->y
+ $orthogonalDirection->y * $size / 2
+ $direction->y * $size
);
$bottomRight = new ezcGraphCoordinate(
$position->x
- $orthogonalDirection->x * $size / 2,
$position->y
- $orthogonalDirection->y * $size / 2
);
$this->drawSymbol(
$boundings = new ezcGraphBoundings(
min( $topLeft->x, $bottomRight->x ),
min( $topLeft->y, $bottomRight->y ),
max( $topLeft->x, $bottomRight->x ),
max( $topLeft->y, $bottomRight->y )
),
$color,
$this->options->axisEndStyle
);
}
}
/**
* Draw background image
*
* Draws a background image at the defined position. If repeat is set the
* background image will be repeated like any texture.
*
* @param ezcGraphBoundings $boundings Boundings for the background image
* @param string $file Filename of background image
* @param int $position Position of background image
* @param int $repeat Type of repetition
* @return void
*/
abstract public function drawBackgroundImage(
ezcGraphBoundings $boundings,
$file,
$position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE
$repeat = ezcGraph::NO_REPEAT
);
/**
* Draw Symbol
*
* Draws a single symbol defined by the symbol constants in ezcGraph. for
* NO_SYMBOL a rect will be drawn.
*
* @param ezcGraphBoundings $boundings Boundings of symbol
* @param ezcGraphColor $color Color of symbol
* @param int $symbol Type of symbol
* @return void
*/
public function drawSymbol(
ezcGraphBoundings $boundings,
ezcGraphColor $color,
$symbol = ezcGraph::NO_SYMBOL )
{
switch ( $symbol )
{
case ezcGraph::NO_SYMBOL:
case ezcGraph::SQUARE:
$return = $this->driver->drawPolygon(
array(
new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
),
$color,
true
);
// Draw optional gleam
if ( $this->options->legendSymbolGleam !== false )
{
$return = $this->driver->drawPolygon(
array(
$topLeft = new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
),
new ezcGraphCoordinate(
$boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
),
$bottomRight = new ezcGraphCoordinate(
$boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
$boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
),
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
$boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
),
),
new ezcGraphLinearGradient(
$bottomRight,
$topLeft,
$color->darken( -$this->options->legendSymbolGleam ),
$color->darken( $this->options->legendSymbolGleam )
),
true
);
}
return $return;
case ezcGraph::BOX:
$return = $this->driver->drawPolygon(
array(
new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
),
$color,
false
);
return $return;
case ezcGraph::DIAMOND:
$return = $this->driver->drawPolygon(
array(
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
$boundings->y0
),
new ezcGraphCoordinate(
$boundings->x1,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
),
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
$boundings->y1
),
new ezcGraphCoordinate(
$boundings->x0,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
),
),
$color,
true
);
// Draw optional gleam
if ( $this->options->legendSymbolGleam !== false )
{
$return = $this->driver->drawPolygon(
array(
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
),
new ezcGraphCoordinate(
$boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
),
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
$boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
),
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
),
),
new ezcGraphLinearGradient(
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.353553391,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.353553391
),
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * ( 1 - 0.353553391 ),
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * ( 1 - 0.353553391 )
),
$color->darken( -$this->options->legendSymbolGleam ),
$color->darken( $this->options->legendSymbolGleam )
),
true
);
}
return $return;
case ezcGraph::BULLET:
$return = $this->driver->drawCircle(
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
),
$boundings->x1 - $boundings->x0,
$boundings->y1 - $boundings->y0,
$color,
true
);
// Draw optional gleam
if ( $this->options->legendSymbolGleam !== false )
{
$return = $this->driver->drawCircle(
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
),
( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize,
new ezcGraphLinearGradient(
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.292893219,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.292893219
),
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.707106781,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.707106781
),
$color->darken( -$this->options->legendSymbolGleam ),
$color->darken( $this->options->legendSymbolGleam )
),
true
);
}
return $return;
case ezcGraph::CIRCLE:
return $this->driver->drawCircle(
new ezcGraphCoordinate(
$boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
$boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
),
$boundings->x1 - $boundings->x0,
$boundings->y1 - $boundings->y0,
$color,
false
);
}
}
/**
* Finish rendering
*
* Method is called before the final image is renderer, so that finishing
* operations can be performed here.
*
* @return void
*/
abstract protected function finish();
/**
* Reset renderer properties
*
* Reset all renderer properties, which were calculated during the
* rendering process, to offer a clean environment for rerendering.
*
* @return void
*/
protected function resetRenderer()
{
$this->xAxisSpace = false;
$this->yAxisSpace = false;
// Reset driver, maintaining its configuration
$driverClass = get_class( $this->driver );
$driverOptions = $this->driver->options;
$this->driver = new $driverClass();
$this->driver->options = $driverOptions;
}
/**
* Finally renders the image
*
* @param string $file Filename of destination file
* @return void
*/
public function render( $file = null )
{
$this->finish();
if ( $file === null )
{
$this->driver->renderToOutput();
}
else
{
$this->driver->render( $file );
}
$this->resetRenderer();
}
}
?>