<?php
/**
 * File containing the two dimensional renderer
 *
 * 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
 */
/**
 * Class to transform chart primitives into image primitives. Renders charts in
 * a two dimensional view.
 *
 * The class options are defined in the class {@link ezcGraphRenderer2dOptions}
 * extending the basic renderer options in {@link ezcGraphRendererOptions}.
 *
 * <code>
 *   $graph = new ezcGraphPieChart();
 *   $graph->palette = new ezcGraphPaletteBlack();
 *   $graph->title = 'Access statistics';
 *   $graph->options->label = '%2$d (%3$.1f%%)';
 *   
 *   $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
 *       'Mozilla' => 19113,
 *       'Explorer' => 10917,
 *       'Opera' => 1464,
 *       'Safari' => 652,
 *       'Konqueror' => 474,
 *   ) );
 *   $graph->data['Access statistics']->highlight['Explorer'] = true;
 *   
 *   // $graph->renderer = new ezcGraphRenderer2d();
 *   
 *   $graph->renderer->options->moveOut = .2;
 *   
 *   $graph->renderer->options->pieChartOffset = 63;
 *   
 *   $graph->renderer->options->pieChartGleam = .3;
 *   $graph->renderer->options->pieChartGleamColor = '#FFFFFF';
 *   $graph->renderer->options->pieChartGleamBorder = 2;
 *   
 *   $graph->renderer->options->pieChartShadowSize = 3;
 *   $graph->renderer->options->pieChartShadowColor = '#000000';
 *   
 *   $graph->renderer->options->legendSymbolGleam = .5;
 *   $graph->renderer->options->legendSymbolGleamSize = .9;
 *   $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF';
 *   
 *   $graph->renderer->options->pieChartSymbolColor = '#BABDB688';
 *   
 *   $graph->render( 400, 150, 'tutorial_pie_chart_pimped.svg' );
 * </code>
 *
 * @version //autogentag//
 * @package Graph
 * @mainclass
 */
class ezcGraphRenderer2d 
    extends
        ezcGraphRenderer
    implements
        ezcGraphRadarRenderer, ezcGraphStackedBarsRenderer, ezcGraphOdometerRenderer
{

    /**
     * Pie segment labels divided into two array, containing the labels on the
     * left and right side of the pie chart center.
     * 
     * @var array
     */
    protected $pieSegmentLabels = array(
        0 => array(),
        1 => array(),
    );

    /**
     * Contains the boundings used for pie segments
     * 
     * @var ezcGraphBoundings
     */
    protected $pieSegmentBoundings = false;

    /**
     * Array with symbols for post processing, which ensures, that the symbols
     * are rendered topmost.
     * 
     * @var array
     */
    protected $linePostSymbols = array();

    /**
     * Options 
     * 
     * @var ezcGraphRenderer2dOptions
     */
    protected $options;

    /**
     * Collect axis labels, so that the axis are drawn, when all axis spaces 
     * are known.
     * 
     * @var array
     */
    protected $axisLabels = array();

    /**
     * Collects circle sectors to draw shadow in background of all circle 
     * sectors.
     * 
     * @var array
     */
    protected $circleSectors = array();

    /**
     * Constructor
     * 
     * @param array $options Default option array
     * @return void
     * @ignore
     */
    public function __construct( array $options = array() )
    {
        $this->options = new ezcGraphRenderer2dOptions( $options );
    }

    /**
     * __get 
     * 
     * @param mixed $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 'options':
                return $this->options;
            default:
                return parent::__get( $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
     */
    public function drawPieSegment(
        ezcGraphBoundings $boundings,
        ezcGraphContext $context,
        ezcGraphColor $color,
        $startAngle = .0,
        $endAngle = 360.,
        $label = false,
        $moveOut = false )
    {
        // Apply offset
        $startAngle += $this->options->pieChartOffset;
        $endAngle += $this->options->pieChartOffset;

        // Calculate position and size of pie
        $center = new ezcGraphCoordinate(
            $boundings->x0 + ( $boundings->width ) / 2,
            $boundings->y0 + ( $boundings->height ) / 2
        );

        // Limit radius to fourth of width and half of height at maximum
        $radius = min(
            ( $boundings->width ) * $this->options->pieHorizontalSize,
            ( $boundings->height ) * $this->options->pieVerticalSize
        );

        // Move pie segment out of the center
        if ( $moveOut )
        {
            $direction = ( $endAngle + $startAngle ) / 2;

            $center = new ezcGraphCoordinate(
                $center->x + $this->options->moveOut * $radius * cos( deg2rad( $direction ) ),
                $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) )
            );
        }

        // Add circle sector to queue
        $this->circleSectors[] = array(
            'center' =>     $center,
            'context' =>    $context,
            'width' =>      $radius * 2 * ( 1 - $this->options->moveOut ),
            'height' =>     $radius * 2 * ( 1 - $this->options->moveOut ),
            'start' =>      $startAngle,
            'end' =>        $endAngle,
            'color' =>      $color,
        );

        if ( $label )
        {
            // Determine position of label
            $direction = ( $endAngle + $startAngle ) / 2;
            $pieSegmentCenter = new ezcGraphCoordinate(
                $center->x + cos( deg2rad( $direction ) ) * $radius,
                $center->y + sin( deg2rad( $direction ) ) * $radius
            );

            // Split labels up into left an right size and index them on their
            // y position
            $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][$pieSegmentCenter->y] = array(
                new ezcGraphCoordinate(
                    $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3,
                    $center->y + sin( deg2rad( $direction ) ) * $radius * 2 / 3
                ),
                $label,
                $context
            );
        }

        if ( !$this->pieSegmentBoundings )
        {
            $this->pieSegmentBoundings = $boundings;
        }
    }

    /**
     * Draws the collected circle sectors
     *
     * All circle sectors are collected and drawn later to be able to render 
     * the shadows of the pie segments in the back of all pie segments.
     * 
     * @return void
     */
    protected function finishCircleSectors()
    {
        // Add circle sector sides to simple z buffer prioriry list
        if ( $this->options->pieChartShadowSize > 0 )
        {
            foreach ( $this->circleSectors as $circleSector )
            {
                $this->driver->drawCircleSector(
                    new ezcGraphCoordinate(
                        $circleSector['center']->x + $this->options->pieChartShadowSize,
                        $circleSector['center']->y + $this->options->pieChartShadowSize
                    ),
                    $circleSector['width'],
                    $circleSector['height'],
                    $circleSector['start'],
                    $circleSector['end'],
                    $this->options->pieChartShadowColor->transparent( $this->options->pieChartShadowTransparency ),
                    true
                );
            }
        }

        foreach ( $this->circleSectors as $circleSector )
        {
            // Draw circle sector
            $this->addElementReference( 
                $circleSector['context'],
                $this->driver->drawCircleSector(
                    $circleSector['center'],
                    $circleSector['width'],
                    $circleSector['height'],
                    $circleSector['start'],
                    $circleSector['end'],
                    $circleSector['color'],
                    true
                )
            );

            $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder );
            $this->driver->drawCircleSector(
                $circleSector['center'],
                $circleSector['width'],
                $circleSector['height'],
                $circleSector['start'],
                $circleSector['end'],
                $darkenedColor,
                false
            );

            if ( $this->options->pieChartGleam !== false )
            {
                $gradient = new ezcGraphLinearGradient(
                    $circleSector['center'],
                    new ezcGraphCoordinate(
                        $circleSector['center']->x,
                        $circleSector['center']->y - $circleSector['height'] / 2
                    ),
                    $this->options->pieChartGleamColor->transparent( 1 ),
                    $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam )
                );

                $this->addElementReference( $circleSector['context'],
                    $this->driver->drawCircleSector(
                        $circleSector['center'],
                        $circleSector['width'] - $this->options->pieChartGleamBorder * 2,
                        $circleSector['height'] - $this->options->pieChartGleamBorder * 2,
                        $circleSector['start'],
                        $circleSector['end'],
                        $gradient,
                        true
                    )
                );

                $gradient = new ezcGraphLinearGradient(
                    new ezcGraphCoordinate(
                        $circleSector['center']->x,
                        $circleSector['center']->y + $circleSector['height'] / 4
                    ),
                    new ezcGraphCoordinate(
                        $circleSector['center']->x,
                        $circleSector['center']->y + $circleSector['height'] / 2
                    ),
                    $this->options->pieChartGleamColor->transparent( 1 ),
                    $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam )
                );

                $this->addElementReference( $circleSector['context'],
                    $this->driver->drawCircleSector(
                        $circleSector['center'],
                        $circleSector['width'] - $this->options->pieChartGleamBorder * 2,
                        $circleSector['height'] - $this->options->pieChartGleamBorder * 2,
                        $circleSector['start'],
                        $circleSector['end'],
                        $gradient,
                        true
                    )
                );
            }
        }
    }

    /**
     * Draws the collected pie segment labels
     *
     * All labels are collected and drawn later to be able to partition the 
     * available space for the labels woth knowledge of the overall label 
     * count and their required size and optimal position.
     * 
     * @return void
     */
    protected function finishPieSegmentLabels()
    {
        if ( $this->pieSegmentBoundings === false )
        {
            return true;
        }

        $boundings = $this->pieSegmentBoundings;

        // Calculate position and size of pie
        $center = new ezcGraphCoordinate(
            $boundings->x0 + ( $boundings->width ) / 2,
            $boundings->y0 + ( $boundings->height ) / 2
        );

        // Limit radius to fourth of width and half of height at maximum
        $radius = min(
            ( $boundings->width ) * $this->options->pieHorizontalSize,
            ( $boundings->height ) * $this->options->pieVerticalSize
        );

        $pieChartHeight = min(
            $radius * 2 + $radius / max( 1, count ( $this->pieSegmentLabels[0] ), count( $this->pieSegmentLabels[1] ) ) * 4,
            $boundings->height
        );
        $pieChartYPosition = $boundings->y0 + ( ( $boundings->height ) - $pieChartHeight ) / 2;

        // Calculate maximum height of labels
        $labelHeight = min(
            ( count( $this->pieSegmentLabels[0] )
                ? $pieChartHeight / count( $this->pieSegmentLabels[0] )
                : $pieChartHeight
            ),
            ( count( $this->pieSegmentLabels[1] )
                ? $pieChartHeight / count( $this->pieSegmentLabels[1] )
                : $pieChartHeight
            ),
            ( $pieChartHeight ) * $this->options->maxLabelHeight
        );

        $symbolSize = $this->options->symbolSize;

        foreach ( $this->pieSegmentLabels as $side => $labelPart )
        {
            $minHeight = $pieChartYPosition;
            $toShare = $pieChartHeight - count( $labelPart ) * $labelHeight;

            // Sort to draw topmost label first
            ksort( $labelPart );
            $sign = ( $side ? -1 : 1 );

            foreach ( $labelPart as $height => $label )
            {
                if ( ( $height - $labelHeight / 2 ) > $minHeight )
                {
                    $share = min( $toShare, ( $height - $labelHeight / 2) - $minHeight );
                    $minHeight += $share;
                    $toShare -= $share;
                }

                // Determine position of label
                $minHeight += max( 0, $height - $minHeight - $labelHeight ) / $pieChartHeight * $toShare;
                $verticalDistance = ( $center->y - $minHeight - $labelHeight / 2 ) / $radius;

                $labelPosition = new ezcGraphCoordinate(
                    $center->x - 
                    $sign * (
                        abs( $verticalDistance ) > 1
                        // If vertical distance to center is greater then the
                        // radius, use the centerline for the horizontal 
                        // position
                        ? max (
                            5,
                            abs( $label[0]->x - $center->x )
                        )
                        // Else place the label outside of the pie chart
                        : ( cos ( asin ( $verticalDistance ) ) * $radius + 
                            $symbolSize * (int) $this->options->showSymbol 
                        )
                    ),
                    $minHeight + $labelHeight / 2
                );

                if ( $this->options->showSymbol )
                {
                    // Draw label
                    $this->driver->drawLine(
                        $label[0],
                        $labelPosition,
                        $this->options->pieChartSymbolColor,
                        1
                    );

                    $this->driver->drawCircle(
                        $label[0],
                        $symbolSize,
                        $symbolSize,
                        $this->options->pieChartSymbolColor,
                        true
                    );
                    $this->driver->drawCircle(
                        $labelPosition,
                        $symbolSize,
                        $symbolSize,
                        $this->options->pieChartSymbolColor,
                        true
                    );
                }

                $this->addElementReference( 
                    $label[2],
                    $this->driver->drawTextBox(
                        $label[1],
                        new ezcGraphCoordinate(
                            ( !$side ? $boundings->x0 : $labelPosition->x + $symbolSize ),
                            $minHeight
                        ),
                        ( !$side ? $labelPosition->x - $boundings->x0 - $symbolSize : $boundings->x1 - $labelPosition->x - $symbolSize ),
                        $labelHeight,
                        ( !$side ? ezcGraph::RIGHT : ezcGraph::LEFT ) | ezcGraph::MIDDLE
                    )
                );

                // Add used space to minHeight
                $minHeight += $labelHeight;
            }
        }
    }

    /**
     * Draw the collected line symbols
     *
     * Symbols for the data lines are collected and delayed to ensure that 
     * they are not covered and hidden by other data lines.
     * 
     * @return void
     */
    protected function finishLineSymbols()
    {
        foreach ( $this->linePostSymbols as $symbol )
        {
            $this->addElementReference(
                $symbol['context'],
                $this->drawSymbol(
                    $symbol['boundings'],
                    $symbol['color'],
                    $symbol['symbol']
                )
            );
        }
    }
    
    /**
     * 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
     */
    public function drawBar(
        ezcGraphBoundings $boundings,
        ezcGraphContext $context,
        ezcGraphColor $color,
        ezcGraphCoordinate $position,
        $stepSize,
        $dataNumber = 1,
        $dataCount = 1,
        $symbol = ezcGraph::NO_SYMBOL,
        $axisPosition = 0. )
    {
        // Apply margin
        $margin = $stepSize * $this->options->barMargin;
        $padding = $stepSize * $this->options->barPadding;
        $barWidth = ( $stepSize - $margin ) / $dataCount - $padding;
        $offset = - $stepSize / 2 + $margin / 2 + ( $dataCount - $dataNumber - 1 ) * ( $padding + $barWidth ) + $padding / 2;

        $barPointArray = array(
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
                $boundings->y0 + ( $boundings->height ) * $axisPosition
            ),
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
                $boundings->y0 + ( $boundings->height ) * $position->y
            ),
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
                $boundings->y0 + ( $boundings->height ) * $position->y
            ),
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
                $boundings->y0 + ( $boundings->height ) * $axisPosition
            ),
        );

        $this->addElementReference(
            $context,
            $this->driver->drawPolygon(
                $barPointArray,
                $color,
                true
            )
        );

        if ( $this->options->dataBorder > 0 )
        {
            $darkened = $color->darken( $this->options->dataBorder );
            $this->driver->drawPolygon(
                $barPointArray,
                $darkened,
                false,
                1
            );
        }
    }

    /**
     * Draw stacked bar
     *
     * Draws a stacked bar part 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
     * @param ezcGraphCoordinate $position
     * @param float $stepSize Space which can be used for bars
     * @param int $symbol Symbol to draw for line
     * @param float $axisPosition Position of axis for drawing filled lines
     * @return void
     */
    public function drawStackedBar(
        ezcGraphBoundings $boundings,
        ezcGraphContext $context,
        ezcGraphColor $color,
        ezcGraphCoordinate $start,
        ezcGraphCoordinate $position,
        $stepSize,
        $symbol = ezcGraph::NO_SYMBOL,
        $axisPosition = 0. )
    {
        // Apply margin
        $margin = $stepSize * $this->options->barMargin;
        $barWidth = $stepSize - $margin;
        $offset = - $stepSize / 2 + $margin / 2;

        $barPointArray = array(
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
                $boundings->y0 + ( $boundings->height ) * $start->y
            ),
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
                $boundings->y0 + ( $boundings->height ) * $position->y
            ),
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
                $boundings->y0 + ( $boundings->height ) * $position->y
            ),
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
                $boundings->y0 + ( $boundings->height ) * $start->y
            ),
        );

        $this->addElementReference(
            $context,
            $this->driver->drawPolygon(
                $barPointArray,
                $color,
                true
            )
        );

        if ( $this->options->dataBorder > 0 )
        {
            $darkened = $color->darken( $this->options->dataBorder );
            $this->driver->drawPolygon(
                $barPointArray,
                $darkened,
                false,
                1
            );
        }
    }
    
    /**
     * 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
     */
    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. )
    {
        // Perhaps fill up line
        if ( $fillColor !== null &&
             $start->x != $end->x )
        {
            $startValue = $axisPosition - $start->y;
            $endValue = $axisPosition - $end->y;

            if ( ( $startValue == 0 ) ||
                 ( $endValue == 0 ) ||
                 ( $startValue / abs( $startValue ) == $endValue / abs( $endValue ) ) )
            {
                // Values have the same sign or are on the axis
                $this->driver->drawPolygon(
                    array(
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $start->x,
                            $boundings->y0 + ( $boundings->height ) * $start->y
                        ),
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $end->x,
                            $boundings->y0 + ( $boundings->height ) * $end->y
                        ),
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $end->x,
                            $boundings->y0 + ( $boundings->height ) * $axisPosition
                        ),
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $start->x,
                            $boundings->y0 + ( $boundings->height ) * $axisPosition
                        ),
                    ),
                    $fillColor,
                    true
                );
            }
            else
            {
                // values are on differente sides of the axis - split the filled polygon
                $startDiff = abs( $axisPosition - $start->y );
                $endDiff = abs( $axisPosition - $end->y );

                $cuttingPosition = $startDiff / ( $endDiff + $startDiff );
                $cuttingPoint = new ezcGraphCoordinate(
                    $start->x + ( $end->x - $start->x ) * $cuttingPosition,
                    $axisPosition
                );

                $this->driver->drawPolygon(
                    array(
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $start->x,
                            $boundings->y0 + ( $boundings->height ) * $axisPosition
                        ),
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $start->x,
                            $boundings->y0 + ( $boundings->height ) * $start->y
                        ),
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x,
                            $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y
                        ),
                    ),
                    $fillColor,
                    true
                );

                $this->driver->drawPolygon(
                    array(
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $end->x,
                            $boundings->y0 + ( $boundings->height ) * $axisPosition
                        ),
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $end->x,
                            $boundings->y0 + ( $boundings->height ) * $end->y
                        ),
                        new ezcGraphCoordinate(
                            $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x,
                            $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y
                        ),
                    ),
                    $fillColor,
                    true
                );
            }
        }

        // Draw line
        $this->driver->drawLine(
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $start->x,
                $boundings->y0 + ( $boundings->height ) * $start->y
            ),
            new ezcGraphCoordinate(
                $boundings->x0 + ( $boundings->width ) * $end->x,
                $boundings->y0 + ( $boundings->height ) * $end->y
            ),
            $color,
            $thickness
        );

        // Draw line symbol
        if ( $symbol !== ezcGraph::NO_SYMBOL )
        {
            if ( $symbolColor === null )
            {
                $symbolColor = $color;
            }
    
            $this->linePostSymbols[] = array(
                'boundings' => new ezcGraphBoundings(
                    $boundings->x0 + ( $boundings->width ) * $end->x - $this->options->symbolSize / 2,
                    $boundings->y0 + ( $boundings->height ) * $end->y - $this->options->symbolSize / 2,
                    $boundings->x0 + ( $boundings->width ) * $end->x + $this->options->symbolSize / 2,
                    $boundings->y0 + ( $boundings->height ) * $end->y + $this->options->symbolSize / 2
                ),
                'color' => $symbolColor,
                'context' => $context,
                'symbol' => $symbol,
            );
        }
    }

    /**
     * Returns a coordinate in the given bounding box for the given angle
     * radius with the center as base point.
     * 
     * @param ezcGraphBoundings $boundings
     * @param ezcGraphCoordinate $center
     * @param float $angle
     * @param float $radius
     * @return float
     */
    protected function getCoordinateFromAngleAndRadius(
        ezcGraphBoundings $boundings,
        ezcGraphCoordinate $center,
        $angle,
        $radius
    )
    {
        $direction = new ezcGraphCoordinate(
            sin( $angle ) * $boundings->width / 2,
            -cos( $angle ) * $boundings->height / 2
        );

        $offset = new ezcGraphCoordinate(
            sin( $angle ) * $this->xAxisSpace,
            -cos( $angle ) * $this->yAxisSpace
        );

        $direction->x -= 2 * $offset->x;
        $direction->y -= 2 * $offset->y;

        return new ezcGraphCoordinate(
            $boundings->x0 +
                $center->x +
                $offset->x +
                $direction->x * $radius,
            $boundings->y0 +
                $center->y +
                $offset->y +
                $direction->y * $radius
        );
    }

    /**
     * Draw radar chart data line
     *
     * Draws a line as a data element in a radar chart
     * 
     * @param ezcGraphBoundings $boundings Chart boundings
     * @param ezcGraphContext $context Context of call
     * @param ezcGraphColor $color Color of line
     * @param ezcGraphCoordinate $center Center of radar chart
     * @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 $thickness Line thickness
     * @return void
     */
    public function drawRadarDataLine(
        ezcGraphBoundings $boundings,
        ezcGraphContext $context,
        ezcGraphColor $color,
        ezcGraphCoordinate $center,
        ezcGraphCoordinate $start,
        ezcGraphCoordinate $end,
        $dataNumber = 1,
        $dataCount = 1,
        $symbol = ezcGraph::NO_SYMBOL,
        ezcGraphColor $symbolColor = null,
        ezcGraphColor $fillColor = null,
        $thickness = 1.
    )
    {
        // Calculate line points from chart coordinates
        $start = $this->getCoordinateFromAngleAndRadius(
            $boundings,
            $center,
            $start->x * 2 * M_PI,
            $start->y
        );
        $end = $this->getCoordinateFromAngleAndRadius(
            $boundings,
            $center,
            $end->x * 2 * M_PI,
            $end->y
        );

        // Fill line
        if ( $fillColor !== null )
        {
            $this->driver->drawPolygon(
                array(
                    $start,
                    $end,
                    new ezcGraphCoordinate(
                        $boundings->x0 + $center->x,
                        $boundings->y0 + $center->y
                    ),
                ),
                $fillColor,
                true
            );
        }

        // Draw line
        $this->driver->drawLine(
            $start,
            $end,
            $color,
            $thickness
        );

        if ( $symbol !== ezcGraph::NO_SYMBOL )
        {
            $this->drawSymbol(
                new ezcGraphBoundings(
                    $end->x - $this->options->symbolSize / 2,
                    $end->y - $this->options->symbolSize / 2,
                    $end->x + $this->options->symbolSize / 2,
                    $end->y + $this->options->symbolSize / 2
                ),
                $symbolColor,
                $symbol
            );
        }
    }
    
    /**
     * 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
     */
    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 )
    {
        // Bar specific calculations
        if ( $type !== ezcGraph::LINE )
        {
            $margin = $stepSize * $this->options->barMargin;
            $padding = $stepSize * $this->options->barPadding;
            $barWidth = ( $stepSize - $margin ) / $dataCount - $padding;
            $offset = -( $dataNumber +  ( $dataCount - 1 ) / -2 ) * ( $barWidth + $padding );
        }

        $this->driver->options->font = $font;
        $width = $boundings->width / $dataCount;
        
        $dataPoint = new ezcGraphCoordinate(
            $boundings->x0 + ( $boundings->width ) * $end->x + $xOffset +
                ( $type === ezcGraph::LINE ? 0 : $offset ),
            $boundings->y0 + ( $boundings->height ) * $end->y + $yOffset
        );

        if ( $end->y < $axisPosition )
        {
            $this->driver->drawTextBox(
                $text,
                new ezcGraphCoordinate(
                    $dataPoint->x - $width / 2,
                    $dataPoint->y - $size - $font->padding - $this->options->symbolSize
                ),
                $width,
                $size,
                ezcGraph::CENTER | ezcGraph::BOTTOM
            );
        }
        else
        {
            $this->driver->drawTextBox(
                $text,
                new ezcGraphCoordinate(
                    $dataPoint->x - $width / 2,
                    $dataPoint->y + $font->padding + $this->options->symbolSize
                ),
                $width,
                $size,
                ezcGraph::CENTER | ezcGraph::TOP
            );
        }
    }
    
    /**
     * 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
     */
    public function drawLegend(
        ezcGraphBoundings $boundings,
        ezcGraphChartElementLegend $legend,
        $type = ezcGraph::VERTICAL )
    {
        $labels = $legend->labels;
        
        // Calculate boundings of each label
        if ( $type & ezcGraph::VERTICAL )
        {
            $labelWidth = $boundings->width;
            $labelHeight = min( 
                ( $boundings->height ) / count( $labels ) - $legend->spacing, 
                $legend->symbolSize + 2 * $legend->padding
            );
        }
        else
        {
            $labelWidth = ( $boundings->width ) / count( $labels ) - $legend->spacing;
            $labelHeight = min(
                $boundings->height,
                $legend->symbolSize + 2 * $legend->padding
            );
        }

        $symbolSize = $labelHeight - 2 * $legend->padding;

        // Draw all labels
        $labelPosition = new ezcGraphCoordinate( $boundings->x0, $boundings->y0 );
        foreach ( $labels as $label )
        {
            $this->elements['legend_url'][$label['label']] = $label['url'];

            $this->elements['legend'][$label['label']]['symbol'] = $this->drawSymbol(
                new ezcGraphBoundings(
                    $labelPosition->x + $legend->padding,
                    $labelPosition->y + $legend->padding,
                    $labelPosition->x + $legend->padding + $symbolSize,
                    $labelPosition->y + $legend->padding + $symbolSize
                ),
                $label['color'],
                $label['symbol']
            );

            $this->elements['legend'][$label['label']]['text'] = $this->driver->drawTextBox(
                $label['label'],
                new ezcGraphCoordinate(
                    $labelPosition->x + 2 * $legend->padding + $symbolSize,
                    $labelPosition->y + $legend->padding
                ),
                $labelWidth - $symbolSize - 3 * $legend->padding,
                $labelHeight - 2 * $legend->padding,
                ezcGraph::LEFT | ezcGraph::MIDDLE
            );

            $labelPosition->x += ( $type === ezcGraph::VERTICAL ? 0 : $labelWidth + $legend->spacing );
            $labelPosition->y += ( $type === ezcGraph::VERTICAL ? $labelHeight + $legend->spacing : 0 );
        }
    }
    
    /**
     * 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
     */
    public function drawBox(
        ezcGraphBoundings $boundings,
        ezcGraphColor $background = null,
        ezcGraphColor $borderColor = null,
        $borderWidth = 0,
        $margin = 0,
        $padding = 0,
        $title = false,
        $titleSize = 16 )
    {
        // Apply margin
        $boundings->x0 += $margin;
        $boundings->y0 += $margin;
        $boundings->x1 -= $margin;
        $boundings->y1 -= $margin;
        
        if ( $background instanceof ezcGraphColor )
        {
            // Draw box background
            $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 ),
                ),
                $background,
                true
            );
        }

        if ( ( $borderColor instanceof ezcGraphColor ) &&
             ( $borderWidth > 0 ) )
        {
            // Draw border
            $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 ),
                ),
                $borderColor,
                false,
                $borderWidth
            );

            // Reduce local boundings by borderWidth
            $boundings->x0 += $borderWidth;
            $boundings->y0 += $borderWidth;
            $boundings->x1 -= $borderWidth;
            $boundings->y1 -= $borderWidth;
        }

        // Apply padding
        $boundings->x0 += $padding;
        $boundings->y0 += $padding;
        $boundings->x1 -= $padding;
        $boundings->y1 -= $padding;

        // Add box title
        if ( $title !== false )
        {
            switch ( $this->options->titlePosition )
            {
                case ezcGraph::TOP:
                    $this->driver->drawTextBox(
                        $title,
                        new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
                        $boundings->width,
                        $titleSize,
                        $this->options->titleAlignement
                    );

                    $boundings->y0 += $titleSize + $padding;
                    $boundings->y1 -= $titleSize + $padding;
                    break;
                case ezcGraph::BOTTOM:
                    $this->driver->drawTextBox(
                        $title,
                        new ezcGraphCoordinate( $boundings->x0, $boundings->y1 - $titleSize ),
                        $boundings->width,
                        $titleSize,
                        $this->options->titleAlignement
                    );

                    $boundings->y1 -= $titleSize + $padding;
                    break;
            }
        }

        return $boundings;
    }
    
    /**
     * 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
     */
    public function drawText(
        ezcGraphBoundings $boundings,
        $text,
        $align = ezcGraph::LEFT,
        ezcGraphRotation $rotation = null )
    {
        $this->driver->drawTextBox(
            $text,
            new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
            $boundings->width,
            $boundings->height,
            $align,
            $rotation
        );
    }

    /**
     * Draw grid line
     *
     * Draw line for the grid in the chart background
     * 
     * @param ezcGraphCoordinate $start Start point
     * @param ezcGraphCoordinate $end End point
     * @param ezcGraphColor $color Color of the grid line
     * @return void
     */
    public function drawGridLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
    {
        $this->driver->drawLine(
            $start,
            $end,
            $color,
            1
        );
    }

    /**
     * Draw step line
     *
     * Draw a step (marker for label position) on a axis.
     * 
     * @param ezcGraphCoordinate $start Start point
     * @param ezcGraphCoordinate $end End point
     * @param ezcGraphColor $color Color of the grid line
     * @return void
     */
    public function drawStepLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
    {
        $this->driver->drawLine(
            $start,
            $end,
            $color,
            1
        );
    }
    
    /**
     * 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
     */
    public function drawAxis(
        ezcGraphBoundings $boundings,
        ezcGraphCoordinate $start,
        ezcGraphCoordinate $end,
        ezcGraphChartElementAxis $axis,
        ezcGraphAxisLabelRenderer $labelClass = null,
        ezcGraphBoundings $innerBoundings = null )
    {
        // Legacy axis drawing for BC reasons
        if ( $innerBoundings === null )
        {
            return $this->legacyDrawAxis( $boundings, $start, $end, $axis, $labelClass );
        }

        // Calculate axis start and end points depending on inner boundings
        if ( ( $axis->position === ezcGraph::TOP ) ||
             ( $axis->position === ezcGraph::BOTTOM ) )
        {
            $innerStart = new ezcGraphCoordinate( 
                $start->x + $boundings->x0,
                ( $axis->position === ezcGraph::TOP ? $innerBoundings->y0 : $innerBoundings->y1 )
            );
            $innerEnd = new ezcGraphCoordinate(
                $end->x   + $boundings->x0,
                ( $axis->position === ezcGraph::TOP ? $innerBoundings->y1 : $innerBoundings->y0 )
            );
        }
        else
        {
            $innerStart = new ezcGraphCoordinate( 
                ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x0 : $innerBoundings->x1 ),
                $start->y + $boundings->y0
            );
            $innerEnd = new ezcGraphCoordinate(
                ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x1 : $innerBoundings->x0 ),
                $end->y   + $boundings->y0
            );
        }

        // Shorten axis, if requested
        if ( $this->options->shortAxis )
        {
            $start = clone $innerStart;
            $end   = clone $innerEnd;
        }
        else
        {
            $start->x += $boundings->x0;
            $start->y += $boundings->y0;
            $end->x += $boundings->x0;
            $end->y += $boundings->y0;
        }

        // Determine normalized direction
        $direction = new ezcGraphVector(
            $start->x - $end->x,
            $start->y - $end->y
        );
        $direction->unify();

        // Draw axis
        $this->driver->drawLine(
            $start,
            $end,
            $axis->border,
            1
        );

        // Draw small arrowhead
        $this->drawAxisArrowHead(
            $end,
            $direction,
            max(
                $axis->minArrowHeadSize,
                min(
                    $axis->maxArrowHeadSize,
                    abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) )
                )
            ),
            $axis->border
        );

        // Draw axis label, if set
        $this->drawAxisLabel( $end, $innerBoundings, $axis );

        // If font should not be synchronized, use font configuration from
        // each axis
        if ( $this->options->syncAxisFonts === false )
        {
            $this->driver->options->font = $axis->font;
        }

        $labelClass->renderLabels(
            $this,
            $boundings,
            $innerStart,
            $innerEnd,
            $axis,
            $innerBoundings
        );
    }

    /**
     * Draw axis label
     *
     * Draw labels at the end of an axis.
     * 
     * @param ezcGraphCoordinate $position 
     * @param ezcGraphBoundings $boundings 
     * @param ezcGraphChartElementAxis $axis 
     * @return void
     */
    protected function drawAxisLabel( ezcGraphCoordinate $position, ezcGraphBoundings $boundings, ezcGraphChartElementAxis $axis )
    {
        if ( $axis->label === false )
        {
            return;
        }

        $width = $boundings->width;
        switch ( $axis->position )
        {
            case ezcGraph::TOP:
                $this->driver->drawTextBox(
                    $axis->label,
                    new ezcGraphCoordinate(
                        $position->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ),
                        $position->y - $axis->labelMargin - $axis->labelSize
                    ),
                    $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
                    $axis->labelSize,
                    ezcGraph::TOP | ezcGraph::RIGHT,
                    new ezcGraphRotation( $axis->labelRotation, new ezcGraphCoordinate(
                        $position->x + $axis->labelSize / 2,
                        $position->y - $axis->labelSize / 2
                    ) )
                );
                break;
            case ezcGraph::BOTTOM:
                $this->driver->drawTextBox(
                    $axis->label,
                    new ezcGraphCoordinate(
                        $position->x + $axis->labelMargin,
                        $position->y + $axis->labelMargin
                    ),
                    $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
                    $axis->labelSize,
                    ezcGraph::TOP | ezcGraph::LEFT,
                    new ezcGraphRotation( $axis->labelRotation, new ezcGraphCoordinate(
                        $position->x + $axis->labelSize / 2,
                        $position->y + $axis->labelSize / 2
                    ) )
                );
                break;
            case ezcGraph::LEFT:
                $this->driver->drawTextBox(
                    $axis->label,
                    new ezcGraphCoordinate(
                        $position->x - $width,
                        $position->y - $axis->labelSize - $axis->labelMargin
                    ),
                    $width - $axis->labelMargin,
                    $axis->labelSize,
                    ezcGraph::BOTTOM | ezcGraph::RIGHT,
                    new ezcGraphRotation( $axis->labelRotation, new ezcGraphCoordinate(
                        $position->x - $axis->labelSize / 2,
                        $position->y - $axis->labelSize / 2
                    ) )
                );
                break;
            case ezcGraph::RIGHT:
                $this->driver->drawTextBox(
                    $axis->label,
                    new ezcGraphCoordinate(
                        $position->x,
                        $position->y - $axis->labelSize - $axis->labelMargin
                    ),
                    $width - $axis->labelMargin,
                    $axis->labelSize,
                    ezcGraph::BOTTOM | ezcGraph::LEFT,
                    new ezcGraphRotation( $axis->labelRotation, new ezcGraphCoordinate(
                        $position->x + $axis->labelSize / 2,
                        $position->y - $axis->labelSize / 2
                    ) )
                );
                break;
        }
    }

    /**
     * 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.
     *
     * This function is deprecated and will be removed in favor of its
     * reimplementation using the innerBoundings parameter.
     * 
     * @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
     * @apichange
     * @return void
     */
    protected function legacyDrawAxis(
        ezcGraphBoundings $boundings,
        ezcGraphCoordinate $start,
        ezcGraphCoordinate $end,
        ezcGraphChartElementAxis $axis,
        ezcGraphAxisLabelRenderer $labelClass = null )
    {
        // Store axis space for use by label renderer
        switch ( $axis->position )
        {
            case ezcGraph::TOP:
            case ezcGraph::BOTTOM:
                $this->xAxisSpace = ( $boundings->width ) * $axis->axisSpace;
                break;
            case ezcGraph::LEFT:
            case ezcGraph::RIGHT:
                $this->yAxisSpace = ( $boundings->height ) * $axis->axisSpace;
                break;
        }

        // Clone boundings because of internal modifications
        $boundings = clone $boundings;

        $start->x += $boundings->x0;
        $start->y += $boundings->y0;
        $end->x += $boundings->x0;
        $end->y += $boundings->y0;

        // Shorten drawn axis, if requested.
        if ( ( $this->options->shortAxis === true ) &&
             ( ( $axis->position === ezcGraph::TOP ) ||
               ( $axis->position === ezcGraph::BOTTOM ) ) )
        {
            $axisStart = clone $start;
            $axisEnd   = clone $end;

            $axisStart->y += $boundings->height * $axis->axisSpace *
                ( $axis->position === ezcGraph::TOP ? 1 : -1 );
            $axisEnd->y   -= $boundings->height * $axis->axisSpace *
                ( $axis->position === ezcGraph::TOP ? 1 : -1 );
        }
        elseif ( ( $this->options->shortAxis === true ) &&
             ( ( $axis->position === ezcGraph::LEFT ) ||
               ( $axis->position === ezcGraph::RIGHT ) ) )
        {
            $axisStart = clone $start;
            $axisEnd   = clone $end;

            $axisStart->x += $boundings->width * $axis->axisSpace *
                ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
            $axisEnd->x   -= $boundings->width * $axis->axisSpace *
                ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
        }
        else
        {
            $axisStart = $start;
            $axisEnd   = $end;
        }

        // Determine normalized direction
        $direction = new ezcGraphVector(
            $start->x - $end->x,
            $start->y - $end->y
        );
        $direction->unify();

        // Draw axis
        $this->driver->drawLine(
            $axisStart,
            $axisEnd,
            $axis->border,
            1
        );

        // Draw small arrowhead
        $this->drawAxisArrowHead(
            $axisEnd,
            $direction,
            max(
                $axis->minArrowHeadSize,
                min(
                    $axis->maxArrowHeadSize,
                    abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) )
                )
            ),
            $axis->border
        );

        // Draw axis label, if set
        $this->drawAxisLabel( $end, $boundings, $axis );

        // Collect axis labels and draw, when all axisSpaces are collected
        $this->axisLabels[] = array(
            'object' => $labelClass,
            'boundings' => $boundings,
            'start' => clone $start,
            'end' => clone $end,
            'axis' => $axis,
        );

        if ( $this->xAxisSpace && $this->yAxisSpace )
        {
            $this->drawAxisLabels();
        }
    }

    /**
     * Draw all left axis labels
     * 
     * @return void
     */
    protected function drawAxisLabels()
    {
        foreach ( $this->axisLabels as $nr => $axisLabel )
        {
            // If font should not be synchronized, use font configuration from
            // each axis
            if ( $this->options->syncAxisFonts === false )
            {
                $this->driver->options->font = $axisLabel['axis']->font;
            }

            $start = $axisLabel['start'];
            $end = $axisLabel['end'];

            $direction = new ezcGraphVector(
                $end->x - $start->x,
                $end->y - $start->y
            );
            $direction->unify();

            // Convert elipse to circle for correct angle calculation
            $direction->y *= ( $this->xAxisSpace / $this->yAxisSpace );
            $angle = $direction->angle( new ezcGraphVector( 0, 1 ) );

            $movement = new ezcGraphVector(
                sin( $angle ) * $this->xAxisSpace * ( $direction->x < 0 ? -1 : 1 ),
                cos( $angle ) * $this->yAxisSpace
            );

            $start->x += $movement->x;
            $start->y += $movement->y;
            $end->x -= $movement->x;
            $end->y -= $movement->y;

            $axisLabel['object']->renderLabels(
                $this,
                $axisLabel['boundings'],
                $start,
                $end,
                $axisLabel['axis']
            );

            // Prevent from redrawing axis on more then 2 axis.
            unset( $this->axisLabels[$nr] );
        }
    }

    /**
     * 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
     */
    public function drawBackgroundImage(
        ezcGraphBoundings $boundings,
        $file,
        $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE
        $repeat = ezcGraph::NO_REPEAT )
    {
        $imageData = getimagesize( $file );
        $imageWidth = $imageData[0];
        $imageHeight = $imageData[1];

        $imageWidth = min( $imageWidth, $boundings->width );
        $imageHeight = min( $imageHeight, $boundings->height );

        $imagePosition = new ezcGraphCoordinate( 
            $boundings->x0, 
            $boundings->y0
        );

        // Determine x position
        switch ( true ) {
            case ( $repeat & ezcGraph::HORIZONTAL ):
                // If is repeated on this axis fall back to position zero
            case ( $position & ezcGraph::LEFT ):
                $imagePosition->x = $boundings->x0;
                break;
            case ( $position & ezcGraph::RIGHT ):
                $imagePosition->x = max( 
                    $boundings->x1 - $imageWidth,
                    $boundings->x0
                );
                break;
            default:
                $imagePosition->x = max(
                    $boundings->x0 + ( $boundings->width - $imageWidth ) / 2,
                    $boundings->x0
                );
                break;
        }

        // Determine y position
        switch ( true ) {
            case ( $repeat & ezcGraph::VERTICAL ):
                // If is repeated on this axis fall back to position zero
            case ( $position & ezcGraph::TOP ):
                $imagePosition->y = $boundings->y0;
                break;
            case ( $position & ezcGraph::BOTTOM ):
                $imagePosition->y = max( 
                    $boundings->y1 - $imageHeight,
                    $boundings->y0
                );
                break;
            default:
                $imagePosition->y = max(
                    $boundings->y0 + ( $boundings->height - $imageHeight ) / 2,
                    $boundings->y0
                );
                break;
        }

        // Texturize backround based on position and repetition
        $position = new ezcGraphCoordinate(
            $imagePosition->x,
            $imagePosition->y
        );
        
        do 
        {
            $position->y = $imagePosition->y;

            do 
            {
                $this->driver->drawImage( 
                    $file, 
                    $position, 
                    $imageWidth, 
                    $imageHeight 
                );

                $position->y += $imageHeight;
            }
            while ( ( $position->y < $boundings->y1 ) &&
                    ( $repeat & ezcGraph::VERTICAL ) );
            
            $position->x += $imageWidth;
        }
        while ( ( $position->x < $boundings->x1 ) &&
                ( $repeat & ezcGraph::HORIZONTAL ) );
    }

    /**
     * Call all postprocessing functions
     * 
     * @return void
     */
    protected function finish()
    {
        $this->finishCircleSectors();
        $this->finishPieSegmentLabels();
        $this->finishLineSymbols();

        return true;
    }

    /**
     * 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()
    {
        parent::resetRenderer();

        // Also reset special 2D renderer options
        $this->pieSegmentLabels = array(
            0 => array(),
            1 => array(),
        );
        $this->pieSegmentBoundings = false;
        $this->linePostSymbols     = array();
        $this->axisLabels          = array();
        $this->circleSectors       = array();
    }

    /**
     * Render odometer chart
     * 
     * @param ezcGraphBoundings $boundings 
     * @param ezcGraphChartElementAxis $axis
     * @param ezcGraphOdometerChartOptions $options
     * @return ezcGraphBoundings
     */
    public function drawOdometer( 
        ezcGraphBoundings $boundings, 
        ezcGraphChartElementAxis $axis,
        ezcGraphOdometerChartOptions $options )
    {
        $height = $boundings->height * $options->odometerHeight;

        // Draw axis
        $oldAxisSpace = $axis->axisSpace;
        $axis->axisSpace = 0;

        $axis->render( $this, $boundings );

        // Reset axisspaces to correct values
        $this->xAxisSpace = $boundings->width * $oldAxisSpace;
        $this->yAxisSpace = ( $boundings->height - $height ) / 2;

        $this->drawAxisLabels();

        // Reduce size of chart boundings respecting requested odometer height
        $boundings->x0 += $this->xAxisSpace;
        $boundings->x1 -= $this->xAxisSpace;
        $boundings->y0 += $this->yAxisSpace;
        $boundings->y1 -= $this->yAxisSpace;

        $gradient = new ezcGraphLinearGradient(
            new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
            new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
            $options->startColor,
            $options->endColor
        );

        // Simply draw box with gradient and optional border
        $this->drawBox(
            $boundings,
            $gradient,
            $options->borderColor,
            $options->borderWidth
        );

        // Return modified chart boundings
        return $boundings;
    }

    /**
     * Draw a single odometer marker.
     *
     * @param ezcGraphBoundings $boundings
     * @param ezcGraphCoordinate $position
     * @param int $symbol
     * @param ezcGraphColor $color
     * @param int $width
     */
    public function drawOdometerMarker(
        ezcGraphBoundings $boundings,
        ezcGraphCoordinate $position,
        $symbol,
        ezcGraphColor $color,
        $width )
    {
        $this->driver->drawLine(
            new ezcGraphCoordinate(
                $xPos = $boundings->x0 + ( $position->x * $boundings->width ),
                $boundings->y0
            ),
            new ezcGraphCoordinate(
                $xPos,
                $boundings->y1
            ),
            $color,
            $width
        );
    }
}

?>
