[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

/library/ezc/Graph/src/renderer/ -> 3d.php (source)

   1  <?php
   2  /**
   3   * File containing the three dimensional renderer
   4   *
   5   * @package Graph
   6   * @version 1.5
   7   * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
   8   * @license http://ez.no/licenses/new_bsd New BSD License
   9   */
  10  /**
  11   * Class to transform chart primitives into image primitives. This renderer
  12   * renders the charts in a isometric three dimensional view.
  13   *
  14   * The class options are defined in the class {@link ezcGraphRenderer3dOptions}
  15   * extending the basic renderer options in {@link ezcGraphRendererOptions}.
  16   *
  17   * <code>
  18   *   $graph = new ezcGraphPieChart();
  19   *   $graph->palette = new ezcGraphPaletteEzRed();
  20   *   $graph->title = 'Access statistics';
  21   *   $graph->options->label = '%2$d (%3$.1f%%)';
  22   *   
  23   *   $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
  24   *       'Mozilla' => 19113,
  25   *       'Explorer' => 10917,
  26   *       'Opera' => 1464,
  27   *       'Safari' => 652,
  28   *       'Konqueror' => 474,
  29   *   ) );
  30   *   $graph->data['Access statistics']->highlight['Explorer'] = true;
  31   *   
  32   *   $graph->renderer = new ezcGraphRenderer3d();
  33   *   
  34   *   $graph->renderer->options->moveOut = .2;
  35   *   
  36   *   $graph->renderer->options->pieChartOffset = 63;
  37   *   
  38   *   $graph->renderer->options->pieChartGleam = .3;
  39   *   $graph->renderer->options->pieChartGleamColor = '#FFFFFF';
  40   *   
  41   *   $graph->renderer->options->pieChartShadowSize = 5;
  42   *   $graph->renderer->options->pieChartShadowColor = '#000000';
  43   *   
  44   *   $graph->renderer->options->legendSymbolGleam = .5;
  45   *   $graph->renderer->options->legendSymbolGleamSize = .9;
  46   *   $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF';
  47   *   
  48   *   $graph->renderer->options->pieChartSymbolColor = '#55575388';
  49   *   
  50   *   $graph->renderer->options->pieChartHeight = 5;
  51   *   $graph->renderer->options->pieChartRotation = .8;
  52   *   
  53   *   $graph->render( 400, 150, 'tutorial_pie_chart_3d.svg' );
  54   * </code>
  55   *
  56   * @version 1.5
  57   * @package Graph
  58   * @mainclass
  59   */
  60  class ezcGraphRenderer3d 
  61      extends 
  62          ezcGraphRenderer
  63      implements
  64          ezcGraphStackedBarsRenderer
  65  {
  66  
  67      /**
  68       * Pie segment labels divided into two array, containing the labels on the
  69       * left and right side of the pie chart center.
  70       * 
  71       * @var array
  72       */
  73      protected $pieSegmentLabels = array(
  74          0 => array(),
  75          1 => array(),
  76      );
  77  
  78      /**
  79       * Contains the boundings used for pie segments
  80       * 
  81       * @var ezcGraphBoundings
  82       */
  83      protected $pieSegmentBoundings = false;
  84  
  85      /**
  86       * Array with symbols for post processing, which ensures, that the symbols
  87       * are rendered topmost.
  88       * 
  89       * @var array
  90       */
  91      protected $linePostSymbols = array();
  92  
  93      /**
  94       * Array containing lines from the axis and grid which should be redrawn on
  95       * top of the data.
  96       * 
  97       * @var array
  98       */
  99      protected $frontLines = array();
 100  
 101      /**
 102       * Collects circle sectors to draw shadow in background of all circle 
 103       * sectors.
 104       * 
 105       * @var array
 106       */
 107      protected $circleSectors = array();
 108  
 109      /**
 110       * Collects bar sides to draw them in a post processing step to simulate
 111       * a simple z buffer.
 112       *  array(
 113       *      array(
 114       *          'index' => (int) // used for sorting
 115       *          'context' => ezcGraphContext // context of call
 116       *          'method' => (string) // method of driver to call
 117       *          'parameters' => array // parameters for method call
 118       *      ), ...
 119       *  )
 120       *
 121       * @var array
 122       */
 123      protected $barPostProcessing = array();
 124  
 125      /**
 126       * Options
 127       * 
 128       * @var ezcGraphRenderer3dOptions
 129       */
 130      protected $options;
 131  
 132      /**
 133       * Depth of displayed pseudo three dimensional line chart elements.
 134       * 
 135       * @var float
 136       */
 137      protected $depth = false;
 138  
 139      /**
 140       * Factor to reduce the width according to depth 
 141       * 
 142       * @var float
 143       */
 144      protected $xDepthFactor = false;
 145  
 146      /**
 147       * Factor to reduce the height according to depth 
 148       * 
 149       * @var float
 150       */
 151      protected $yDepthFactor = false;
 152  
 153      /**
 154       * Boundings for the chart data
 155       * 
 156       * @var ezcGraphBoundings
 157       */
 158      protected $dataBoundings = false;
 159  
 160      /**
 161       * Collect axis labels, so that the axis are drawn, when all axis spaces 
 162       * are known.
 163       * 
 164       * @var array
 165       */
 166      protected $axisLabels = array();
 167      
 168      /**
 169       * Constructor
 170       * 
 171       * @param array $options Default option array
 172       * @return void
 173       * @ignore
 174       */
 175      public function __construct( array $options = array() )
 176      {
 177          $this->options = new ezcGraphRenderer3dOptions( $options );
 178      }
 179  
 180      /**
 181       * __get 
 182       * 
 183       * @param mixed $propertyName 
 184       * @throws ezcBasePropertyNotFoundException
 185       *          If a the value for the property options is not an instance of
 186       * @return mixed
 187       * @ignore
 188       */
 189      public function __get( $propertyName )
 190      {
 191          switch ( $propertyName )
 192          {
 193              case 'options':
 194                  return $this->options;
 195              default:
 196                  return parent::__get( $propertyName );
 197          }
 198      }
 199  
 200      /**
 201       * Calculate the display coordinate from a coordinate
 202       *
 203       * Calculates the display coordinate of a coordinate depending on the 
 204       * depth setting and the distance of the coordinate to the front of the 
 205       * chart.
 206       * 
 207       * @param ezcGraphCoordinate $c Coordinate
 208       * @param float $front Distance to front (0 - 1)
 209       * @return ezcGraphCoordinate Resulting coordinate
 210       */
 211      protected function get3dCoordinate( ezcGraphCoordinate $c, $front = 1. )
 212      {
 213          return new ezcGraphCoordinate(
 214              ( $c->x - $this->dataBoundings->x0 ) * $this->xDepthFactor + $this->dataBoundings->x0 + $this->depth * $front,
 215              ( $c->y - $this->dataBoundings->y0 ) * $this->yDepthFactor + $this->dataBoundings->y0 + $this->depth * ( 1 - $front )
 216          );
 217      }
 218  
 219      /**
 220       * Draw pie segment
 221       *
 222       * Draws a single pie segment
 223       * 
 224       * @param ezcGraphBoundings $boundings Chart boundings
 225       * @param ezcGraphContext $context Context of call
 226       * @param ezcGraphColor $color Color of pie segment
 227       * @param float $startAngle Start angle
 228       * @param float $endAngle End angle
 229       * @param mixed $label Label of pie segment
 230       * @param bool $moveOut Move out from middle for hilighting
 231       * @return void
 232       */
 233      public function drawPieSegment(
 234          ezcGraphBoundings $boundings,
 235          ezcGraphContext $context,
 236          ezcGraphColor $color,
 237          $startAngle = .0,
 238          $endAngle = 360.,
 239          $label = false,
 240          $moveOut = false )
 241      {
 242          // Apply offset
 243          $startAngle += $this->options->pieChartOffset;
 244          $endAngle += $this->options->pieChartOffset;
 245  
 246          // Calculate position and size of pie
 247          $center = new ezcGraphCoordinate(
 248              $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
 249              $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2 
 250                  - $this->options->pieChartHeight / 2 
 251          );
 252  
 253          // Limit radius to fourth of width and half of height at maximum
 254          $radius = min(
 255              ( $boundings->x1 - $boundings->x0 ) * $this->options->pieHorizontalSize,
 256              ( $boundings->y1 - $boundings->y0 ) * $this->options->pieVerticalSize
 257          );
 258  
 259          // Move pie segment out of the center
 260          if ( $moveOut )
 261          {
 262              $direction = ( $endAngle + $startAngle ) / 2;
 263  
 264              $center = new ezcGraphCoordinate(
 265                  $center->x + $this->options->moveOut * $radius * cos( deg2rad( $direction ) ),
 266                  $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) ) * $this->options->pieChartRotation
 267              );
 268          }
 269  
 270          // Add circle sector to queue
 271          $this->circleSectors[] = array(
 272              'center' =>     $center,
 273              'context' =>    $context,
 274              'width' =>      $radius * 2 * ( 1 - $this->options->moveOut ),
 275              'height' =>     $radius * 2 * ( 1 - $this->options->moveOut ) * $this->options->pieChartRotation - $this->options->pieChartHeight,
 276              'start' =>      $startAngle,
 277              'end' =>        $endAngle,
 278              'color' =>      $color,
 279          );
 280  
 281          if ( $label )
 282          {
 283              // Determine position of label
 284              $direction = ( $endAngle + $startAngle ) / 2;
 285              $pieSegmentCenter = new ezcGraphCoordinate(
 286                  $center->x + cos( deg2rad( $direction ) ) * $radius,
 287                  $center->y + sin( deg2rad( $direction ) ) * $radius * $this->options->pieChartRotation
 288              );
 289  
 290              // Split labels up into left a right site and index them on their
 291              // y position
 292              $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][(int) ( $pieSegmentCenter->y * 100 )] = array(
 293                  new ezcGraphCoordinate(
 294                      $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3 * ( 1 - $this->options->moveOut ),
 295                      $center->y + sin( deg2rad( $direction ) ) * ( $radius - $this->options->pieChartHeight ) * 2 / 3 * ( 1 - $this->options->moveOut ) * $this->options->pieChartRotation
 296                  ),
 297                  $label,
 298                  $context,
 299              );
 300          }
 301  
 302          if ( !$this->pieSegmentBoundings )
 303          {
 304              $this->pieSegmentBoundings = $boundings;
 305          }
 306      }
 307  
 308      /**
 309       * Draws the collected pie segment labels
 310       *
 311       * All labels are collected and drawn later to be able to partition the 
 312       * available space for the labels woth knowledge of the overall label 
 313       * count and their required size and optimal position.
 314       * 
 315       * @return void
 316       */
 317      protected function finishPieSegmentLabels()
 318      {
 319          if ( $this->pieSegmentBoundings === false )
 320          {
 321              return true;
 322          }
 323  
 324          $boundings = $this->pieSegmentBoundings;
 325  
 326          // Calculate position and size of pie
 327          $center = new ezcGraphCoordinate(
 328              $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
 329              $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
 330          );
 331  
 332          // Limit radius to fourth of width and half of height at maximum
 333          $radius = min(
 334              ( $boundings->width ) * $this->options->pieHorizontalSize,
 335              ( $boundings->height ) * $this->options->pieVerticalSize
 336          );
 337  
 338          $pieChartHeight = min(
 339              $radius * 2 + $radius / max( 1, count ( $this->pieSegmentLabels[0] ), count( $this->pieSegmentLabels[1] ) ) * 4,
 340              $boundings->height
 341          );
 342          $pieChartYPosition = $boundings->y0 + ( ( $boundings->height ) - $pieChartHeight ) / 2;
 343  
 344          // Calculate maximum height of labels
 345          $labelHeight = min(
 346              ( count( $this->pieSegmentLabels[0] )
 347                  ? $pieChartHeight / count( $this->pieSegmentLabels[0] )
 348                  : $pieChartHeight
 349              ),
 350              ( count( $this->pieSegmentLabels[1] )
 351                  ? $pieChartHeight / count( $this->pieSegmentLabels[1] )
 352                  : $pieChartHeight
 353              ),
 354              ( $pieChartHeight ) * $this->options->maxLabelHeight
 355          );
 356  
 357          $symbolSize = $this->options->symbolSize;
 358  
 359          foreach ( $this->pieSegmentLabels as $side => $labelPart )
 360          {
 361              $minHeight = $pieChartYPosition;
 362              $toShare = $pieChartHeight - count( $labelPart ) * $labelHeight;
 363  
 364              // Sort to draw topmost label first
 365              ksort( $labelPart );
 366              $sign = ( $side ? -1 : 1 );
 367  
 368              foreach ( $labelPart as $height => $label )
 369              {
 370                  $height = (int) ( $height / 100 );
 371  
 372                  if ( ( $height - $labelHeight / 2 ) > $minHeight )
 373                  {
 374                      $share = min( $toShare, ( $height - $labelHeight / 2) - $minHeight );
 375                      $minHeight += $share;
 376                      $toShare -= $share;
 377                  }
 378  
 379                  // Determine position of label
 380                  $minHeight += max( 0, $height - $minHeight - $labelHeight ) / $pieChartHeight * $toShare;
 381                  $verticalDistance = ( $center->y - $minHeight - $labelHeight / 2 ) / $radius;
 382  
 383                  $labelPosition = new ezcGraphCoordinate(
 384                      $center->x - 
 385                      $sign * (
 386                          abs( $verticalDistance ) > 1
 387                          // If vertical distance to center is greater then the
 388                          // radius, use the centerline for the horizontal 
 389                          // position
 390                          ? max (
 391                              5,
 392                              abs( $label[0]->x - $center->x )
 393                          )
 394                          // Else place the label outside of the pie chart
 395                          : ( cos ( asin ( $verticalDistance ) ) * $radius + 
 396                              $symbolSize * (int) $this->options->showSymbol 
 397                          )
 398                      ),
 399                      $minHeight + $labelHeight / 2
 400                  );
 401  
 402                  if ( $this->options->showSymbol )
 403                  {
 404                      // Draw label
 405                      $this->driver->drawLine(
 406                          $label[0],
 407                          $labelPosition,
 408                          $this->options->pieChartSymbolColor,
 409                          1
 410                      );
 411  
 412                      $this->driver->drawCircle(
 413                          $label[0],
 414                          $symbolSize,
 415                          $symbolSize,
 416                          $this->options->pieChartSymbolColor,
 417                          true
 418                      );
 419                      $this->driver->drawCircle(
 420                          $labelPosition,
 421                          $symbolSize,
 422                          $symbolSize,
 423                          $this->options->pieChartSymbolColor,
 424                          true
 425                      );
 426                  }
 427  
 428                  $this->addElementReference( $label[2], 
 429                      $this->driver->drawTextBox(
 430                          $label[1],
 431                          new ezcGraphCoordinate(
 432                              ( !$side ? $boundings->x0 : $labelPosition->x + $symbolSize ),
 433                              $minHeight
 434                          ),
 435                          ( !$side ? $labelPosition->x - $boundings->x0 - $symbolSize : $boundings->x1 - $labelPosition->x - $symbolSize ),
 436                          $labelHeight,
 437                          ( !$side ? ezcGraph::RIGHT : ezcGraph::LEFT ) | ezcGraph::MIDDLE
 438                      )
 439                  );
 440  
 441                  // Add used space to minHeight
 442                  $minHeight += $labelHeight;
 443              }
 444          }
 445      }
 446  
 447      /**
 448       * Draws the collected circle sectors
 449       *
 450       * All circle sectors are collected and drawn later to be able to render 
 451       * the shadows of the pie segments in the back of all pie segments, and 
 452       * ensure the correct drawing order for all pie segment elements.
 453       * 
 454       * @return void
 455       */
 456      protected function finishCirleSectors()
 457      {
 458          $zBuffer = array();
 459  
 460          $shadows = array();
 461          $shadowCenter = false;
 462          $shadowEndAngle = false;
 463  
 464          // Add circle sector sides to simple z buffer prioriry list
 465          foreach ( $this->circleSectors as $circleSector )
 466          {
 467              // Draw shadow if wanted
 468              if ( $this->options->pieChartShadowSize > 0 )
 469              {
 470                  if ( $shadowEndAngle === false )
 471                  {
 472                      $shadowStartAngle = $circleSector['start'];
 473                      $shadowEndAngle = $circleSector['end'];
 474                      $shadowCenter = $circleSector['center'];
 475                  }
 476                  elseif ( $circleSector['center'] == $shadowCenter )
 477                  {
 478                      $shadowEndAngle = $circleSector['end'];
 479                  }
 480                  else
 481                  {
 482                      $shadows[] = array( 
 483                          'center' => $shadowCenter,
 484                          'start' => $shadowStartAngle, 
 485                          'end' => $shadowEndAngle,
 486                          'width' => $circleSector['width'],
 487                          'height' => $circleSector['height'],
 488                      );
 489  
 490                      $shadowCenter = $circleSector['center'];
 491                      $shadowStartAngle = $circleSector['start'];
 492                      $shadowEndAngle = $circleSector['end'];
 493                  }
 494              }
 495  
 496              $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder );
 497  
 498              $center = (int) ( $circleSector['center']->y + sin( deg2rad( $circleSector['start'] + ( $circleSector['end'] - $circleSector['start'] ) / 2 ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight / 2 + 1 );
 499  
 500              $zBuffer[$center][] = array(
 501                  'method' => 'drawCircularArc',
 502                  'paramenters' => array(
 503                      $circleSector['center'],
 504                      $circleSector['width'],
 505                      $circleSector['height'],
 506                      $this->options->pieChartHeight,
 507                      $circleSector['start'],
 508                      $circleSector['end'],
 509                      $circleSector['color']
 510                  )
 511              );
 512  
 513              // Left side
 514              $polygonPoints = array(
 515                  $circleSector['center'],
 516                  new ezcGraphCoordinate(
 517                      $circleSector['center']->x,
 518                      $circleSector['center']->y + $this->options->pieChartHeight
 519                  ),
 520                  new ezcGraphCoordinate(
 521                      $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2,
 522                      $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight
 523                  ),
 524                  new ezcGraphCoordinate(
 525                      $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2,
 526                      $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2
 527                  ),
 528              );
 529  
 530              // Get average y coordinate for polygon to use for zBuffer
 531              $center = 0;
 532              foreach ( $polygonPoints as $point )
 533              {
 534                  $center += $point->y;
 535              }
 536              $center = (int) ( $center / count( $polygonPoints ) );
 537  
 538              $zBuffer[$center][] = array(
 539                  'method' => 'drawPolygon',
 540                  'paramenters' => array(
 541                      $polygonPoints,
 542                      $circleSector['color'],
 543                      true
 544                  ),
 545              );
 546  
 547              $zBuffer[$center][] = array(
 548                  'method' => 'drawPolygon',
 549                  'paramenters' => array(
 550                      $polygonPoints,
 551                      $darkenedColor,
 552                      false
 553                  ),
 554              );
 555  
 556              // Right side
 557              $polygonPoints = array(
 558                  $circleSector['center'],
 559                  new ezcGraphCoordinate(
 560                      $circleSector['center']->x,
 561                      $circleSector['center']->y + $this->options->pieChartHeight
 562                  ),
 563                  new ezcGraphCoordinate(
 564                      $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2,
 565                      $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight
 566                  ),
 567                  new ezcGraphCoordinate(
 568                      $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2,
 569                      $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2
 570                  ),
 571              );
 572  
 573              // Get average y coordinate for polygon to use for zBuffer
 574              $center = 0;
 575              foreach ( $polygonPoints as $point )
 576              {
 577                  $center += $point->y;
 578              }
 579              $center = (int) ( $center / count( $polygonPoints ) );
 580  
 581              $zBuffer[$center][] = array(
 582                  'method' => 'drawPolygon',
 583                  'paramenters' => array(
 584                      $polygonPoints,
 585                      $circleSector['color'],
 586                      true
 587                  ),
 588              );
 589  
 590              $zBuffer[$center][] = array(
 591                  'method' => 'drawPolygon',
 592                  'paramenters' => array(
 593                      $polygonPoints,
 594                      $darkenedColor,
 595                      false
 596                  ),
 597              );
 598          }
 599  
 600          if ( $this->options->pieChartShadowSize > 0 )
 601          {
 602              $shadows[] = array( 
 603                  'center' => $shadowCenter,
 604                  'start' => $shadowStartAngle, 
 605                  'end' => $shadowEndAngle,
 606                  'width' => $circleSector['width'],
 607                  'height' => $circleSector['height'],
 608              );
 609          }
 610  
 611          // Draw collected shadows
 612          foreach ( $shadows as $circleSector )
 613          {
 614              for ( $i = $this->options->pieChartShadowSize; $i > 0; --$i )
 615              {
 616                  $startAngle = $circleSector['start'];
 617                  $endAngle = $circleSector['end'];
 618  
 619                  $startAngle = $circleSector['start'] - ( $this->options->pieChartShadowSize - $i );
 620                  $endAngle = $circleSector['end'] + ( $this->options->pieChartShadowSize - $i );
 621  
 622                  if ( ( $endAngle - $startAngle ) >= 360 )
 623                  {
 624                      $this->driver->drawCircle(
 625                          new ezcGraphCoordinate(
 626                              $circleSector['center']->x,
 627                              $circleSector['center']->y + $this->options->pieChartHeight
 628                          ),
 629                          $circleSector['width'] + $i * 2,
 630                          $circleSector['height'] + $i * 2,
 631                          $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ),
 632                          true
 633                      );
 634                  }
 635                  else
 636                  {
 637                      $this->driver->drawCircleSector(
 638                          new ezcGraphCoordinate(
 639                              $circleSector['center']->x,
 640                              $circleSector['center']->y + $this->options->pieChartHeight
 641                          ),
 642                          $circleSector['width'] + $i * 2,
 643                          $circleSector['height'] + $i * 2,
 644                          $startAngle,
 645                          $endAngle,
 646                          $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ),
 647                          true
 648                      );
 649                  }
 650              }
 651          }
 652  
 653          ksort( $zBuffer );
 654          foreach ( $zBuffer as $sides )
 655          {
 656              foreach ( $sides as $side )
 657              {
 658                  call_user_func_array( array( $this->driver, $side['method'] ), $side['paramenters'] );
 659              }
 660          }
 661  
 662          // Draw circle sector for front
 663          foreach ( $this->circleSectors as $circleSector )
 664          {
 665              $this->addElementReference( $circleSector['context'],
 666                  $this->driver->drawCircleSector(
 667                      $circleSector['center'],
 668                      $circleSector['width'],
 669                      $circleSector['height'],
 670                      $circleSector['start'],
 671                      $circleSector['end'],
 672                      $circleSector['color'],
 673                      true
 674                  )
 675              );
 676  
 677              if ( $this->options->pieChartGleam !== false )
 678              {
 679                  $gradient = new ezcGraphLinearGradient(
 680                      $circleSector['center'],
 681                      new ezcGraphCoordinate(
 682                          $circleSector['center']->x - $circleSector['width'] / 2,
 683                          $circleSector['center']->y - $circleSector['height'] / 2
 684                      ),
 685                      $this->options->pieChartGleamColor->transparent( 1 ),
 686                      $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam )
 687                  );
 688  
 689                  $this->addElementReference( $circleSector['context'],
 690                      $this->driver->drawCircleSector(
 691                          $circleSector['center'],
 692                          $circleSector['width'] - $this->options->pieChartGleamBorder * 2,
 693                          $circleSector['height'] - $this->options->pieChartGleamBorder * 2 * $this->options->pieChartRotation,
 694                          $circleSector['start'],
 695                          $circleSector['end'],
 696                          $gradient,
 697                          true
 698                      )
 699                  );
 700              }
 701  
 702              $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder );
 703              $this->driver->drawCircleSector(
 704                  $circleSector['center'],
 705                  $circleSector['width'],
 706                  $circleSector['height'],
 707                  $circleSector['start'],
 708                  $circleSector['end'],
 709                  $darkenedColor,
 710                  false
 711              );
 712  
 713              if ( $this->options->pieChartGleam !== false )
 714              {
 715                  $radialGradient = new ezcGraphRadialGradient(
 716                      new ezcGraphCoordinate(
 717                          $circleSector['center']->x + $circleSector['width'] / 2 * cos( deg2rad( 135 ) ),
 718                          $circleSector['center']->y + $circleSector['height'] / 2 * sin( deg2rad( 135 ) )
 719                      ),
 720                      $circleSector['width'],
 721                      $circleSector['height'],
 722                      $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ),
 723                      $this->options->pieChartGleamColor->transparent( .8 )
 724                  );
 725  
 726                  $this->driver->drawCircularArc(
 727                      $circleSector['center'],
 728                      $circleSector['width'],
 729                      $circleSector['height'],
 730                      0,
 731                      $circleSector['start'],
 732                      $circleSector['end'],
 733                      $radialGradient,
 734                      false
 735                  );
 736              }
 737          }
 738      }
 739  
 740      /**
 741       * Draw collected front lines
 742       *
 743       * Draw all grid and axis lines, which should be redrawn in front of the 
 744       * data.
 745       * 
 746       * @return void
 747       */
 748      protected function finishFrontLines()
 749      {
 750          foreach ( $this->frontLines as $line )
 751          {
 752              $this->driver->drawLine(
 753                  $line[0],
 754                  $line[1],
 755                  $line[2],
 756                  $line[3]
 757              );
 758          }
 759      }
 760  
 761      /**
 762       * Draw the collected line symbols
 763       *
 764       * Symbols for the data lines are collected and delayed to ensure that 
 765       * they are not covered and hidden by other data lines.
 766       * 
 767       * @return void
 768       */
 769      protected function finishLineSymbols()
 770      {
 771          foreach ( $this->linePostSymbols as $symbol )
 772          {
 773              $this->addElementReference( $symbol['context'],
 774                  $this->drawSymbol(
 775                      $symbol['boundings'],
 776                      $symbol['color'],
 777                      $symbol['symbol']
 778                  )
 779              );
 780          }
 781      }
 782      
 783      /**
 784       * Draws a bar with a rectangular ground shape.
 785       * 
 786       * @param ezcGraphContext $context
 787       * @param ezcGraphColor $color
 788       * @param ezcGraphCoordinate $position
 789       * @param float $barWidth
 790       * @param float $offset
 791       * @param float $axisPosition
 792       * @param float $startDepth
 793       * @param float $midDepth
 794       * @param float $endDepth
 795       * @return void
 796       */
 797      protected function drawRectangularBar(
 798          ezcGraphContext $context,
 799          ezcGraphColor $color,
 800          ezcGraphCoordinate $position,
 801          $barWidth,
 802          $offset,
 803          $axisPosition,
 804          $startDepth,
 805          $midDepth,
 806          $endDepth )
 807      {
 808          $barPolygonArray = array(
 809              new ezcGraphCoordinate( 
 810                  $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset,
 811                  $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
 812              ),
 813              new ezcGraphCoordinate( 
 814                  $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset,
 815                  $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
 816              ),
 817              new ezcGraphCoordinate( 
 818                  $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth,
 819                  $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
 820              ),
 821              new ezcGraphCoordinate( 
 822                  $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth,
 823                  $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
 824              ),
 825          );
 826  
 827          // Draw right bar side
 828          $this->barPostProcessing[] = array(
 829              'index' => $barPolygonArray[2]->x + ( 1 - $position->y ),
 830              'method' => 'drawPolygon',
 831              'context' => $context,
 832              'parameters' => array(
 833                  array(
 834                      $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
 835                      $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
 836                      $this->get3dCoordinate( $barPolygonArray[3], $endDepth ),
 837                      $this->get3dCoordinate( $barPolygonArray[2], $endDepth ),
 838                  ),
 839                  $color->darken( $this->options->barDarkenSide ),
 840                  true
 841              ),
 842          );
 843  
 844          // Draw top side
 845          $this->barPostProcessing[] = array(
 846              'index' => $barPolygonArray[1]->x + ( 1 - $position->y ),
 847              'method' => 'drawPolygon',
 848              'context' => $context,
 849              'parameters' => array(
 850                  ( $barPolygonArray[1]->y < $barPolygonArray[3]->y
 851                  ?   array(
 852                          $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
 853                          $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
 854                          $this->get3dCoordinate( $barPolygonArray[2], $endDepth ),
 855                          $this->get3dCoordinate( $barPolygonArray[1], $endDepth ),
 856                      )
 857                  :   array(
 858                          $this->get3dCoordinate( $barPolygonArray[0], $startDepth ),
 859                          $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
 860                          $this->get3dCoordinate( $barPolygonArray[3], $endDepth ),
 861                          $this->get3dCoordinate( $barPolygonArray[0], $endDepth ),
 862                      )
 863                  ),
 864                  $color->darken( $this->options->barDarkenTop ),
 865                  true
 866              ),
 867          );
 868  
 869          // Draw top side gleam
 870          if ( $this->options->barChartGleam !== false )
 871          {
 872              $this->barPostProcessing[] = array(
 873                  'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ),
 874                  'method' => 'drawPolygon',
 875                  'context' => $context,
 876                  'parameters' => array(
 877                      ( $barPolygonArray[1]->y < $barPolygonArray[3]->y
 878                      ?   array(
 879                              $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
 880                              $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
 881                              $this->get3dCoordinate( $barPolygonArray[2], $endDepth ),
 882                              $this->get3dCoordinate( $barPolygonArray[1], $endDepth ),
 883                          )
 884                      :   array(
 885                              $this->get3dCoordinate( $barPolygonArray[0], $startDepth ),
 886                              $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
 887                              $this->get3dCoordinate( $barPolygonArray[3], $endDepth ),
 888                              $this->get3dCoordinate( $barPolygonArray[0], $endDepth ),
 889                          )
 890                      ),
 891                      new ezcGraphLinearGradient(
 892                          ( $barPolygonArray[1]->y < $barPolygonArray[3]->y
 893                          ? $this->get3dCoordinate( $barPolygonArray[2], $endDepth )
 894                          : $this->get3dCoordinate( $barPolygonArray[3], $endDepth )
 895                          ),
 896                          ( $barPolygonArray[1]->y < $barPolygonArray[3]->y
 897                          ? $this->get3dCoordinate( $barPolygonArray[1], $startDepth )
 898                          : $this->get3dCoordinate( $barPolygonArray[0], $startDepth )
 899                          ),
 900                          ezcGraphColor::fromHex( '#FFFFFFFF' ),
 901                          ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam )
 902                      ),
 903                      true
 904                  ),
 905              );
 906          }
 907  
 908          // Draw front side
 909          $this->barPostProcessing[] = array(
 910              'index' => $barPolygonArray[1]->x + ( 1 - $position->y ),
 911              'method' => 'drawPolygon',
 912              'context' => $context,
 913              'parameters' => array(
 914                  array(
 915                      $this->get3dCoordinate( $barPolygonArray[0], $startDepth ),
 916                      $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
 917                      $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
 918                      $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
 919                  ),
 920                  $color,
 921                  true
 922              ),
 923          );
 924  
 925          // Draw front side gleam
 926          if ( $this->options->barChartGleam !== false )
 927          {
 928              $this->barPostProcessing[] = array(
 929                  'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ),
 930                  'method' => 'drawPolygon',
 931                  'context' => $context,
 932                  'parameters' => array(
 933                      array(
 934                          $this->get3dCoordinate( $barPolygonArray[0], $startDepth ),
 935                          $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
 936                          $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
 937                          $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
 938                      ),
 939                      new ezcGraphLinearGradient(
 940                          $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
 941                          $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
 942                          ezcGraphColor::fromHex( '#FFFFFFFF' ),
 943                          ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam )
 944                      ),
 945                      true
 946                  ),
 947              );
 948          }
 949      }
 950  
 951      /**
 952       * Draws a bar with a diamond ground shape.
 953       * 
 954       * @param ezcGraphContext $context
 955       * @param ezcGraphColor $color
 956       * @param ezcGraphCoordinate $position
 957       * @param float $barWidth
 958       * @param float $offset
 959       * @param float $axisPosition
 960       * @param float $startDepth
 961       * @param float $midDepth
 962       * @param float $endDepth
 963       * @return void
 964       */
 965      protected function drawDiamondBar(
 966          ezcGraphContext $context,
 967          ezcGraphColor $color,
 968          ezcGraphCoordinate $position,
 969          $barWidth,
 970          $offset,
 971          $axisPosition,
 972          $startDepth,
 973          $midDepth,
 974          $endDepth )
 975      {
 976          $barCoordinateArray = array(
 977              // The bottom point of the diamond is moved to .7 instead 
 978              // of .5 because it looks more correct, even it is wrong...
 979              'x' => array(
 980                  $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset,
 981                  $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .7,
 982                  $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth,
 983                  $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .3,
 984              ),
 985              'y' => array(
 986                  $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ),
 987                  $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ),
 988              ),
 989          );
 990  
 991          // Left side
 992          $this->barPostProcessing[] = array(
 993              'index' => $barCoordinateArray['x'][0],
 994              'method' => 'drawPolygon',
 995              'context' => $context,
 996              'parameters' => array(
 997                  array(
 998                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][0] ), $midDepth ),
 999                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][1] ), $midDepth ),
1000                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ),
1001                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ),
1002                  ),
1003                  $color,
1004                  true
1005              ),
1006          );
1007  
1008          // Right side
1009          $this->barPostProcessing[] = array(
1010              'index' => $barCoordinateArray['x'][1],
1011              'method' => 'drawPolygon',
1012              'context' => $context,
1013              'parameters' => array(
1014                  array(
1015                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][0] ), $midDepth ),
1016                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][1] ), $midDepth ),
1017                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ),
1018                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ),
1019                  ),
1020                  $color->darken( $this->options->barDarkenSide ),
1021                  true
1022              ),
1023          );
1024  
1025          $topLocation = min(
1026              $barCoordinateArray['y'][0],
1027              $barCoordinateArray['y'][1]
1028          );
1029  
1030          // Top side
1031          $this->barPostProcessing[] = array(
1032              'index' => $barCoordinateArray['x'][0],
1033              'method' => 'drawPolygon',
1034              'context' => $context,
1035              'parameters' => array(
1036                  array(
1037                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ),
1038                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ),
1039                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ),
1040                      $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ),
1041                  ),
1042                  $color->darken( $this->options->barDarkenTop ),
1043                  true
1044              ),
1045          );
1046  
1047          // Top side gleam
1048          if ( $this->options->barChartGleam !== false )
1049          {
1050              $this->barPostProcessing[] = array(
1051                  'index' => $barCoordinateArray['x'][0] + 1,
1052                  'method' => 'drawPolygon',
1053                  'context' => $context,
1054                  'parameters' => array(
1055                      array(
1056                          $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ),
1057                          $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ),
1058                          $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ),
1059                          $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ),
1060                      ),
1061                      new ezcGraphLinearGradient(
1062                          $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ),
1063                          $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ),
1064                          ezcGraphColor::fromHex( '#FFFFFFFF' ),
1065                          ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam )
1066                      ),
1067                      true
1068                  ),
1069              );
1070          }
1071      }
1072  
1073      /**
1074       * Draws a bar with a circular ground shape.
1075       * 
1076       * @param ezcGraphContext $context
1077       * @param ezcGraphColor $color
1078       * @param ezcGraphCoordinate $position
1079       * @param float $barWidth
1080       * @param float $offset
1081       * @param float $axisPosition
1082       * @param float $startDepth
1083       * @param float $midDepth
1084       * @param float $endDepth
1085       * @param int $symbol
1086       * @return void
1087       */
1088      protected function drawCircularBar(
1089          ezcGraphContext $context,
1090          ezcGraphColor $color,
1091          ezcGraphCoordinate $position,
1092          $barWidth,
1093          $offset,
1094          $axisPosition,
1095          $startDepth,
1096          $midDepth,
1097          $endDepth,
1098          $symbol )
1099      {
1100          $barCenterTop = new ezcGraphCoordinate(
1101              $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2,
1102              $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
1103              
1104          );
1105          $barCenterBottom = new ezcGraphCoordinate(
1106              $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2,
1107              $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
1108          );
1109  
1110          if ( $barCenterTop->y > $barCenterBottom->y )
1111          {
1112              $tmp = $barCenterTop;
1113              $barCenterTop = $barCenterBottom;
1114              $barCenterBottom = $tmp;
1115          }
1116  
1117          $this->barPostProcessing[] = array(
1118              'index' => $barCenterBottom->x,
1119              'method' => 'drawCircularArc',
1120              'context' => $context,
1121              'parameters' => array(
1122                  $this->get3dCoordinate( $barCenterTop, $midDepth ),
1123                  $barWidth,
1124                  $barWidth / 2,
1125                  ( $barCenterBottom->y - $barCenterTop->y ) * $this->yDepthFactor,
1126                  0,
1127                  180,
1128                  $color
1129              ),
1130          );
1131  
1132          $this->barPostProcessing[] = array(
1133              'index' => $barCenterBottom->x + 1,
1134              'method' => 'drawCircle',
1135              'context' => $context,
1136              'parameters' => array(
1137                  $top = $this->get3dCoordinate( $barCenterTop, $midDepth ),
1138                  $barWidth,
1139                  $barWidth / 2,
1140                  ( $symbol === ezcGraph::CIRCLE
1141                      ? new ezcGraphLinearGradient(
1142                          new ezcGraphCoordinate(
1143                              $top->x - $barWidth / 2,
1144                              $top->y
1145                          ),
1146                          new ezcGraphCoordinate(
1147                              $top->x + $barWidth / 2,
1148                              $top->y
1149                          ),
1150                          $color->darken( $this->options->barDarkenTop ),
1151                          $color
1152                      )    
1153                      : $color
1154                  )
1155              ),
1156          );
1157      }
1158  
1159      /**
1160       * Draw bar
1161       *
1162       * Draws a bar as a data element in a line chart
1163       * 
1164       * @param ezcGraphBoundings $boundings Chart boundings
1165       * @param ezcGraphContext $context Context of call
1166       * @param ezcGraphColor $color Color of line
1167       * @param ezcGraphCoordinate $position Position of data point
1168       * @param float $stepSize Space which can be used for bars
1169       * @param int $dataNumber Number of dataset
1170       * @param int $dataCount Count of datasets in chart
1171       * @param int $symbol Symbol to draw for line
1172       * @param float $axisPosition Position of axis for drawing filled lines
1173       * @return void
1174       */
1175      public function drawBar(
1176          ezcGraphBoundings $boundings,
1177          ezcGraphContext $context,
1178          ezcGraphColor $color,
1179          ezcGraphCoordinate $position,
1180          $stepSize,
1181          $dataNumber = 1,
1182          $dataCount = 1,
1183          $symbol = ezcGraph::NO_SYMBOL,
1184          $axisPosition = 0. )
1185      {
1186          // Apply margin
1187          $margin = $stepSize * $this->options->barMargin;
1188          $padding = $stepSize * $this->options->barPadding;
1189          $barWidth = ( $stepSize - $margin ) / $dataCount - $padding;
1190          $offset = - $stepSize / 2 + $margin / 2 + ( $dataCount - $dataNumber - 1 ) * ( $padding + $barWidth ) + $padding / 2;
1191  
1192          if ( $barWidth < 0 )
1193          {
1194              $offset -= $barWidth = abs( $barWidth );
1195          }
1196  
1197          $startDepth = $this->options->barMargin;
1198          $midDepth = .5;
1199          $endDepth = 1 - $this->options->barMargin;
1200  
1201          switch ( $symbol )
1202          {
1203              case ezcGraph::NO_SYMBOL:
1204                  $this->drawRectangularBar(
1205                      $context,
1206                      $color,
1207                      $position,
1208                      $barWidth,
1209                      $offset,
1210                      $axisPosition,
1211                      $startDepth,
1212                      $midDepth,
1213                      $endDepth
1214                  );
1215                  break;
1216              case ezcGraph::DIAMOND:
1217                  $this->drawDiamondBar(
1218                      $context,
1219                      $color,
1220                      $position,
1221                      $barWidth,
1222                      $offset,
1223                      $axisPosition,
1224                      $startDepth,
1225                      $midDepth,
1226                      $endDepth
1227                  );
1228                  break;
1229              case ezcGraph::BULLET:
1230              case ezcGraph::CIRCLE:
1231                  $this->drawCircularBar(
1232                      $context,
1233                      $color,
1234                      $position,
1235                      $barWidth,
1236                      $offset,
1237                      $axisPosition,
1238                      $startDepth,
1239                      $midDepth,
1240                      $endDepth,
1241                      $symbol
1242                  );
1243                  break;
1244          }
1245      }
1246   
1247      /**
1248       * Draw stacked bar
1249       *
1250       * Draws a stacked bar part as a data element in a line chart
1251       * 
1252       * @param ezcGraphBoundings $boundings Chart boundings
1253       * @param ezcGraphContext $context Context of call
1254       * @param ezcGraphColor $color Color of line
1255       * @param ezcGraphCoordinate $start
1256       * @param ezcGraphCoordinate $position
1257       * @param float $stepSize Space which can be used for bars
1258       * @param int $symbol Symbol to draw for line
1259       * @param float $axisPosition Position of axis for drawing filled lines
1260       * @return void
1261       */
1262      public function drawStackedBar(
1263          ezcGraphBoundings $boundings,
1264          ezcGraphContext $context,
1265          ezcGraphColor $color,
1266          ezcGraphCoordinate $start,
1267          ezcGraphCoordinate $position,
1268          $stepSize,
1269          $symbol = ezcGraph::NO_SYMBOL,
1270          $axisPosition = 0. )
1271      {
1272          // Apply margin
1273          $margin = $stepSize * $this->options->barMargin;
1274          $barWidth = $stepSize - $margin;
1275          $offset = - $stepSize / 2 + $margin / 2;
1276  
1277          if ( $barWidth < 0 )
1278          {
1279              $offset -= $barWidth = abs( $barWidth );
1280          }
1281  
1282          $startDepth = $this->options->barMargin;
1283          $midDepth = .5;
1284          $endDepth = 1 - $this->options->barMargin;
1285  
1286          switch ( $symbol )
1287          {
1288              case ezcGraph::NO_SYMBOL:
1289              case ezcGraph::DIAMOND:
1290              case ezcGraph::BULLET:
1291              case ezcGraph::CIRCLE:
1292                  $this->drawRectangularBar(
1293                      $context,
1294                      $color,
1295                      $position,
1296                      $barWidth,
1297                      $offset,
1298                      $start->y,
1299                      $startDepth,
1300                      $midDepth,
1301                      $endDepth
1302                  );
1303                  break;
1304          }
1305      }
1306  
1307      /**
1308       * Draw all collected bar elements
1309       *
1310       * Draw all collected bar elements after sorting them depending of their
1311       * position to simulate simple z buffering.
1312       * 
1313       * @access protected
1314       * @return void
1315       */
1316      protected function finishBars()
1317      {
1318          if ( !count( $this->barPostProcessing ) )
1319          {
1320              return true;
1321          }
1322  
1323          $zIndexArray = array();
1324          foreach ( $this->barPostProcessing as $key => $barPolygon )
1325          {
1326              $zIndexArray[$key] = $barPolygon['index'];
1327          }
1328  
1329          array_multisort(
1330              $zIndexArray, SORT_ASC, SORT_NUMERIC,
1331              $this->barPostProcessing
1332          );
1333  
1334          foreach ( $this->barPostProcessing as $bar )
1335          {
1336              $this->addElementReference( $bar['context'],
1337                  call_user_func_array(
1338                      array( $this->driver, $bar['method'] ),
1339                      $bar['parameters']
1340                  )
1341              );
1342          }
1343      }
1344  
1345      /**
1346       * Draw data line
1347       *
1348       * Draws a line as a data element in a line chart
1349       * 
1350       * @param ezcGraphBoundings $boundings Chart boundings
1351       * @param ezcGraphContext $context Context of call
1352       * @param ezcGraphColor $color Color of line
1353       * @param ezcGraphCoordinate $start Starting point
1354       * @param ezcGraphCoordinate $end Ending point
1355       * @param int $dataNumber Number of dataset
1356       * @param int $dataCount Count of datasets in chart
1357       * @param int $symbol Symbol to draw for line
1358       * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor
1359       * @param ezcGraphColor $fillColor Color to fill line with
1360       * @param float $axisPosition Position of axis for drawing filled lines
1361       * @param float $thickness Line thickness
1362       * @return void
1363       */
1364      public function drawDataLine(
1365          ezcGraphBoundings $boundings,
1366          ezcGraphContext $context,
1367          ezcGraphColor $color,
1368          ezcGraphCoordinate $start,
1369          ezcGraphCoordinate $end,
1370          $dataNumber = 0,
1371          $dataCount = 1,
1372          $symbol = ezcGraph::NO_SYMBOL,
1373          ezcGraphColor $symbolColor = null,
1374          ezcGraphColor $fillColor = null,
1375          $axisPosition = 0.,
1376          $thickness = 1. )
1377      {
1378          // Calculate line width based on options
1379          if ( $this->options->seperateLines )
1380          {
1381              $startDepth = ( 1 / $dataCount ) * $dataNumber;
1382              $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 );
1383          }
1384          else
1385          {
1386              $startDepth = false;
1387              $endDepth = true;
1388          }
1389  
1390          // Determine Coordinates depending on boundings and data point position
1391          $startCoord = new ezcGraphCoordinate( 
1392              $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
1393              $this->dataBoundings->y0 + $this->yAxisSpace + $start->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
1394          );
1395          $endCoord = new ezcGraphCoordinate( 
1396              $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
1397              $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
1398          );
1399  
1400          // 3D-fy coordinates
1401          $linePolygonPoints = array(
1402              $this->get3dCoordinate( $startCoord, $startDepth ),
1403              $this->get3dCoordinate( $endCoord, $startDepth ),
1404              $this->get3dCoordinate( $endCoord, $endDepth ),
1405              $this->get3dCoordinate( $startCoord, $endDepth ),
1406          );
1407  
1408          $startAxisCoord = new ezcGraphCoordinate( 
1409              $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
1410              $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
1411          );
1412          $endAxisCoord = new ezcGraphCoordinate( 
1413              $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
1414              $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
1415          );
1416  
1417          // 3D-fy coordinates
1418          $axisPolygonPoints = array(
1419              $this->get3dCoordinate( $startAxisCoord, $startDepth ),
1420              $this->get3dCoordinate( $endAxisCoord, $startDepth ),
1421              $this->get3dCoordinate( $endAxisCoord, $endDepth ),
1422              $this->get3dCoordinate( $startAxisCoord, $endDepth ),
1423          );
1424  
1425          // Perhaps fill up line
1426          if ( $fillColor !== null &&
1427               $start->x != $end->x )
1428          {
1429              $startValue = $axisPosition - $start->y;
1430              $endValue = $axisPosition - $end->y;
1431  
1432              if ( ( $startValue == 0 ) ||
1433                   ( $endValue == 0 ) ||
1434                   ( $startValue / abs( $startValue ) == $endValue / abs( $endValue ) ) )
1435              {
1436                  // Values have the same sign or are on the axis
1437                  $this->driver->drawPolygon(
1438                      array(
1439                          $linePolygonPoints[0],
1440                          $linePolygonPoints[1],
1441                          $this->get3dCoordinate( $endAxisCoord, $startDepth ),
1442                          $this->get3dCoordinate( $startAxisCoord, $startDepth ),
1443                      ),
1444                      $fillColor,
1445                      true
1446                  );
1447              }
1448              else
1449              {
1450                  // values are on differente sides of the axis - split the filled polygon
1451                  $startDiff = abs( $axisPosition - $start->y );
1452                  $endDiff = abs( $axisPosition - $end->y );
1453  
1454                  $cuttingPosition = $startDiff / ( $endDiff + $startDiff );
1455                  $cuttingPoint = new ezcGraphCoordinate(
1456                      $startCoord->x + ( $endCoord->x - $startCoord->x ) * $cuttingPosition,
1457                      $startAxisCoord->y
1458                  );
1459  
1460                  $this->driver->drawPolygon(
1461                      array(
1462                          $this->get3dCoordinate( $startAxisCoord, $startDepth ),
1463                          $linePolygonPoints[0],
1464                          $this->get3dCoordinate( $cuttingPoint, $startDepth ),
1465                      ),
1466                      $fillColor,
1467                      true
1468                  );
1469  
1470                  $this->driver->drawPolygon(
1471                      array(
1472                          $this->get3dCoordinate( $endAxisCoord, $startDepth ),
1473                          $linePolygonPoints[1],
1474                          $this->get3dCoordinate( $cuttingPoint, $startDepth ),
1475                      ),
1476                      $fillColor,
1477                      true
1478                  );
1479              }
1480  
1481              // Draw closing foo
1482              $this->driver->drawPolygon(
1483                  array(
1484                      $linePolygonPoints[2],
1485                      $linePolygonPoints[1],
1486                      $this->get3dCoordinate( $endAxisCoord, $startDepth ),
1487                      $this->get3dCoordinate( $endAxisCoord, $endDepth ),
1488                  ),
1489                  $fillColor,
1490                  true
1491              );
1492          }
1493  
1494  
1495          // Draw line
1496          $this->driver->drawPolygon(
1497              $linePolygonPoints,
1498              $color,
1499              true,
1500              $thickness
1501          );
1502  
1503          // Draw polygon border
1504          if ( $this->options->dataBorder > 0 )
1505          {
1506              $this->driver->drawPolygon(
1507                  $linePolygonPoints,
1508                  $color->darken( $this->options->dataBorder ),
1509                  false,
1510                  $thickness
1511              );
1512          }
1513  
1514          // Draw line symbol
1515          if ( $this->options->showSymbol && 
1516               ( $symbol !== ezcGraph::NO_SYMBOL ) )
1517          {
1518              if ( $symbolColor === null )
1519              {
1520                  $symbolColor = $color;
1521              }
1522  
1523              $this->linePostSymbols[] = array(
1524                  'boundings' => new ezcGraphBoundings(
1525                      $linePolygonPoints[2]->x - $this->options->symbolSize / 2,
1526                      $linePolygonPoints[2]->y - $this->options->symbolSize / 2,
1527                      $linePolygonPoints[2]->x + $this->options->symbolSize / 2,
1528                      $linePolygonPoints[2]->y + $this->options->symbolSize / 2
1529                  ),
1530                  'color' => $symbolColor,
1531                  'context' => $context,
1532                  'symbol' => $symbol,
1533              );
1534          }
1535      }
1536      
1537      /**
1538       * Draws a highlight textbox for a datapoint.
1539       *
1540       * A highlight textbox for line and bar charts means a box with the current 
1541       * value in the graph.
1542       * 
1543       * @param ezcGraphBoundings $boundings Chart boundings
1544       * @param ezcGraphContext $context Context of call
1545       * @param ezcGraphCoordinate $end Ending point
1546       * @param float $axisPosition Position of axis for drawing filled lines
1547       * @param int $dataNumber Number of dataset
1548       * @param int $dataCount Count of datasets in chart
1549       * @param ezcGraphFontOptions $font Font used for highlight string
1550       * @param string $text Acutual value
1551       * @param int $size Size of highlight text
1552       * @param ezcGraphColor $markLines
1553       * @param int $xOffset
1554       * @param int $yOffset
1555       * @param float $stepSize
1556       * @param int $type
1557       * @return void
1558       */
1559      public function drawDataHighlightText(
1560          ezcGraphBoundings $boundings,
1561          ezcGraphContext $context,
1562          ezcGraphCoordinate $end,
1563          $axisPosition = 0.,
1564          $dataNumber = 1,
1565          $dataCount = 1,
1566          ezcGraphFontOptions $font,
1567          $text,
1568          $size,
1569          ezcGraphColor $markLines = null,
1570          $xOffset = 0,
1571          $yOffset = 0,
1572          $stepSize = 0.,
1573          $type = ezcGraph::LINE )
1574      {
1575          $this->driver->options->font = $font;
1576          $width = $this->dataBoundings->width / $dataCount;
1577  
1578          // Calculate line width based on options
1579          if ( $this->options->seperateLines )
1580          {
1581              $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 );
1582          }
1583          else
1584          {
1585              $endDepth = true;
1586          }
1587          
1588          $dataPoint = new ezcGraphCoordinate( 
1589              $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
1590              $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
1591          );
1592  
1593          if ( $end->y < $axisPosition )
1594          {
1595              $this->driver->drawTextBox(
1596                  $text,
1597                  $this->get3dCoordinate( new ezcGraphCoordinate(
1598                      $dataPoint->x - $width / 2,
1599                      $dataPoint->y - $size - $font->padding - $this->options->symbolSize
1600                  ), $endDepth ),
1601                  $width * $this->xDepthFactor,
1602                  $size,
1603                  ezcGraph::CENTER | ezcGraph::BOTTOM
1604              );
1605          }
1606          else
1607          {
1608              $this->driver->drawTextBox(
1609                  $text,
1610                  $this->get3dCoordinate( new ezcGraphCoordinate(
1611                      $dataPoint->x - $width / 2,
1612                      $dataPoint->y + $font->padding + $this->options->symbolSize
1613                  ), $endDepth ),
1614                  $width * $this->xDepthFactor,
1615                  $size,
1616                  ezcGraph::CENTER | ezcGraph::TOP
1617              );
1618          }
1619      }
1620      
1621      /**
1622       * Draw legend
1623       *
1624       * Will draw a legend in the bounding box
1625       * 
1626       * @param ezcGraphBoundings $boundings Bounding of legend
1627       * @param ezcGraphChartElementLegend $legend Legend to draw;
1628       * @param int $type Type of legend: Protrait or landscape
1629       * @return void
1630       */
1631      public function drawLegend(
1632          ezcGraphBoundings $boundings,
1633          ezcGraphChartElementLegend $legend,
1634          $type = ezcGraph::VERTICAL )
1635      {
1636          $labels = $legend->labels;
1637          
1638          // Calculate boundings of each label
1639          if ( $type & ezcGraph::VERTICAL )
1640          {
1641              $labelWidth = $boundings->x1 - $boundings->x0;
1642              $labelHeight = min( 
1643                  ( $boundings->y1 - $boundings->y0 ) / count( $labels ) - $legend->spacing, 
1644                  $legend->symbolSize + 2 * $legend->padding
1645              );
1646          }
1647          else
1648          {
1649              $labelWidth = ( $boundings->x1 - $boundings->x0 ) / count( $labels ) - $legend->spacing;
1650              $labelHeight = min(
1651                  $boundings->height,
1652                  $legend->symbolSize + 2 * $legend->padding
1653              );
1654          }
1655  
1656          $symbolSize = $labelHeight - 2 * $legend->padding;
1657  
1658          // Draw all labels
1659          $labelPosition = new ezcGraphCoordinate( $boundings->x0, $boundings->y0 );
1660          foreach ( $labels as $label )
1661          {
1662              $this->elements['legend_url'][$label['label']] = $label['url'];
1663  
1664              $this->elements['legend'][$label['label']]['symbol'] = $this->drawSymbol(
1665                  new ezcGraphBoundings(
1666                      $labelPosition->x + $legend->padding,
1667                      $labelPosition->y + $legend->padding,
1668                      $labelPosition->x + $legend->padding + $symbolSize,
1669                      $labelPosition->y + $legend->padding + $symbolSize
1670                  ),
1671                  $label['color'],
1672                  $label['symbol']
1673              );
1674  
1675              $this->elements['legend'][$label['label']]['text'] = $this->driver->drawTextBox(
1676                  $label['label'],
1677                  new ezcGraphCoordinate(
1678                      $labelPosition->x + 2 * $legend->padding + $symbolSize,
1679                      $labelPosition->y + $legend->padding
1680                  ),
1681                  $labelWidth - $symbolSize - 3 * $legend->padding,
1682                  $labelHeight - 2 * $legend->padding,
1683                  ezcGraph::LEFT | ezcGraph::MIDDLE
1684              );
1685  
1686              $labelPosition->x += ( $type === ezcGraph::VERTICAL ? 0 : $labelWidth + $legend->spacing );
1687              $labelPosition->y += ( $type === ezcGraph::VERTICAL ? $labelHeight + $legend->spacing : 0 );
1688          }
1689      }
1690      
1691      /**
1692       * Draw box
1693       *
1694       * Box are wrapping each major chart element and draw border, background
1695       * and title to each chart element.
1696       *
1697       * Optionally a padding and margin for each box can be defined.
1698       * 
1699       * @param ezcGraphBoundings $boundings Boundings of the box
1700       * @param ezcGraphColor $background Background color
1701       * @param ezcGraphColor $borderColor Border color
1702       * @param int $borderWidth Border width
1703       * @param int $margin Margin
1704       * @param int $padding Padding
1705       * @param mixed $title Title of the box
1706       * @param int $titleSize Size of title in the box
1707       * @return ezcGraphBoundings Remaining inner boundings
1708       */
1709      public function drawBox(
1710          ezcGraphBoundings $boundings,
1711          ezcGraphColor $background = null,
1712          ezcGraphColor $borderColor = null,
1713          $borderWidth = 0,
1714          $margin = 0,
1715          $padding = 0,
1716          $title = false,
1717          $titleSize = 16 )
1718      {
1719          // Apply margin
1720          $boundings->x0 += $margin;
1721          $boundings->y0 += $margin;
1722          $boundings->x1 -= $margin;
1723          $boundings->y1 -= $margin;
1724          
1725          if ( $background instanceof ezcGraphColor )
1726          {
1727              // Draw box background
1728              $this->driver->drawPolygon(
1729                  array(
1730                      new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
1731                      new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
1732                      new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
1733                      new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
1734                  ),
1735                  $background,
1736                  true
1737              );
1738          }
1739  
1740          if ( ( $borderColor instanceof ezcGraphColor ) &&
1741               ( $borderWidth > 0 ) )
1742          {
1743              // Draw border
1744              $this->driver->drawPolygon(
1745                  array(
1746                      new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
1747                      new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
1748                      new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
1749                      new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
1750                  ),
1751                  $borderColor,
1752                  false,
1753                  $borderWidth
1754              );
1755  
1756              // Reduce local boundings by borderWidth
1757              $boundings->x0 += $borderWidth;
1758              $boundings->y0 += $borderWidth;
1759              $boundings->x1 -= $borderWidth;
1760              $boundings->y1 -= $borderWidth;
1761          }
1762  
1763          // Apply padding
1764          $boundings->x0 += $padding;
1765          $boundings->y0 += $padding;
1766          $boundings->x1 -= $padding;
1767          $boundings->y1 -= $padding;
1768  
1769          // Add box title
1770          if ( $title !== false )
1771          {
1772              switch ( $this->options->titlePosition )
1773              {
1774                  case ezcGraph::TOP:
1775                      $this->driver->drawTextBox(
1776                          $title,
1777                          new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
1778                          $boundings->x1 - $boundings->x0,
1779                          $titleSize,
1780                          $this->options->titleAlignement
1781                      );
1782  
1783                      $boundings->y0 += $titleSize + $padding;
1784                      $boundings->y1 -= $titleSize + $padding;
1785                      break;
1786                  case ezcGraph::BOTTOM:
1787                      $this->driver->drawTextBox(
1788                          $title,
1789                          new ezcGraphCoordinate( $boundings->x0, $boundings->y1 - $titleSize ),
1790                          $boundings->x1 - $boundings->x0,
1791                          $titleSize,
1792                          $this->options->titleAlignement
1793                      );
1794  
1795                      $boundings->y1 -= $titleSize + $padding;
1796                      break;
1797              }
1798          }
1799  
1800          return $boundings;
1801      }
1802      
1803      /**
1804       * Draw text
1805       *
1806       * Draws the provided text in the boundings
1807       * 
1808       * @param ezcGraphBoundings $boundings Boundings of text
1809       * @param string $text Text
1810       * @param int $align Alignement of text
1811       * @param ezcGraphRotation $rotation
1812       * @return void
1813       */
1814      public function drawText(
1815          ezcGraphBoundings $boundings,
1816          $text,
1817          $align = ezcGraph::LEFT,
1818          ezcGraphRotation $rotation = null )
1819      {
1820          if ( $this->depth === false )
1821          {
1822              // We are not 3d for now, wg. rendering normal text boxes like the
1823              // title
1824              $topleft = new ezcGraphCoordinate( 
1825                  $boundings->x0, 
1826                  $boundings->y0
1827              );
1828              $bottomright = new ezcGraphCoordinate( 
1829                  $boundings->x1, 
1830                  $boundings->y1
1831              );
1832          }
1833          else
1834          {
1835              // The 3d part started
1836              $topleft = $this->get3dCoordinate( 
1837                  new ezcGraphCoordinate( 
1838                      $boundings->x0, 
1839                      $boundings->y0
1840                  ), false 
1841              );
1842              $bottomright = $this->get3dCoordinate( 
1843                  new ezcGraphCoordinate( 
1844                      $boundings->x1, 
1845                      $boundings->y1
1846                  ), false 
1847              );
1848  
1849              // Also modify rotation accordingly
1850              if ( $rotation !== null )
1851              {
1852                  $rotation = new ezcGraphRotation(
1853                      $rotation->getRotation(),
1854                      $this->get3dCoordinate( $rotation->getCenter(), false )
1855                  );
1856              }
1857          }
1858  
1859          $this->driver->drawTextBox(
1860              $text,
1861              $topleft,
1862              $bottomright->x - $topleft->x,
1863              $bottomright->y - $topleft->y,
1864              $align,
1865              $rotation
1866          );
1867      }
1868  
1869      /**
1870       * Draw grid line
1871       *
1872       * Draw line for the grid in the chart background
1873       * 
1874       * 
1875       * @param ezcGraphCoordinate $start Start point
1876       * @param ezcGraphCoordinate $end End point
1877       * @param ezcGraphColor $color Color of the grid line
1878       * @return void
1879       */
1880      public function drawGridLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
1881      {
1882          $gridPolygonCoordinates = array(
1883              $this->get3dCoordinate( $start, false ),
1884              $this->get3dCoordinate( $end, false ),
1885              $this->get3dCoordinate( $end, true ),
1886              $this->get3dCoordinate( $start, true ),
1887          );
1888  
1889          // Draw grid polygon
1890          if ( $this->options->fillGrid === 0 )
1891          {
1892              $this->driver->drawLine(
1893                  $gridPolygonCoordinates[2],
1894                  $gridPolygonCoordinates[3],
1895                  $color
1896              );
1897          }
1898          else
1899          {
1900              if ( $this->options->fillGrid === 1 )
1901              {
1902                  $this->driver->drawPolygon(
1903                      $gridPolygonCoordinates,
1904                      $color,
1905                      true
1906                  );
1907              }
1908              else
1909              {
1910                  $this->driver->drawPolygon(
1911                      $gridPolygonCoordinates,
1912                      $color->transparent( $this->options->fillGrid ),
1913                      true
1914                  );
1915              }
1916              
1917              // Draw grid lines - scedule some for later to be drawn in front of 
1918              // the data
1919              $this->frontLines[] = array(
1920                  $gridPolygonCoordinates[0],
1921                  $gridPolygonCoordinates[1],
1922                  $color,
1923                  1
1924              );
1925          
1926              $this->frontLines[] = array(
1927                  $gridPolygonCoordinates[1],
1928                  $gridPolygonCoordinates[2],
1929                  $color,
1930                  1
1931              );
1932  
1933              $this->driver->drawLine(
1934                  $gridPolygonCoordinates[2],
1935                  $gridPolygonCoordinates[3],
1936                  $color,
1937                  1
1938              );
1939  
1940              $this->frontLines[] = array(
1941                  $gridPolygonCoordinates[3],
1942                  $gridPolygonCoordinates[0],
1943                  $color,
1944                  1
1945              );
1946          }
1947      }
1948  
1949      /**
1950       * Draw step line
1951       *
1952       * Draw a step (marker for label position) on a axis.
1953       * 
1954       * @param ezcGraphCoordinate $start Start point
1955       * @param ezcGraphCoordinate $end End point
1956       * @param ezcGraphColor $color Color of the grid line
1957       * @return void
1958       */
1959      public function drawStepLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
1960      {
1961          $stepPolygonCoordinates = array(
1962              $this->get3dCoordinate( $start, true ),
1963              $this->get3dCoordinate( $end, true ),
1964              $this->get3dCoordinate( $end, false ),
1965              $this->get3dCoordinate( $start, false ),
1966          );
1967  
1968          // Draw step polygon
1969          if ( ( $this->options->fillAxis > 0 ) &&
1970               ( $this->options->fillAxis < 1 ) )
1971          {
1972              $this->driver->drawPolygon(
1973                  $stepPolygonCoordinates,
1974                  $color->transparent( $this->options->fillAxis ),
1975                  true
1976              );
1977  
1978              $this->driver->drawPolygon(
1979                  $stepPolygonCoordinates,
1980                  $color,
1981                  false
1982              );
1983          }
1984          else
1985          {
1986              $this->driver->drawPolygon(
1987                  $stepPolygonCoordinates,
1988                  $color,
1989                  ! (bool) $this->options->fillAxis
1990              );
1991          }
1992      }
1993      
1994      /**
1995       * Draw axis
1996       *
1997       * Draws an axis form the provided start point to the end point. A specific 
1998       * angle of the axis is not required.
1999       *
2000       * For the labeleing of the axis a sorted array with major steps and an 
2001       * array with minor steps is expected, which are build like this:
2002       *  array(
2003       *      array(
2004       *          'position' => (float),
2005       *          'label' => (string),
2006       *      )
2007       *  )
2008       * where the label is optional.
2009       *
2010       * The label renderer class defines how the labels are rendered. For more
2011       * documentation on this topic have a look at the basic label renderer 
2012       * class.
2013       *
2014       * Additionally it can be specified if a major and minor grid are rendered 
2015       * by defining a color for them. The axis label is used to add a caption 
2016       * for the axis.
2017       * 
2018       * @param ezcGraphBoundings $boundings Boundings of axis
2019       * @param ezcGraphCoordinate $start Start point of axis
2020       * @param ezcGraphCoordinate $end Endpoint of axis
2021       * @param ezcGraphChartElementAxis $axis Axis to render
2022       * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer
2023       * @return void
2024       */
2025      public function drawAxis(
2026          ezcGraphBoundings $boundings,
2027          ezcGraphCoordinate $start,
2028          ezcGraphCoordinate $end,
2029          ezcGraphChartElementAxis $axis,
2030          ezcGraphAxisLabelRenderer $labelClass = null )
2031      {
2032          // Calculate used space for three dimensional effects
2033          if ( $this->depth === false )
2034          {
2035              $this->depth = min(
2036                  ( $boundings->x1 - $boundings->x0 ) * $this->options->depth,
2037                  ( $boundings->y1 - $boundings->y0 ) * $this->options->depth
2038              );
2039  
2040              $this->xDepthFactor = 1 - $this->depth / ( $boundings->x1 - $boundings->x0 );
2041              $this->yDepthFactor = 1 - $this->depth / ( $boundings->y1 - $boundings->y0 );
2042  
2043              $this->dataBoundings = clone $boundings;
2044          }
2045  
2046          // Clone boundings to not be affected by internal mofifications
2047          $boundings = clone $boundings;
2048  
2049          switch ( $axis->position )
2050          {
2051              case ezcGraph::TOP:
2052              case ezcGraph::BOTTOM:
2053                  $this->xAxisSpace = ( $this->dataBoundings->x1 - $this->dataBoundings->x0 ) * $axis->axisSpace;
2054                  break;
2055              case ezcGraph::LEFT:
2056              case ezcGraph::RIGHT:
2057                  $this->yAxisSpace = ( $this->dataBoundings->y1 - $this->dataBoundings->y0 ) * $axis->axisSpace;
2058                  break;
2059          }
2060  
2061          // Determine normalized direction
2062          $direction = new ezcGraphVector(
2063              $start->x - $end->x,
2064              $start->y - $end->y
2065          );
2066          $direction->unify();
2067  
2068          $start->x += $boundings->x0;
2069          $start->y += $boundings->y0;
2070          $end->x += $boundings->x0;
2071          $end->y += $boundings->y0;
2072  
2073          // Shorten drawn axis, if requested.
2074          if ( ( $this->options->shortAxis === true ) &&
2075               ( ( $axis->position === ezcGraph::TOP ) ||
2076                 ( $axis->position === ezcGraph::BOTTOM ) ) )
2077          {
2078              $axisStart = clone $start;
2079              $axisEnd   = clone $end;
2080  
2081              $axisStart->y += $boundings->height * $axis->axisSpace *
2082                  ( $axis->position === ezcGraph::TOP ? 1 : -1 );
2083              $axisEnd->y   -= $boundings->height * $axis->axisSpace *
2084                  ( $axis->position === ezcGraph::TOP ? 1 : -1 );
2085          }
2086          elseif ( ( $this->options->shortAxis === true ) &&
2087               ( ( $axis->position === ezcGraph::LEFT ) ||
2088                 ( $axis->position === ezcGraph::RIGHT ) ) )
2089          {
2090              $axisStart = clone $start;
2091              $axisEnd   = clone $end;
2092  
2093              $axisStart->x += $boundings->width * $axis->axisSpace *
2094                  ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
2095              $axisEnd->x   -= $boundings->width * $axis->axisSpace *
2096                  ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
2097          }
2098          else
2099          {
2100              $axisStart = $start;
2101              $axisEnd   = $end;
2102          }
2103  
2104          $axisPolygonCoordinates = array(
2105              $this->get3dCoordinate( $axisStart, true ),
2106              $this->get3dCoordinate( $axisEnd, true ),
2107              $this->get3dCoordinate( $axisEnd, false ),
2108              $this->get3dCoordinate( $axisStart, false ),
2109          );
2110  
2111          // Draw axis
2112          if ( ( $this->options->fillAxis > 0 ) &&
2113               ( $this->options->fillAxis < 1 ) )
2114          {
2115              $this->driver->drawPolygon(
2116                  $axisPolygonCoordinates,
2117                  $axis->border->transparent( $this->options->fillAxis ),
2118                  true
2119              );
2120          }
2121          else
2122          {
2123              $this->driver->drawPolygon(
2124                  $axisPolygonCoordinates,
2125                  $axis->border,
2126                  ! (bool) $this->options->fillAxis
2127              );
2128          }
2129  
2130          // Draw axis lines - scedule some for later to be drawn in front of 
2131          // the data
2132          $this->driver->drawLine(
2133              $axisPolygonCoordinates[0],
2134              $axisPolygonCoordinates[1],
2135              $axis->border,
2136              1
2137          );
2138      
2139          $this->frontLines[] = array(
2140              $axisPolygonCoordinates[1],
2141              $axisPolygonCoordinates[2],
2142              $axis->border,
2143              1
2144          );
2145  
2146          $this->frontLines[] = array(
2147              $axisPolygonCoordinates[2],
2148              $axisPolygonCoordinates[3],
2149              $axis->border,
2150              1
2151          );
2152  
2153          $this->frontLines[] = array(
2154              $axisPolygonCoordinates[3],
2155              $axisPolygonCoordinates[0],
2156              $axis->border,
2157              1
2158          );
2159  
2160          // Draw small arrowhead
2161          $this->drawAxisArrowHead(
2162              $axisPolygonCoordinates[1],
2163              $direction,
2164              max(
2165                  $axis->minArrowHeadSize,
2166                  min(
2167                      $axis->maxArrowHeadSize,
2168                      abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) )
2169                  )
2170              ),
2171              $axis->border
2172          );
2173  
2174          // Draw axis label
2175          if ( $axis->label !== false )
2176          {
2177              $width = $this->dataBoundings->x1 - $this->dataBoundings->x0;
2178              switch ( $axis->position )
2179              {
2180                  case ezcGraph::TOP:
2181                      $this->driver->drawTextBox(
2182                          $axis->label,
2183                          new ezcGraphCoordinate(
2184                              $axisPolygonCoordinates[2]->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ),
2185                              $axisPolygonCoordinates[2]->y - $axis->labelMargin - $axis->labelSize
2186                          ),
2187                          $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
2188                          $axis->labelSize,
2189                          ezcGraph::TOP | ezcGraph::RIGHT
2190                      );
2191                      break;
2192                  case ezcGraph::BOTTOM:
2193                      $this->driver->drawTextBox(
2194                          $axis->label,
2195                          new ezcGraphCoordinate(
2196                              $axisPolygonCoordinates[1]->x + $axis->labelMargin,
2197                              $axisPolygonCoordinates[1]->y + $axis->labelMargin
2198                          ),
2199                          $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
2200                          $axis->labelSize,
2201                          ezcGraph::TOP | ezcGraph::LEFT
2202                      );
2203                      break;
2204                  case ezcGraph::LEFT:
2205                      $this->driver->drawTextBox(
2206                          $axis->label,
2207                          new ezcGraphCoordinate(
2208                              $axisPolygonCoordinates[1]->x - $width,
2209                              $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin
2210                          ),
2211                          $width - $axis->labelMargin,
2212                          $axis->labelSize,
2213                          ezcGraph::BOTTOM | ezcGraph::RIGHT
2214                      );
2215                      break;
2216                  case ezcGraph::RIGHT:
2217                      $this->driver->drawTextBox(
2218                          $axis->label,
2219                          new ezcGraphCoordinate(
2220                              $axisPolygonCoordinates[1]->x,
2221                              $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin
2222                          ),
2223                          $width - $axis->labelMargin,
2224                          $axis->labelSize,
2225                          ezcGraph::BOTTOM | ezcGraph::LEFT
2226                      );
2227                      break;
2228              }
2229          }
2230  
2231          // Collect axis labels and draw, when all axisSpaces are collected
2232          $this->axisLabels[] = array(
2233              'object' => $labelClass,
2234              'boundings' => $boundings,
2235              'start' => clone $start,
2236              'end' => clone $end,
2237              'axis' => $axis,
2238          );
2239  
2240          if ( $this->xAxisSpace && $this->yAxisSpace )
2241          {
2242              foreach ( $this->axisLabels as $axisLabel )
2243              {
2244                  // If font should not be synchronized, use font configuration from
2245                  // each axis
2246                  if ( $this->options->syncAxisFonts === false )
2247                  {
2248                      $this->driver->options->font = $axisLabel['axis']->font;
2249                  }
2250  
2251                  switch ( $axisLabel['axis']->position )
2252                  {
2253                      case ezcGraph::RIGHT:
2254                      case ezcGraph::LEFT:
2255                          $axisLabel['start']->x += $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 );
2256                          $axisLabel['end']->x -= $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 );
2257                          break;
2258                      case ezcGraph::TOP:
2259                      case ezcGraph::BOTTOM:
2260                          $axisLabel['start']->y += $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 );
2261                          $axisLabel['end']->y -= $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 );
2262                          break;
2263                  }
2264  
2265                  $axisLabel['object']->renderLabels(
2266                      $this,
2267                      $axisLabel['boundings'],
2268                      $axisLabel['start'],
2269                      $axisLabel['end'],
2270                      $axisLabel['axis']
2271                  );
2272              }
2273          }
2274      }
2275  
2276      /**
2277       * Draw background image
2278       *
2279       * Draws a background image at the defined position. If repeat is set the
2280       * background image will be repeated like any texture.
2281       * 
2282       * @param ezcGraphBoundings $boundings Boundings for the background image
2283       * @param string $file Filename of background image
2284       * @param int $position Position of background image
2285       * @param int $repeat Type of repetition
2286       * @return void
2287       */
2288      public function drawBackgroundImage(
2289          ezcGraphBoundings $boundings,
2290          $file,
2291          $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE
2292          $repeat = ezcGraph::NO_REPEAT )
2293      {
2294          $imageData = getimagesize( $file );
2295          $imageWidth = $imageData[0];
2296          $imageHeight = $imageData[1];
2297  
2298          $imageWidth = min( $imageWidth, $boundings->x1 - $boundings->x0 );
2299          $imageHeight = min( $imageHeight, $boundings->y1 - $boundings->y0 );
2300  
2301          $imagePosition = new ezcGraphCoordinate( 
2302              $boundings->x0, 
2303              $boundings->y0
2304          );
2305  
2306          // Determine x position
2307          switch ( true ) {
2308              case ( $repeat & ezcGraph::HORIZONTAL ):
2309                  // If is repeated on this axis fall back to position zero
2310              case ( $position & ezcGraph::LEFT ):
2311                  $imagePosition->x = $boundings->x0;
2312                  break;
2313              case ( $position & ezcGraph::RIGHT ):
2314                  $imagePosition->x = max( 
2315                      $boundings->x1 - $imageWidth,
2316                      $boundings->x0
2317                  );
2318                  break;
2319              default:
2320                  $imagePosition->x = max(
2321                      $boundings->x0 + ( $boundings->x1 - $boundings->x0 - $imageWidth ) / 2,
2322                      $boundings->x0
2323                  );
2324                  break;
2325          }
2326  
2327          // Determine y position
2328          switch ( true ) {
2329              case ( $repeat & ezcGraph::VERTICAL ):
2330                  // If is repeated on this axis fall back to position zero
2331              case ( $position & ezcGraph::TOP ):
2332                  $imagePosition->y = $boundings->y0;
2333                  break;
2334              case ( $position & ezcGraph::BOTTOM ):
2335                  $imagePosition->y = max( 
2336                      $boundings->y1 - $imageHeight,
2337                      $boundings->y0
2338                  );
2339                  break;
2340              default:
2341                  $imagePosition->y = max(
2342                      $boundings->y0 + ( $boundings->y1 - $boundings->y0 - $imageHeight ) / 2,
2343                      $boundings->y0
2344                  );
2345                  break;
2346          }
2347  
2348          // Texturize backround based on position and repetition
2349          $position = new ezcGraphCoordinate(
2350              $imagePosition->x,
2351              $imagePosition->y
2352          );
2353          
2354          do 
2355          {
2356              $position->y = $imagePosition->y;
2357  
2358              do 
2359              {
2360                  $this->driver->drawImage( 
2361                      $file, 
2362                      $position, 
2363                      $imageWidth, 
2364                      $imageHeight 
2365                  );
2366  
2367                  $position->y += $imageHeight;
2368              }
2369              while ( ( $position->y < $boundings->y1 ) &&
2370                      ( $repeat & ezcGraph::VERTICAL ) );
2371              
2372              $position->x += $imageWidth;
2373          }
2374          while ( ( $position->x < $boundings->x1 ) &&
2375                  ( $repeat & ezcGraph::HORIZONTAL ) );
2376      }
2377  
2378      /**
2379       * Call all postprocessing functions
2380       * 
2381       * @return void
2382       */
2383      protected function finish()
2384      {
2385          $this->finishCirleSectors();
2386          $this->finishPieSegmentLabels();
2387          $this->finishBars();
2388          $this->finishLineSymbols();
2389          $this->finishFrontLines();
2390  
2391          return true;
2392      }
2393  
2394      /**
2395       * Reset renderer properties
2396       *
2397       * Reset all renderer properties, which were calculated during the
2398       * rendering process, to offer a clean environment for rerendering.
2399       * 
2400       * @return void
2401       */
2402      protected function resetRenderer()
2403      {
2404          parent::resetRenderer();
2405  
2406          // Also reset special 3D renderer options
2407          $this->pieSegmentLabels = array(
2408              0 => array(),
2409              1 => array(),
2410          );
2411          $this->pieSegmentBoundings = false;
2412          $this->linePostSymbols     = array();
2413          $this->frontLines          = array();
2414          $this->circleSectors       = array();
2415          $this->barPostProcessing   = array();
2416          $this->depth               = false;
2417          $this->xDepthFactor        = false;
2418          $this->yDepthFactor        = false;
2419          $this->dataBoundings       = false;
2420          $this->axisLabels          = array();
2421      }
2422  }
2423  
2424  ?>


Generated: Thu Jul 28 15:48:31 2011 Cross-referenced by PHPXref 0.7