| <?php |
| /** |
| * File containing the three 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. This renderer |
| * renders the charts in a isometric three dimensional view. |
| * |
| * The class options are defined in the class {@link ezcGraphRenderer3dOptions} |
| * extending the basic renderer options in {@link ezcGraphRendererOptions}. |
| * |
| * <code> |
| * $graph = new ezcGraphPieChart(); |
| * $graph->palette = new ezcGraphPaletteEzRed(); |
| * $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 ezcGraphRenderer3d(); |
| * |
| * $graph->renderer->options->moveOut = .2; |
| * |
| * $graph->renderer->options->pieChartOffset = 63; |
| * |
| * $graph->renderer->options->pieChartGleam = .3; |
| * $graph->renderer->options->pieChartGleamColor = '#FFFFFF'; |
| * |
| * $graph->renderer->options->pieChartShadowSize = 5; |
| * $graph->renderer->options->pieChartShadowColor = '#000000'; |
| * |
| * $graph->renderer->options->legendSymbolGleam = .5; |
| * $graph->renderer->options->legendSymbolGleamSize = .9; |
| * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF'; |
| * |
| * $graph->renderer->options->pieChartSymbolColor = '#55575388'; |
| * |
| * $graph->renderer->options->pieChartHeight = 5; |
| * $graph->renderer->options->pieChartRotation = .8; |
| * |
| * $graph->render( 400, 150, 'tutorial_pie_chart_3d.svg' ); |
| * </code> |
| * |
| * @version //autogentag// |
| * @package Graph |
| * @mainclass |
| */ |
| class ezcGraphRenderer3d |
| extends |
| ezcGraphRenderer |
| implements |
| ezcGraphStackedBarsRenderer |
| { |
| |
| /** |
| * 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(); |
| |
| /** |
| * Array containing lines from the axis and grid which should be redrawn on |
| * top of the data. |
| * |
| * @var array |
| */ |
| protected $frontLines = array(); |
| |
| /** |
| * Collects circle sectors to draw shadow in background of all circle |
| * sectors. |
| * |
| * @var array |
| */ |
| protected $circleSectors = array(); |
| |
| /** |
| * Collects bar sides to draw them in a post processing step to simulate |
| * a simple z buffer. |
| * array( |
| * array( |
| * 'index' => (int) // used for sorting |
| * 'context' => ezcGraphContext // context of call |
| * 'method' => (string) // method of driver to call |
| * 'parameters' => array // parameters for method call |
| * ), ... |
| * ) |
| * |
| * @var array |
| */ |
| protected $barPostProcessing = array(); |
| |
| /** |
| * Options |
| * |
| * @var ezcGraphRenderer3dOptions |
| */ |
| protected $options; |
| |
| /** |
| * Depth of displayed pseudo three dimensional line chart elements. |
| * |
| * @var float |
| */ |
| protected $depth = false; |
| |
| /** |
| * Factor to reduce the width according to depth |
| * |
| * @var float |
| */ |
| protected $xDepthFactor = false; |
| |
| /** |
| * Factor to reduce the height according to depth |
| * |
| * @var float |
| */ |
| protected $yDepthFactor = false; |
| |
| /** |
| * Boundings for the chart data |
| * |
| * @var ezcGraphBoundings |
| */ |
| protected $dataBoundings = false; |
| |
| /** |
| * Collect axis labels, so that the axis are drawn, when all axis spaces |
| * are known. |
| * |
| * @var array |
| */ |
| protected $axisLabels = array(); |
| |
| /** |
| * Constructor |
| * |
| * @param array $options Default option array |
| * @return void |
| * @ignore |
| */ |
| public function __construct( array $options = array() ) |
| { |
| $this->options = new ezcGraphRenderer3dOptions( $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 ); |
| } |
| } |
| |
| /** |
| * Calculate the display coordinate from a coordinate |
| * |
| * Calculates the display coordinate of a coordinate depending on the |
| * depth setting and the distance of the coordinate to the front of the |
| * chart. |
| * |
| * @param ezcGraphCoordinate $c Coordinate |
| * @param float $front Distance to front (0 - 1) |
| * @return ezcGraphCoordinate Resulting coordinate |
| */ |
| protected function get3dCoordinate( ezcGraphCoordinate $c, $front = 1. ) |
| { |
| return new ezcGraphCoordinate( |
| ( $c->x - $this->dataBoundings->x0 ) * $this->xDepthFactor + $this->dataBoundings->x0 + $this->depth * $front, |
| ( $c->y - $this->dataBoundings->y0 ) * $this->yDepthFactor + $this->dataBoundings->y0 + $this->depth * ( 1 - $front ) |
| ); |
| } |
| |
| /** |
| * 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->x1 - $boundings->x0 ) / 2, |
| $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 |
| - $this->options->pieChartHeight / 2 |
| ); |
| |
| // Limit radius to fourth of width and half of height at maximum |
| $radius = min( |
| ( $boundings->x1 - $boundings->x0 ) * $this->options->pieHorizontalSize, |
| ( $boundings->y1 - $boundings->y0 ) * $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 ) ) * $this->options->pieChartRotation |
| ); |
| } |
| |
| // 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 ) * $this->options->pieChartRotation - $this->options->pieChartHeight, |
| '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 * $this->options->pieChartRotation |
| ); |
| |
| // Split labels up into left a right site and index them on their |
| // y position |
| $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][(int) ( $pieSegmentCenter->y * 100 )] = array( |
| new ezcGraphCoordinate( |
| $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3 * ( 1 - $this->options->moveOut ), |
| $center->y + sin( deg2rad( $direction ) ) * ( $radius - $this->options->pieChartHeight ) * 2 / 3 * ( 1 - $this->options->moveOut ) * $this->options->pieChartRotation |
| ), |
| $label, |
| $context, |
| ); |
| } |
| |
| if ( !$this->pieSegmentBoundings ) |
| { |
| $this->pieSegmentBoundings = $boundings; |
| } |
| } |
| |
| /** |
| * 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->x1 - $boundings->x0 ) / 2, |
| $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 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 ) |
| { |
| $height = (int) ( $height / 100 ); |
| |
| 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; |
| } |
| } |
| } |
| |
| /** |
| * 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, and |
| * ensure the correct drawing order for all pie segment elements. |
| * |
| * @return void |
| */ |
| protected function finishCirleSectors() |
| { |
| $zBuffer = array(); |
| |
| $shadows = array(); |
| $shadowCenter = false; |
| $shadowEndAngle = false; |
| |
| // Add circle sector sides to simple z buffer prioriry list |
| foreach ( $this->circleSectors as $circleSector ) |
| { |
| // Draw shadow if wanted |
| if ( $this->options->pieChartShadowSize > 0 ) |
| { |
| if ( $shadowEndAngle === false ) |
| { |
| $shadowStartAngle = $circleSector['start']; |
| $shadowEndAngle = $circleSector['end']; |
| $shadowCenter = $circleSector['center']; |
| } |
| elseif ( $circleSector['center'] == $shadowCenter ) |
| { |
| $shadowEndAngle = $circleSector['end']; |
| } |
| else |
| { |
| $shadows[] = array( |
| 'center' => $shadowCenter, |
| 'start' => $shadowStartAngle, |
| 'end' => $shadowEndAngle, |
| 'width' => $circleSector['width'], |
| 'height' => $circleSector['height'], |
| ); |
| |
| $shadowCenter = $circleSector['center']; |
| $shadowStartAngle = $circleSector['start']; |
| $shadowEndAngle = $circleSector['end']; |
| } |
| } |
| |
| $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder ); |
| |
| $center = (int) ( $circleSector['center']->y + sin( deg2rad( $circleSector['start'] + ( $circleSector['end'] - $circleSector['start'] ) / 2 ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight / 2 + 1 ); |
| |
| $zBuffer[$center][] = array( |
| 'method' => 'drawCircularArc', |
| 'paramenters' => array( |
| $circleSector['center'], |
| $circleSector['width'], |
| $circleSector['height'], |
| $this->options->pieChartHeight, |
| $circleSector['start'], |
| $circleSector['end'], |
| $circleSector['color'] |
| ) |
| ); |
| |
| // Left side |
| $polygonPoints = array( |
| $circleSector['center'], |
| new ezcGraphCoordinate( |
| $circleSector['center']->x, |
| $circleSector['center']->y + $this->options->pieChartHeight |
| ), |
| new ezcGraphCoordinate( |
| $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2, |
| $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight |
| ), |
| new ezcGraphCoordinate( |
| $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2, |
| $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2 |
| ), |
| ); |
| |
| // Get average y coordinate for polygon to use for zBuffer |
| $center = 0; |
| foreach ( $polygonPoints as $point ) |
| { |
| $center += $point->y; |
| } |
| $center = (int) ( $center / count( $polygonPoints ) ); |
| |
| $zBuffer[$center][] = array( |
| 'method' => 'drawPolygon', |
| 'paramenters' => array( |
| $polygonPoints, |
| $circleSector['color'], |
| true |
| ), |
| ); |
| |
| $zBuffer[$center][] = array( |
| 'method' => 'drawPolygon', |
| 'paramenters' => array( |
| $polygonPoints, |
| $darkenedColor, |
| false |
| ), |
| ); |
| |
| // Right side |
| $polygonPoints = array( |
| $circleSector['center'], |
| new ezcGraphCoordinate( |
| $circleSector['center']->x, |
| $circleSector['center']->y + $this->options->pieChartHeight |
| ), |
| new ezcGraphCoordinate( |
| $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2, |
| $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight |
| ), |
| new ezcGraphCoordinate( |
| $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2, |
| $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2 |
| ), |
| ); |
| |
| // Get average y coordinate for polygon to use for zBuffer |
| $center = 0; |
| foreach ( $polygonPoints as $point ) |
| { |
| $center += $point->y; |
| } |
| $center = (int) ( $center / count( $polygonPoints ) ); |
| |
| $zBuffer[$center][] = array( |
| 'method' => 'drawPolygon', |
| 'paramenters' => array( |
| $polygonPoints, |
| $circleSector['color'], |
| true |
| ), |
| ); |
| |
| $zBuffer[$center][] = array( |
| 'method' => 'drawPolygon', |
| 'paramenters' => array( |
| $polygonPoints, |
| $darkenedColor, |
| false |
| ), |
| ); |
| } |
| |
| if ( $this->options->pieChartShadowSize > 0 ) |
| { |
| $shadows[] = array( |
| 'center' => $shadowCenter, |
| 'start' => $shadowStartAngle, |
| 'end' => $shadowEndAngle, |
| 'width' => $circleSector['width'], |
| 'height' => $circleSector['height'], |
| ); |
| } |
| |
| // Draw collected shadows |
| foreach ( $shadows as $circleSector ) |
| { |
| for ( $i = $this->options->pieChartShadowSize; $i > 0; --$i ) |
| { |
| $startAngle = $circleSector['start']; |
| $endAngle = $circleSector['end']; |
| |
| $startAngle = $circleSector['start'] - ( $this->options->pieChartShadowSize - $i ); |
| $endAngle = $circleSector['end'] + ( $this->options->pieChartShadowSize - $i ); |
| |
| if ( ( $endAngle - $startAngle ) >= 360 ) |
| { |
| $this->driver->drawCircle( |
| new ezcGraphCoordinate( |
| $circleSector['center']->x, |
| $circleSector['center']->y + $this->options->pieChartHeight |
| ), |
| $circleSector['width'] + $i * 2, |
| $circleSector['height'] + $i * 2, |
| $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ), |
| true |
| ); |
| } |
| else |
| { |
| $this->driver->drawCircleSector( |
| new ezcGraphCoordinate( |
| $circleSector['center']->x, |
| $circleSector['center']->y + $this->options->pieChartHeight |
| ), |
| $circleSector['width'] + $i * 2, |
| $circleSector['height'] + $i * 2, |
| $startAngle, |
| $endAngle, |
| $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ), |
| true |
| ); |
| } |
| } |
| } |
| |
| ksort( $zBuffer ); |
| foreach ( $zBuffer as $sides ) |
| { |
| foreach ( $sides as $side ) |
| { |
| call_user_func_array( array( $this->driver, $side['method'] ), $side['paramenters'] ); |
| } |
| } |
| |
| // Draw circle sector for front |
| foreach ( $this->circleSectors as $circleSector ) |
| { |
| $this->addElementReference( $circleSector['context'], |
| $this->driver->drawCircleSector( |
| $circleSector['center'], |
| $circleSector['width'], |
| $circleSector['height'], |
| $circleSector['start'], |
| $circleSector['end'], |
| $circleSector['color'], |
| true |
| ) |
| ); |
| |
| if ( $this->options->pieChartGleam !== false ) |
| { |
| $gradient = new ezcGraphLinearGradient( |
| $circleSector['center'], |
| new ezcGraphCoordinate( |
| $circleSector['center']->x - $circleSector['width'] / 2, |
| $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 * $this->options->pieChartRotation, |
| $circleSector['start'], |
| $circleSector['end'], |
| $gradient, |
| 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 ) |
| { |
| $radialGradient = new ezcGraphRadialGradient( |
| new ezcGraphCoordinate( |
| $circleSector['center']->x + $circleSector['width'] / 2 * cos( deg2rad( 135 ) ), |
| $circleSector['center']->y + $circleSector['height'] / 2 * sin( deg2rad( 135 ) ) |
| ), |
| $circleSector['width'], |
| $circleSector['height'], |
| $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ), |
| $this->options->pieChartGleamColor->transparent( .8 ) |
| ); |
| |
| $this->driver->drawCircularArc( |
| $circleSector['center'], |
| $circleSector['width'], |
| $circleSector['height'], |
| 0, |
| $circleSector['start'], |
| $circleSector['end'], |
| $radialGradient, |
| false |
| ); |
| } |
| } |
| } |
| |
| /** |
| * Draw collected front lines |
| * |
| * Draw all grid and axis lines, which should be redrawn in front of the |
| * data. |
| * |
| * @return void |
| */ |
| protected function finishFrontLines() |
| { |
| foreach ( $this->frontLines as $line ) |
| { |
| $this->driver->drawLine( |
| $line[0], |
| $line[1], |
| $line[2], |
| $line[3] |
| ); |
| } |
| } |
| |
| /** |
| * 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'] |
| ) |
| ); |
| } |
| } |
| |
| /** |
| * Draws a bar with a rectangular ground shape. |
| * |
| * @param ezcGraphContext $context |
| * @param ezcGraphColor $color |
| * @param ezcGraphCoordinate $position |
| * @param float $barWidth |
| * @param float $offset |
| * @param float $axisPosition |
| * @param float $startDepth |
| * @param float $midDepth |
| * @param float $endDepth |
| * @return void |
| */ |
| protected function drawRectangularBar( |
| ezcGraphContext $context, |
| ezcGraphColor $color, |
| ezcGraphCoordinate $position, |
| $barWidth, |
| $offset, |
| $axisPosition, |
| $startDepth, |
| $midDepth, |
| $endDepth ) |
| { |
| $barPolygonArray = array( |
| new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset, |
| $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ), |
| new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset, |
| $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ), |
| new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth, |
| $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ), |
| new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth, |
| $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ), |
| ); |
| |
| // Draw right bar side |
| $this->barPostProcessing[] = array( |
| 'index' => $barPolygonArray[2]->x + ( 1 - $position->y ), |
| 'method' => 'drawPolygon', |
| 'context' => $context, |
| 'parameters' => array( |
| array( |
| $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[3], $endDepth ), |
| $this->get3dCoordinate( $barPolygonArray[2], $endDepth ), |
| ), |
| $color->darken( $this->options->barDarkenSide ), |
| true |
| ), |
| ); |
| |
| // Draw top side |
| $this->barPostProcessing[] = array( |
| 'index' => $barPolygonArray[1]->x + ( 1 - $position->y ), |
| 'method' => 'drawPolygon', |
| 'context' => $context, |
| 'parameters' => array( |
| ( $barPolygonArray[1]->y < $barPolygonArray[3]->y |
| ? array( |
| $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[2], $endDepth ), |
| $this->get3dCoordinate( $barPolygonArray[1], $endDepth ), |
| ) |
| : array( |
| $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[3], $endDepth ), |
| $this->get3dCoordinate( $barPolygonArray[0], $endDepth ), |
| ) |
| ), |
| $color->darken( $this->options->barDarkenTop ), |
| true |
| ), |
| ); |
| |
| // Draw top side gleam |
| if ( $this->options->barChartGleam !== false ) |
| { |
| $this->barPostProcessing[] = array( |
| 'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ), |
| 'method' => 'drawPolygon', |
| 'context' => $context, |
| 'parameters' => array( |
| ( $barPolygonArray[1]->y < $barPolygonArray[3]->y |
| ? array( |
| $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[2], $endDepth ), |
| $this->get3dCoordinate( $barPolygonArray[1], $endDepth ), |
| ) |
| : array( |
| $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[3], $endDepth ), |
| $this->get3dCoordinate( $barPolygonArray[0], $endDepth ), |
| ) |
| ), |
| new ezcGraphLinearGradient( |
| ( $barPolygonArray[1]->y < $barPolygonArray[3]->y |
| ? $this->get3dCoordinate( $barPolygonArray[2], $endDepth ) |
| : $this->get3dCoordinate( $barPolygonArray[3], $endDepth ) |
| ), |
| ( $barPolygonArray[1]->y < $barPolygonArray[3]->y |
| ? $this->get3dCoordinate( $barPolygonArray[1], $startDepth ) |
| : $this->get3dCoordinate( $barPolygonArray[0], $startDepth ) |
| ), |
| ezcGraphColor::fromHex( '#FFFFFFFF' ), |
| ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam ) |
| ), |
| true |
| ), |
| ); |
| } |
| |
| // Draw front side |
| $this->barPostProcessing[] = array( |
| 'index' => $barPolygonArray[1]->x + ( 1 - $position->y ), |
| 'method' => 'drawPolygon', |
| 'context' => $context, |
| 'parameters' => array( |
| array( |
| $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), |
| ), |
| $color, |
| true |
| ), |
| ); |
| |
| // Draw front side gleam |
| if ( $this->options->barChartGleam !== false ) |
| { |
| $this->barPostProcessing[] = array( |
| 'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ), |
| 'method' => 'drawPolygon', |
| 'context' => $context, |
| 'parameters' => array( |
| array( |
| $this->get3dCoordinate( $barPolygonArray[0], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[2], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), |
| ), |
| new ezcGraphLinearGradient( |
| $this->get3dCoordinate( $barPolygonArray[3], $startDepth ), |
| $this->get3dCoordinate( $barPolygonArray[1], $startDepth ), |
| ezcGraphColor::fromHex( '#FFFFFFFF' ), |
| ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam ) |
| ), |
| true |
| ), |
| ); |
| } |
| } |
| |
| /** |
| * Draws a bar with a diamond ground shape. |
| * |
| * @param ezcGraphContext $context |
| * @param ezcGraphColor $color |
| * @param ezcGraphCoordinate $position |
| * @param float $barWidth |
| * @param float $offset |
| * @param float $axisPosition |
| * @param float $startDepth |
| * @param float $midDepth |
| * @param float $endDepth |
| * @return void |
| */ |
| protected function drawDiamondBar( |
| ezcGraphContext $context, |
| ezcGraphColor $color, |
| ezcGraphCoordinate $position, |
| $barWidth, |
| $offset, |
| $axisPosition, |
| $startDepth, |
| $midDepth, |
| $endDepth ) |
| { |
| $barCoordinateArray = array( |
| // The bottom point of the diamond is moved to .7 instead |
| // of .5 because it looks more correct, even it is wrong... |
| 'x' => array( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset, |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .7, |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth, |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .3, |
| ), |
| 'y' => array( |
| $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ), |
| $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ), |
| ), |
| ); |
| |
| // Left side |
| $this->barPostProcessing[] = array( |
| 'index' => $barCoordinateArray['x'][0], |
| 'method' => 'drawPolygon', |
| 'context' => $context, |
| 'parameters' => array( |
| array( |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][0] ), $midDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][1] ), $midDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ), |
| ), |
| $color, |
| true |
| ), |
| ); |
| |
| // Right side |
| $this->barPostProcessing[] = array( |
| 'index' => $barCoordinateArray['x'][1], |
| 'method' => 'drawPolygon', |
| 'context' => $context, |
| 'parameters' => array( |
| array( |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][0] ), $midDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][1] ), $midDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ), |
| ), |
| $color->darken( $this->options->barDarkenSide ), |
| true |
| ), |
| ); |
| |
| $topLocation = min( |
| $barCoordinateArray['y'][0], |
| $barCoordinateArray['y'][1] |
| ); |
| |
| // Top side |
| $this->barPostProcessing[] = array( |
| 'index' => $barCoordinateArray['x'][0], |
| 'method' => 'drawPolygon', |
| 'context' => $context, |
| 'parameters' => array( |
| array( |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ), |
| ), |
| $color->darken( $this->options->barDarkenTop ), |
| true |
| ), |
| ); |
| |
| // Top side gleam |
| if ( $this->options->barChartGleam !== false ) |
| { |
| $this->barPostProcessing[] = array( |
| 'index' => $barCoordinateArray['x'][0] + 1, |
| 'method' => 'drawPolygon', |
| 'context' => $context, |
| 'parameters' => array( |
| array( |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ), |
| ), |
| new ezcGraphLinearGradient( |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ), |
| $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ), |
| ezcGraphColor::fromHex( '#FFFFFFFF' ), |
| ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam ) |
| ), |
| true |
| ), |
| ); |
| } |
| } |
| |
| /** |
| * Draws a bar with a circular ground shape. |
| * |
| * @param ezcGraphContext $context |
| * @param ezcGraphColor $color |
| * @param ezcGraphCoordinate $position |
| * @param float $barWidth |
| * @param float $offset |
| * @param float $axisPosition |
| * @param float $startDepth |
| * @param float $midDepth |
| * @param float $endDepth |
| * @param int $symbol |
| * @return void |
| */ |
| protected function drawCircularBar( |
| ezcGraphContext $context, |
| ezcGraphColor $color, |
| ezcGraphCoordinate $position, |
| $barWidth, |
| $offset, |
| $axisPosition, |
| $startDepth, |
| $midDepth, |
| $endDepth, |
| $symbol ) |
| { |
| $barCenterTop = new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2, |
| $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| |
| ); |
| $barCenterBottom = new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2, |
| $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ); |
| |
| if ( $barCenterTop->y > $barCenterBottom->y ) |
| { |
| $tmp = $barCenterTop; |
| $barCenterTop = $barCenterBottom; |
| $barCenterBottom = $tmp; |
| } |
| |
| $this->barPostProcessing[] = array( |
| 'index' => $barCenterBottom->x, |
| 'method' => 'drawCircularArc', |
| 'context' => $context, |
| 'parameters' => array( |
| $this->get3dCoordinate( $barCenterTop, $midDepth ), |
| $barWidth, |
| $barWidth / 2, |
| ( $barCenterBottom->y - $barCenterTop->y ) * $this->yDepthFactor, |
| 0, |
| 180, |
| $color |
| ), |
| ); |
| |
| $this->barPostProcessing[] = array( |
| 'index' => $barCenterBottom->x + 1, |
| 'method' => 'drawCircle', |
| 'context' => $context, |
| 'parameters' => array( |
| $top = $this->get3dCoordinate( $barCenterTop, $midDepth ), |
| $barWidth, |
| $barWidth / 2, |
| ( $symbol === ezcGraph::CIRCLE |
| ? new ezcGraphLinearGradient( |
| new ezcGraphCoordinate( |
| $top->x - $barWidth / 2, |
| $top->y |
| ), |
| new ezcGraphCoordinate( |
| $top->x + $barWidth / 2, |
| $top->y |
| ), |
| $color->darken( $this->options->barDarkenTop ), |
| $color |
| ) |
| : $color |
| ) |
| ), |
| ); |
| } |
| |
| /** |
| * 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; |
| |
| if ( $barWidth < 0 ) |
| { |
| $offset -= $barWidth = abs( $barWidth ); |
| } |
| |
| $startDepth = $this->options->barMargin; |
| $midDepth = .5; |
| $endDepth = 1 - $this->options->barMargin; |
| |
| switch ( $symbol ) |
| { |
| case ezcGraph::NO_SYMBOL: |
| $this->drawRectangularBar( |
| $context, |
| $color, |
| $position, |
| $barWidth, |
| $offset, |
| $axisPosition, |
| $startDepth, |
| $midDepth, |
| $endDepth |
| ); |
| break; |
| case ezcGraph::DIAMOND: |
| $this->drawDiamondBar( |
| $context, |
| $color, |
| $position, |
| $barWidth, |
| $offset, |
| $axisPosition, |
| $startDepth, |
| $midDepth, |
| $endDepth |
| ); |
| break; |
| case ezcGraph::BULLET: |
| case ezcGraph::CIRCLE: |
| $this->drawCircularBar( |
| $context, |
| $color, |
| $position, |
| $barWidth, |
| $offset, |
| $axisPosition, |
| $startDepth, |
| $midDepth, |
| $endDepth, |
| $symbol |
| ); |
| break; |
| } |
| } |
| |
| /** |
| * 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; |
| |
| if ( $barWidth < 0 ) |
| { |
| $offset -= $barWidth = abs( $barWidth ); |
| } |
| |
| $startDepth = $this->options->barMargin; |
| $midDepth = .5; |
| $endDepth = 1 - $this->options->barMargin; |
| |
| switch ( $symbol ) |
| { |
| case ezcGraph::NO_SYMBOL: |
| case ezcGraph::DIAMOND: |
| case ezcGraph::BULLET: |
| case ezcGraph::CIRCLE: |
| $this->drawRectangularBar( |
| $context, |
| $color, |
| $position, |
| $barWidth, |
| $offset, |
| $start->y, |
| $startDepth, |
| $midDepth, |
| $endDepth |
| ); |
| break; |
| } |
| } |
| |
| /** |
| * Draw all collected bar elements |
| * |
| * Draw all collected bar elements after sorting them depending of their |
| * position to simulate simple z buffering. |
| * |
| * @access protected |
| * @return void |
| */ |
| protected function finishBars() |
| { |
| if ( !count( $this->barPostProcessing ) ) |
| { |
| return true; |
| } |
| |
| $zIndexArray = array(); |
| foreach ( $this->barPostProcessing as $key => $barPolygon ) |
| { |
| $zIndexArray[$key] = $barPolygon['index']; |
| } |
| |
| array_multisort( |
| $zIndexArray, SORT_ASC, SORT_NUMERIC, |
| $this->barPostProcessing |
| ); |
| |
| foreach ( $this->barPostProcessing as $bar ) |
| { |
| $this->addElementReference( $bar['context'], |
| call_user_func_array( |
| array( $this->driver, $bar['method'] ), |
| $bar['parameters'] |
| ) |
| ); |
| } |
| } |
| |
| /** |
| * 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 = 0, |
| $dataCount = 1, |
| $symbol = ezcGraph::NO_SYMBOL, |
| ezcGraphColor $symbolColor = null, |
| ezcGraphColor $fillColor = null, |
| $axisPosition = 0., |
| $thickness = 1. ) |
| { |
| // Calculate line width based on options |
| if ( $this->options->seperateLines ) |
| { |
| $startDepth = ( 1 / $dataCount ) * $dataNumber; |
| $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 ); |
| } |
| else |
| { |
| $startDepth = false; |
| $endDepth = true; |
| } |
| |
| // Determine Coordinates depending on boundings and data point position |
| $startCoord = new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), |
| $this->dataBoundings->y0 + $this->yAxisSpace + $start->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ); |
| $endCoord = new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), |
| $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ); |
| |
| // 3D-fy coordinates |
| $linePolygonPoints = array( |
| $this->get3dCoordinate( $startCoord, $startDepth ), |
| $this->get3dCoordinate( $endCoord, $startDepth ), |
| $this->get3dCoordinate( $endCoord, $endDepth ), |
| $this->get3dCoordinate( $startCoord, $endDepth ), |
| ); |
| |
| $startAxisCoord = new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), |
| $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ); |
| $endAxisCoord = new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), |
| $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ); |
| |
| // 3D-fy coordinates |
| $axisPolygonPoints = array( |
| $this->get3dCoordinate( $startAxisCoord, $startDepth ), |
| $this->get3dCoordinate( $endAxisCoord, $startDepth ), |
| $this->get3dCoordinate( $endAxisCoord, $endDepth ), |
| $this->get3dCoordinate( $startAxisCoord, $endDepth ), |
| ); |
| |
| // 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( |
| $linePolygonPoints[0], |
| $linePolygonPoints[1], |
| $this->get3dCoordinate( $endAxisCoord, $startDepth ), |
| $this->get3dCoordinate( $startAxisCoord, $startDepth ), |
| ), |
| $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( |
| $startCoord->x + ( $endCoord->x - $startCoord->x ) * $cuttingPosition, |
| $startAxisCoord->y |
| ); |
| |
| $this->driver->drawPolygon( |
| array( |
| $this->get3dCoordinate( $startAxisCoord, $startDepth ), |
| $linePolygonPoints[0], |
| $this->get3dCoordinate( $cuttingPoint, $startDepth ), |
| ), |
| $fillColor, |
| true |
| ); |
| |
| $this->driver->drawPolygon( |
| array( |
| $this->get3dCoordinate( $endAxisCoord, $startDepth ), |
| $linePolygonPoints[1], |
| $this->get3dCoordinate( $cuttingPoint, $startDepth ), |
| ), |
| $fillColor, |
| true |
| ); |
| } |
| |
| // Draw closing foo |
| $this->driver->drawPolygon( |
| array( |
| $linePolygonPoints[2], |
| $linePolygonPoints[1], |
| $this->get3dCoordinate( $endAxisCoord, $startDepth ), |
| $this->get3dCoordinate( $endAxisCoord, $endDepth ), |
| ), |
| $fillColor, |
| true |
| ); |
| } |
| |
| |
| // Draw line |
| $this->driver->drawPolygon( |
| $linePolygonPoints, |
| $color, |
| true, |
| $thickness |
| ); |
| |
| // Draw polygon border |
| if ( $this->options->dataBorder > 0 ) |
| { |
| $this->driver->drawPolygon( |
| $linePolygonPoints, |
| $color->darken( $this->options->dataBorder ), |
| false, |
| $thickness |
| ); |
| } |
| |
| // Draw line symbol |
| if ( $this->options->showSymbol && |
| ( $symbol !== ezcGraph::NO_SYMBOL ) ) |
| { |
| if ( $symbolColor === null ) |
| { |
| $symbolColor = $color; |
| } |
| |
| $this->linePostSymbols[] = array( |
| 'boundings' => new ezcGraphBoundings( |
| $linePolygonPoints[2]->x - $this->options->symbolSize / 2, |
| $linePolygonPoints[2]->y - $this->options->symbolSize / 2, |
| $linePolygonPoints[2]->x + $this->options->symbolSize / 2, |
| $linePolygonPoints[2]->y + $this->options->symbolSize / 2 |
| ), |
| 'color' => $symbolColor, |
| 'context' => $context, |
| 'symbol' => $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 ) |
| { |
| $this->driver->options->font = $font; |
| $width = $this->dataBoundings->width / $dataCount; |
| |
| // Calculate line width based on options |
| if ( $this->options->seperateLines ) |
| { |
| $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 ); |
| } |
| else |
| { |
| $endDepth = true; |
| } |
| |
| $dataPoint = new ezcGraphCoordinate( |
| $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ), |
| $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ) |
| ); |
| |
| if ( $end->y < $axisPosition ) |
| { |
| $this->driver->drawTextBox( |
| $text, |
| $this->get3dCoordinate( new ezcGraphCoordinate( |
| $dataPoint->x - $width / 2, |
| $dataPoint->y - $size - $font->padding - $this->options->symbolSize |
| ), $endDepth ), |
| $width * $this->xDepthFactor, |
| $size, |
| ezcGraph::CENTER | ezcGraph::BOTTOM |
| ); |
| } |
| else |
| { |
| $this->driver->drawTextBox( |
| $text, |
| $this->get3dCoordinate( new ezcGraphCoordinate( |
| $dataPoint->x - $width / 2, |
| $dataPoint->y + $font->padding + $this->options->symbolSize |
| ), $endDepth ), |
| $width * $this->xDepthFactor, |
| $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->x1 - $boundings->x0; |
| $labelHeight = min( |
| ( $boundings->y1 - $boundings->y0 ) / count( $labels ) - $legend->spacing, |
| $legend->symbolSize + 2 * $legend->padding |
| ); |
| } |
| else |
| { |
| $labelWidth = ( $boundings->x1 - $boundings->x0 ) / 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->x1 - $boundings->x0, |
| $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->x1 - $boundings->x0, |
| $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 ) |
| { |
| if ( $this->depth === false ) |
| { |
| // We are not 3d for now, wg. rendering normal text boxes like the |
| // title |
| $topleft = new ezcGraphCoordinate( |
| $boundings->x0, |
| $boundings->y0 |
| ); |
| $bottomright = new ezcGraphCoordinate( |
| $boundings->x1, |
| $boundings->y1 |
| ); |
| } |
| else |
| { |
| // The 3d part started |
| $topleft = $this->get3dCoordinate( |
| new ezcGraphCoordinate( |
| $boundings->x0, |
| $boundings->y0 |
| ), false |
| ); |
| $bottomright = $this->get3dCoordinate( |
| new ezcGraphCoordinate( |
| $boundings->x1, |
| $boundings->y1 |
| ), false |
| ); |
| |
| // Also modify rotation accordingly |
| if ( $rotation !== null ) |
| { |
| $rotation = new ezcGraphRotation( |
| $rotation->getRotation(), |
| $this->get3dCoordinate( $rotation->getCenter(), false ) |
| ); |
| } |
| } |
| |
| $this->driver->drawTextBox( |
| $text, |
| $topleft, |
| $bottomright->x - $topleft->x, |
| $bottomright->y - $topleft->y, |
| $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 ) |
| { |
| $gridPolygonCoordinates = array( |
| $this->get3dCoordinate( $start, false ), |
| $this->get3dCoordinate( $end, false ), |
| $this->get3dCoordinate( $end, true ), |
| $this->get3dCoordinate( $start, true ), |
| ); |
| |
| // Draw grid polygon |
| if ( $this->options->fillGrid === 0 ) |
| { |
| $this->driver->drawLine( |
| $gridPolygonCoordinates[2], |
| $gridPolygonCoordinates[3], |
| $color |
| ); |
| } |
| else |
| { |
| if ( $this->options->fillGrid === 1 ) |
| { |
| $this->driver->drawPolygon( |
| $gridPolygonCoordinates, |
| $color, |
| true |
| ); |
| } |
| else |
| { |
| $this->driver->drawPolygon( |
| $gridPolygonCoordinates, |
| $color->transparent( $this->options->fillGrid ), |
| true |
| ); |
| } |
| |
| // Draw grid lines - scedule some for later to be drawn in front of |
| // the data |
| $this->frontLines[] = array( |
| $gridPolygonCoordinates[0], |
| $gridPolygonCoordinates[1], |
| $color, |
| 1 |
| ); |
| |
| $this->frontLines[] = array( |
| $gridPolygonCoordinates[1], |
| $gridPolygonCoordinates[2], |
| $color, |
| 1 |
| ); |
| |
| $this->driver->drawLine( |
| $gridPolygonCoordinates[2], |
| $gridPolygonCoordinates[3], |
| $color, |
| 1 |
| ); |
| |
| $this->frontLines[] = array( |
| $gridPolygonCoordinates[3], |
| $gridPolygonCoordinates[0], |
| $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 ) |
| { |
| $stepPolygonCoordinates = array( |
| $this->get3dCoordinate( $start, true ), |
| $this->get3dCoordinate( $end, true ), |
| $this->get3dCoordinate( $end, false ), |
| $this->get3dCoordinate( $start, false ), |
| ); |
| |
| // Draw step polygon |
| if ( ( $this->options->fillAxis > 0 ) && |
| ( $this->options->fillAxis < 1 ) ) |
| { |
| $this->driver->drawPolygon( |
| $stepPolygonCoordinates, |
| $color->transparent( $this->options->fillAxis ), |
| true |
| ); |
| |
| $this->driver->drawPolygon( |
| $stepPolygonCoordinates, |
| $color, |
| false |
| ); |
| } |
| else |
| { |
| $this->driver->drawPolygon( |
| $stepPolygonCoordinates, |
| $color, |
| ! (bool) $this->options->fillAxis |
| ); |
| } |
| } |
| |
| /** |
| * 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 ) |
| { |
| // Calculate used space for three dimensional effects |
| if ( $this->depth === false ) |
| { |
| $this->depth = min( |
| ( $boundings->x1 - $boundings->x0 ) * $this->options->depth, |
| ( $boundings->y1 - $boundings->y0 ) * $this->options->depth |
| ); |
| |
| $this->xDepthFactor = 1 - $this->depth / ( $boundings->x1 - $boundings->x0 ); |
| $this->yDepthFactor = 1 - $this->depth / ( $boundings->y1 - $boundings->y0 ); |
| |
| $this->dataBoundings = clone $boundings; |
| } |
| |
| // Clone boundings to not be affected by internal mofifications |
| $boundings = clone $boundings; |
| |
| switch ( $axis->position ) |
| { |
| case ezcGraph::TOP: |
| case ezcGraph::BOTTOM: |
| $this->xAxisSpace = ( $this->dataBoundings->x1 - $this->dataBoundings->x0 ) * $axis->axisSpace; |
| break; |
| case ezcGraph::LEFT: |
| case ezcGraph::RIGHT: |
| $this->yAxisSpace = ( $this->dataBoundings->y1 - $this->dataBoundings->y0 ) * $axis->axisSpace; |
| break; |
| } |
| |
| // Determine normalized direction |
| $direction = new ezcGraphVector( |
| $start->x - $end->x, |
| $start->y - $end->y |
| ); |
| $direction->unify(); |
| |
| $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; |
| } |
| |
| $axisPolygonCoordinates = array( |
| $this->get3dCoordinate( $axisStart, true ), |
| $this->get3dCoordinate( $axisEnd, true ), |
| $this->get3dCoordinate( $axisEnd, false ), |
| $this->get3dCoordinate( $axisStart, false ), |
| ); |
| |
| // Draw axis |
| if ( ( $this->options->fillAxis > 0 ) && |
| ( $this->options->fillAxis < 1 ) ) |
| { |
| $this->driver->drawPolygon( |
| $axisPolygonCoordinates, |
| $axis->border->transparent( $this->options->fillAxis ), |
| true |
| ); |
| } |
| else |
| { |
| $this->driver->drawPolygon( |
| $axisPolygonCoordinates, |
| $axis->border, |
| ! (bool) $this->options->fillAxis |
| ); |
| } |
| |
| // Draw axis lines - scedule some for later to be drawn in front of |
| // the data |
| $this->driver->drawLine( |
| $axisPolygonCoordinates[0], |
| $axisPolygonCoordinates[1], |
| $axis->border, |
| 1 |
| ); |
| |
| $this->frontLines[] = array( |
| $axisPolygonCoordinates[1], |
| $axisPolygonCoordinates[2], |
| $axis->border, |
| 1 |
| ); |
| |
| $this->frontLines[] = array( |
| $axisPolygonCoordinates[2], |
| $axisPolygonCoordinates[3], |
| $axis->border, |
| 1 |
| ); |
| |
| $this->frontLines[] = array( |
| $axisPolygonCoordinates[3], |
| $axisPolygonCoordinates[0], |
| $axis->border, |
| 1 |
| ); |
| |
| // Draw small arrowhead |
| $this->drawAxisArrowHead( |
| $axisPolygonCoordinates[1], |
| $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 ( $axis->label !== false ) |
| { |
| $width = $this->dataBoundings->x1 - $this->dataBoundings->x0; |
| switch ( $axis->position ) |
| { |
| case ezcGraph::TOP: |
| $this->driver->drawTextBox( |
| $axis->label, |
| new ezcGraphCoordinate( |
| $axisPolygonCoordinates[2]->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ), |
| $axisPolygonCoordinates[2]->y - $axis->labelMargin - $axis->labelSize |
| ), |
| $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, |
| $axis->labelSize, |
| ezcGraph::TOP | ezcGraph::RIGHT |
| ); |
| break; |
| case ezcGraph::BOTTOM: |
| $this->driver->drawTextBox( |
| $axis->label, |
| new ezcGraphCoordinate( |
| $axisPolygonCoordinates[1]->x + $axis->labelMargin, |
| $axisPolygonCoordinates[1]->y + $axis->labelMargin |
| ), |
| $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin, |
| $axis->labelSize, |
| ezcGraph::TOP | ezcGraph::LEFT |
| ); |
| break; |
| case ezcGraph::LEFT: |
| $this->driver->drawTextBox( |
| $axis->label, |
| new ezcGraphCoordinate( |
| $axisPolygonCoordinates[1]->x - $width, |
| $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin |
| ), |
| $width - $axis->labelMargin, |
| $axis->labelSize, |
| ezcGraph::BOTTOM | ezcGraph::RIGHT |
| ); |
| break; |
| case ezcGraph::RIGHT: |
| $this->driver->drawTextBox( |
| $axis->label, |
| new ezcGraphCoordinate( |
| $axisPolygonCoordinates[1]->x, |
| $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin |
| ), |
| $width - $axis->labelMargin, |
| $axis->labelSize, |
| ezcGraph::BOTTOM | ezcGraph::LEFT |
| ); |
| break; |
| } |
| } |
| |
| // 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 ) |
| { |
| foreach ( $this->axisLabels as $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; |
| } |
| |
| switch ( $axisLabel['axis']->position ) |
| { |
| case ezcGraph::RIGHT: |
| case ezcGraph::LEFT: |
| $axisLabel['start']->x += $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); |
| $axisLabel['end']->x -= $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); |
| break; |
| case ezcGraph::TOP: |
| case ezcGraph::BOTTOM: |
| $axisLabel['start']->y += $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); |
| $axisLabel['end']->y -= $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 ); |
| break; |
| } |
| |
| $axisLabel['object']->renderLabels( |
| $this, |
| $axisLabel['boundings'], |
| $axisLabel['start'], |
| $axisLabel['end'], |
| $axisLabel['axis'] |
| ); |
| } |
| } |
| } |
| |
| /** |
| * 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->x1 - $boundings->x0 ); |
| $imageHeight = min( $imageHeight, $boundings->y1 - $boundings->y0 ); |
| |
| $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->x1 - $boundings->x0 - $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->y1 - $boundings->y0 - $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->finishCirleSectors(); |
| $this->finishPieSegmentLabels(); |
| $this->finishBars(); |
| $this->finishLineSymbols(); |
| $this->finishFrontLines(); |
| |
| 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 3D renderer options |
| $this->pieSegmentLabels = array( |
| 0 => array(), |
| 1 => array(), |
| ); |
| $this->pieSegmentBoundings = false; |
| $this->linePostSymbols = array(); |
| $this->frontLines = array(); |
| $this->circleSectors = array(); |
| $this->barPostProcessing = array(); |
| $this->depth = false; |
| $this->xDepthFactor = false; |
| $this->yDepthFactor = false; |
| $this->dataBoundings = false; |
| $this->axisLabels = array(); |
| } |
| } |
| |
| ?> |