[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

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

   1  <?php
   2  /**
   3   * File containing the two 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. Renders charts in
  12   * a two dimensional view.
  13   *
  14   * The class options are defined in the class {@link ezcGraphRenderer2dOptions}
  15   * extending the basic renderer options in {@link ezcGraphRendererOptions}.
  16   *
  17   * <code>
  18   *   $graph = new ezcGraphPieChart();
  19   *   $graph->palette = new ezcGraphPaletteBlack();
  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 ezcGraphRenderer2d();
  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   *   $graph->renderer->options->pieChartGleamBorder = 2;
  41   *   
  42   *   $graph->renderer->options->pieChartShadowSize = 3;
  43   *   $graph->renderer->options->pieChartShadowColor = '#000000';
  44   *   
  45   *   $graph->renderer->options->legendSymbolGleam = .5;
  46   *   $graph->renderer->options->legendSymbolGleamSize = .9;
  47   *   $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF';
  48   *   
  49   *   $graph->renderer->options->pieChartSymbolColor = '#BABDB688';
  50   *   
  51   *   $graph->render( 400, 150, 'tutorial_pie_chart_pimped.svg' );
  52   * </code>
  53   *
  54   * @version 1.5
  55   * @package Graph
  56   * @mainclass
  57   */
  58  class ezcGraphRenderer2d 
  59      extends
  60          ezcGraphRenderer
  61      implements
  62          ezcGraphRadarRenderer, ezcGraphStackedBarsRenderer, ezcGraphOdometerRenderer
  63  {
  64  
  65      /**
  66       * Pie segment labels divided into two array, containing the labels on the
  67       * left and right side of the pie chart center.
  68       * 
  69       * @var array
  70       */
  71      protected $pieSegmentLabels = array(
  72          0 => array(),
  73          1 => array(),
  74      );
  75  
  76      /**
  77       * Contains the boundings used for pie segments
  78       * 
  79       * @var ezcGraphBoundings
  80       */
  81      protected $pieSegmentBoundings = false;
  82  
  83      /**
  84       * Array with symbols for post processing, which ensures, that the symbols
  85       * are rendered topmost.
  86       * 
  87       * @var array
  88       */
  89      protected $linePostSymbols = array();
  90  
  91      /**
  92       * Options 
  93       * 
  94       * @var ezcGraphRenderer2dOptions
  95       */
  96      protected $options;
  97  
  98      /**
  99       * Collect axis labels, so that the axis are drawn, when all axis spaces 
 100       * are known.
 101       * 
 102       * @var array
 103       */
 104      protected $axisLabels = array();
 105  
 106      /**
 107       * Collects circle sectors to draw shadow in background of all circle 
 108       * sectors.
 109       * 
 110       * @var array
 111       */
 112      protected $circleSectors = array();
 113  
 114      /**
 115       * Constructor
 116       * 
 117       * @param array $options Default option array
 118       * @return void
 119       * @ignore
 120       */
 121      public function __construct( array $options = array() )
 122      {
 123          $this->options = new ezcGraphRenderer2dOptions( $options );
 124      }
 125  
 126      /**
 127       * __get 
 128       * 
 129       * @param mixed $propertyName 
 130       * @throws ezcBasePropertyNotFoundException
 131       *          If a the value for the property options is not an instance of
 132       * @return mixed
 133       * @ignore
 134       */
 135      public function __get( $propertyName )
 136      {
 137          switch ( $propertyName )
 138          {
 139              case 'options':
 140                  return $this->options;
 141              default:
 142                  return parent::__get( $propertyName );
 143          }
 144      }
 145  
 146      /**
 147       * Draw pie segment
 148       *
 149       * Draws a single pie segment
 150       * 
 151       * @param ezcGraphBoundings $boundings Chart boundings
 152       * @param ezcGraphContext $context Context of call
 153       * @param ezcGraphColor $color Color of pie segment
 154       * @param float $startAngle Start angle
 155       * @param float $endAngle End angle
 156       * @param mixed $label Label of pie segment
 157       * @param bool $moveOut Move out from middle for hilighting
 158       * @return void
 159       */
 160      public function drawPieSegment(
 161          ezcGraphBoundings $boundings,
 162          ezcGraphContext $context,
 163          ezcGraphColor $color,
 164          $startAngle = .0,
 165          $endAngle = 360.,
 166          $label = false,
 167          $moveOut = false )
 168      {
 169          // Apply offset
 170          $startAngle += $this->options->pieChartOffset;
 171          $endAngle += $this->options->pieChartOffset;
 172  
 173          // Calculate position and size of pie
 174          $center = new ezcGraphCoordinate(
 175              $boundings->x0 + ( $boundings->width ) / 2,
 176              $boundings->y0 + ( $boundings->height ) / 2
 177          );
 178  
 179          // Limit radius to fourth of width and half of height at maximum
 180          $radius = min(
 181              ( $boundings->width ) * $this->options->pieHorizontalSize,
 182              ( $boundings->height ) * $this->options->pieVerticalSize
 183          );
 184  
 185          // Move pie segment out of the center
 186          if ( $moveOut )
 187          {
 188              $direction = ( $endAngle + $startAngle ) / 2;
 189  
 190              $center = new ezcGraphCoordinate(
 191                  $center->x + $this->options->moveOut * $radius * cos( deg2rad( $direction ) ),
 192                  $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) )
 193              );
 194          }
 195  
 196          // Add circle sector to queue
 197          $this->circleSectors[] = array(
 198              'center' =>     $center,
 199              'context' =>    $context,
 200              'width' =>      $radius * 2 * ( 1 - $this->options->moveOut ),
 201              'height' =>     $radius * 2 * ( 1 - $this->options->moveOut ),
 202              'start' =>      $startAngle,
 203              'end' =>        $endAngle,
 204              'color' =>      $color,
 205          );
 206  
 207          if ( $label )
 208          {
 209              // Determine position of label
 210              $direction = ( $endAngle + $startAngle ) / 2;
 211              $pieSegmentCenter = new ezcGraphCoordinate(
 212                  $center->x + cos( deg2rad( $direction ) ) * $radius,
 213                  $center->y + sin( deg2rad( $direction ) ) * $radius
 214              );
 215  
 216              // Split labels up into left an right size and index them on their
 217              // y position
 218              $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][$pieSegmentCenter->y] = array(
 219                  new ezcGraphCoordinate(
 220                      $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3,
 221                      $center->y + sin( deg2rad( $direction ) ) * $radius * 2 / 3
 222                  ),
 223                  $label,
 224                  $context
 225              );
 226          }
 227  
 228          if ( !$this->pieSegmentBoundings )
 229          {
 230              $this->pieSegmentBoundings = $boundings;
 231          }
 232      }
 233  
 234      /**
 235       * Draws the collected circle sectors
 236       *
 237       * All circle sectors are collected and drawn later to be able to render 
 238       * the shadows of the pie segments in the back of all pie segments.
 239       * 
 240       * @return void
 241       */
 242      protected function finishCircleSectors()
 243      {
 244          // Add circle sector sides to simple z buffer prioriry list
 245          if ( $this->options->pieChartShadowSize > 0 )
 246          {
 247              foreach ( $this->circleSectors as $circleSector )
 248              {
 249                  $this->driver->drawCircleSector(
 250                      new ezcGraphCoordinate(
 251                          $circleSector['center']->x + $this->options->pieChartShadowSize,
 252                          $circleSector['center']->y + $this->options->pieChartShadowSize
 253                      ),
 254                      $circleSector['width'],
 255                      $circleSector['height'],
 256                      $circleSector['start'],
 257                      $circleSector['end'],
 258                      $this->options->pieChartShadowColor->transparent( $this->options->pieChartShadowTransparency ),
 259                      true
 260                  );
 261              }
 262          }
 263  
 264          foreach ( $this->circleSectors as $circleSector )
 265          {
 266              // Draw circle sector
 267              $this->addElementReference( 
 268                  $circleSector['context'],
 269                  $this->driver->drawCircleSector(
 270                      $circleSector['center'],
 271                      $circleSector['width'],
 272                      $circleSector['height'],
 273                      $circleSector['start'],
 274                      $circleSector['end'],
 275                      $circleSector['color'],
 276                      true
 277                  )
 278              );
 279  
 280              $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder );
 281              $this->driver->drawCircleSector(
 282                  $circleSector['center'],
 283                  $circleSector['width'],
 284                  $circleSector['height'],
 285                  $circleSector['start'],
 286                  $circleSector['end'],
 287                  $darkenedColor,
 288                  false
 289              );
 290  
 291              if ( $this->options->pieChartGleam !== false )
 292              {
 293                  $gradient = new ezcGraphLinearGradient(
 294                      $circleSector['center'],
 295                      new ezcGraphCoordinate(
 296                          $circleSector['center']->x,
 297                          $circleSector['center']->y - $circleSector['height'] / 2
 298                      ),
 299                      $this->options->pieChartGleamColor->transparent( 1 ),
 300                      $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam )
 301                  );
 302  
 303                  $this->addElementReference( $circleSector['context'],
 304                      $this->driver->drawCircleSector(
 305                          $circleSector['center'],
 306                          $circleSector['width'] - $this->options->pieChartGleamBorder * 2,
 307                          $circleSector['height'] - $this->options->pieChartGleamBorder * 2,
 308                          $circleSector['start'],
 309                          $circleSector['end'],
 310                          $gradient,
 311                          true
 312                      )
 313                  );
 314  
 315                  $gradient = new ezcGraphLinearGradient(
 316                      new ezcGraphCoordinate(
 317                          $circleSector['center']->x,
 318                          $circleSector['center']->y + $circleSector['height'] / 4
 319                      ),
 320                      new ezcGraphCoordinate(
 321                          $circleSector['center']->x,
 322                          $circleSector['center']->y + $circleSector['height'] / 2
 323                      ),
 324                      $this->options->pieChartGleamColor->transparent( 1 ),
 325                      $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam )
 326                  );
 327  
 328                  $this->addElementReference( $circleSector['context'],
 329                      $this->driver->drawCircleSector(
 330                          $circleSector['center'],
 331                          $circleSector['width'] - $this->options->pieChartGleamBorder * 2,
 332                          $circleSector['height'] - $this->options->pieChartGleamBorder * 2,
 333                          $circleSector['start'],
 334                          $circleSector['end'],
 335                          $gradient,
 336                          true
 337                      )
 338                  );
 339              }
 340          }
 341      }
 342  
 343      /**
 344       * Draws the collected pie segment labels
 345       *
 346       * All labels are collected and drawn later to be able to partition the 
 347       * available space for the labels woth knowledge of the overall label 
 348       * count and their required size and optimal position.
 349       * 
 350       * @return void
 351       */
 352      protected function finishPieSegmentLabels()
 353      {
 354          if ( $this->pieSegmentBoundings === false )
 355          {
 356              return true;
 357          }
 358  
 359          $boundings = $this->pieSegmentBoundings;
 360  
 361          // Calculate position and size of pie
 362          $center = new ezcGraphCoordinate(
 363              $boundings->x0 + ( $boundings->width ) / 2,
 364              $boundings->y0 + ( $boundings->height ) / 2
 365          );
 366  
 367          // Limit radius to fourth of width and half of height at maximum
 368          $radius = min(
 369              ( $boundings->width ) * $this->options->pieHorizontalSize,
 370              ( $boundings->height ) * $this->options->pieVerticalSize
 371          );
 372  
 373          $pieChartHeight = min(
 374              $radius * 2 + $radius / max( 1, count ( $this->pieSegmentLabels[0] ), count( $this->pieSegmentLabels[1] ) ) * 4,
 375              $boundings->height
 376          );
 377          $pieChartYPosition = $boundings->y0 + ( ( $boundings->height ) - $pieChartHeight ) / 2;
 378  
 379          // Calculate maximum height of labels
 380          $labelHeight = min(
 381              ( count( $this->pieSegmentLabels[0] )
 382                  ? $pieChartHeight / count( $this->pieSegmentLabels[0] )
 383                  : $pieChartHeight
 384              ),
 385              ( count( $this->pieSegmentLabels[1] )
 386                  ? $pieChartHeight / count( $this->pieSegmentLabels[1] )
 387                  : $pieChartHeight
 388              ),
 389              ( $pieChartHeight ) * $this->options->maxLabelHeight
 390          );
 391  
 392          $symbolSize = $this->options->symbolSize;
 393  
 394          foreach ( $this->pieSegmentLabels as $side => $labelPart )
 395          {
 396              $minHeight = $pieChartYPosition;
 397              $toShare = $pieChartHeight - count( $labelPart ) * $labelHeight;
 398  
 399              // Sort to draw topmost label first
 400              ksort( $labelPart );
 401              $sign = ( $side ? -1 : 1 );
 402  
 403              foreach ( $labelPart as $height => $label )
 404              {
 405                  if ( ( $height - $labelHeight / 2 ) > $minHeight )
 406                  {
 407                      $share = min( $toShare, ( $height - $labelHeight / 2) - $minHeight );
 408                      $minHeight += $share;
 409                      $toShare -= $share;
 410                  }
 411  
 412                  // Determine position of label
 413                  $minHeight += max( 0, $height - $minHeight - $labelHeight ) / $pieChartHeight * $toShare;
 414                  $verticalDistance = ( $center->y - $minHeight - $labelHeight / 2 ) / $radius;
 415  
 416                  $labelPosition = new ezcGraphCoordinate(
 417                      $center->x - 
 418                      $sign * (
 419                          abs( $verticalDistance ) > 1
 420                          // If vertical distance to center is greater then the
 421                          // radius, use the centerline for the horizontal 
 422                          // position
 423                          ? max (
 424                              5,
 425                              abs( $label[0]->x - $center->x )
 426                          )
 427                          // Else place the label outside of the pie chart
 428                          : ( cos ( asin ( $verticalDistance ) ) * $radius + 
 429                              $symbolSize * (int) $this->options->showSymbol 
 430                          )
 431                      ),
 432                      $minHeight + $labelHeight / 2
 433                  );
 434  
 435                  if ( $this->options->showSymbol )
 436                  {
 437                      // Draw label
 438                      $this->driver->drawLine(
 439                          $label[0],
 440                          $labelPosition,
 441                          $this->options->pieChartSymbolColor,
 442                          1
 443                      );
 444  
 445                      $this->driver->drawCircle(
 446                          $label[0],
 447                          $symbolSize,
 448                          $symbolSize,
 449                          $this->options->pieChartSymbolColor,
 450                          true
 451                      );
 452                      $this->driver->drawCircle(
 453                          $labelPosition,
 454                          $symbolSize,
 455                          $symbolSize,
 456                          $this->options->pieChartSymbolColor,
 457                          true
 458                      );
 459                  }
 460  
 461                  $this->addElementReference( 
 462                      $label[2],
 463                      $this->driver->drawTextBox(
 464                          $label[1],
 465                          new ezcGraphCoordinate(
 466                              ( !$side ? $boundings->x0 : $labelPosition->x + $symbolSize ),
 467                              $minHeight
 468                          ),
 469                          ( !$side ? $labelPosition->x - $boundings->x0 - $symbolSize : $boundings->x1 - $labelPosition->x - $symbolSize ),
 470                          $labelHeight,
 471                          ( !$side ? ezcGraph::RIGHT : ezcGraph::LEFT ) | ezcGraph::MIDDLE
 472                      )
 473                  );
 474  
 475                  // Add used space to minHeight
 476                  $minHeight += $labelHeight;
 477              }
 478          }
 479      }
 480  
 481      /**
 482       * Draw the collected line symbols
 483       *
 484       * Symbols for the data lines are collected and delayed to ensure that 
 485       * they are not covered and hidden by other data lines.
 486       * 
 487       * @return void
 488       */
 489      protected function finishLineSymbols()
 490      {
 491          foreach ( $this->linePostSymbols as $symbol )
 492          {
 493              $this->addElementReference(
 494                  $symbol['context'],
 495                  $this->drawSymbol(
 496                      $symbol['boundings'],
 497                      $symbol['color'],
 498                      $symbol['symbol']
 499                  )
 500              );
 501          }
 502      }
 503      
 504      /**
 505       * Draw bar
 506       *
 507       * Draws a bar as a data element in a line chart
 508       * 
 509       * @param ezcGraphBoundings $boundings Chart boundings
 510       * @param ezcGraphContext $context Context of call
 511       * @param ezcGraphColor $color Color of line
 512       * @param ezcGraphCoordinate $position Position of data point
 513       * @param float $stepSize Space which can be used for bars
 514       * @param int $dataNumber Number of dataset
 515       * @param int $dataCount Count of datasets in chart
 516       * @param int $symbol Symbol to draw for line
 517       * @param float $axisPosition Position of axis for drawing filled lines
 518       * @return void
 519       */
 520      public function drawBar(
 521          ezcGraphBoundings $boundings,
 522          ezcGraphContext $context,
 523          ezcGraphColor $color,
 524          ezcGraphCoordinate $position,
 525          $stepSize,
 526          $dataNumber = 1,
 527          $dataCount = 1,
 528          $symbol = ezcGraph::NO_SYMBOL,
 529          $axisPosition = 0. )
 530      {
 531          // Apply margin
 532          $margin = $stepSize * $this->options->barMargin;
 533          $padding = $stepSize * $this->options->barPadding;
 534          $barWidth = ( $stepSize - $margin ) / $dataCount - $padding;
 535          $offset = - $stepSize / 2 + $margin / 2 + ( $dataCount - $dataNumber - 1 ) * ( $padding + $barWidth ) + $padding / 2;
 536  
 537          $barPointArray = array(
 538              new ezcGraphCoordinate(
 539                  $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
 540                  $boundings->y0 + ( $boundings->height ) * $axisPosition
 541              ),
 542              new ezcGraphCoordinate(
 543                  $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
 544                  $boundings->y0 + ( $boundings->height ) * $position->y
 545              ),
 546              new ezcGraphCoordinate(
 547                  $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
 548                  $boundings->y0 + ( $boundings->height ) * $position->y
 549              ),
 550              new ezcGraphCoordinate(
 551                  $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
 552                  $boundings->y0 + ( $boundings->height ) * $axisPosition
 553              ),
 554          );
 555  
 556          $this->addElementReference(
 557              $context,
 558              $this->driver->drawPolygon(
 559                  $barPointArray,
 560                  $color,
 561                  true
 562              )
 563          );
 564  
 565          if ( $this->options->dataBorder > 0 )
 566          {
 567              $darkened = $color->darken( $this->options->dataBorder );
 568              $this->driver->drawPolygon(
 569                  $barPointArray,
 570                  $darkened,
 571                  false,
 572                  1
 573              );
 574          }
 575      }
 576  
 577      /**
 578       * Draw stacked bar
 579       *
 580       * Draws a stacked bar part as a data element in a line chart
 581       * 
 582       * @param ezcGraphBoundings $boundings Chart boundings
 583       * @param ezcGraphContext $context Context of call
 584       * @param ezcGraphColor $color Color of line
 585       * @param ezcGraphCoordinate $start
 586       * @param ezcGraphCoordinate $position
 587       * @param float $stepSize Space which can be used for bars
 588       * @param int $symbol Symbol to draw for line
 589       * @param float $axisPosition Position of axis for drawing filled lines
 590       * @return void
 591       */
 592      public function drawStackedBar(
 593          ezcGraphBoundings $boundings,
 594          ezcGraphContext $context,
 595          ezcGraphColor $color,
 596          ezcGraphCoordinate $start,
 597          ezcGraphCoordinate $position,
 598          $stepSize,
 599          $symbol = ezcGraph::NO_SYMBOL,
 600          $axisPosition = 0. )
 601      {
 602          // Apply margin
 603          $margin = $stepSize * $this->options->barMargin;
 604          $barWidth = $stepSize - $margin;
 605          $offset = - $stepSize / 2 + $margin / 2;
 606  
 607          $barPointArray = array(
 608              new ezcGraphCoordinate(
 609                  $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
 610                  $boundings->y0 + ( $boundings->height ) * $start->y
 611              ),
 612              new ezcGraphCoordinate(
 613                  $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
 614                  $boundings->y0 + ( $boundings->height ) * $position->y
 615              ),
 616              new ezcGraphCoordinate(
 617                  $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
 618                  $boundings->y0 + ( $boundings->height ) * $position->y
 619              ),
 620              new ezcGraphCoordinate(
 621                  $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
 622                  $boundings->y0 + ( $boundings->height ) * $start->y
 623              ),
 624          );
 625  
 626          $this->addElementReference(
 627              $context,
 628              $this->driver->drawPolygon(
 629                  $barPointArray,
 630                  $color,
 631                  true
 632              )
 633          );
 634  
 635          if ( $this->options->dataBorder > 0 )
 636          {
 637              $darkened = $color->darken( $this->options->dataBorder );
 638              $this->driver->drawPolygon(
 639                  $barPointArray,
 640                  $darkened,
 641                  false,
 642                  1
 643              );
 644          }
 645      }
 646      
 647      /**
 648       * Draw data line
 649       *
 650       * Draws a line as a data element in a line chart
 651       * 
 652       * @param ezcGraphBoundings $boundings Chart boundings
 653       * @param ezcGraphContext $context Context of call
 654       * @param ezcGraphColor $color Color of line
 655       * @param ezcGraphCoordinate $start Starting point
 656       * @param ezcGraphCoordinate $end Ending point
 657       * @param int $dataNumber Number of dataset
 658       * @param int $dataCount Count of datasets in chart
 659       * @param int $symbol Symbol to draw for line
 660       * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor
 661       * @param ezcGraphColor $fillColor Color to fill line with
 662       * @param float $axisPosition Position of axis for drawing filled lines
 663       * @param float $thickness Line thickness
 664       * @return void
 665       */
 666      public function drawDataLine(
 667          ezcGraphBoundings $boundings,
 668          ezcGraphContext $context,
 669          ezcGraphColor $color,
 670          ezcGraphCoordinate $start,
 671          ezcGraphCoordinate $end,
 672          $dataNumber = 1,
 673          $dataCount = 1,
 674          $symbol = ezcGraph::NO_SYMBOL,
 675          ezcGraphColor $symbolColor = null,
 676          ezcGraphColor $fillColor = null,
 677          $axisPosition = 0.,
 678          $thickness = 1. )
 679      {
 680          // Perhaps fill up line
 681          if ( $fillColor !== null &&
 682               $start->x != $end->x )
 683          {
 684              $startValue = $axisPosition - $start->y;
 685              $endValue = $axisPosition - $end->y;
 686  
 687              if ( ( $startValue == 0 ) ||
 688                   ( $endValue == 0 ) ||
 689                   ( $startValue / abs( $startValue ) == $endValue / abs( $endValue ) ) )
 690              {
 691                  // Values have the same sign or are on the axis
 692                  $this->driver->drawPolygon(
 693                      array(
 694                          new ezcGraphCoordinate(
 695                              $boundings->x0 + ( $boundings->width ) * $start->x,
 696                              $boundings->y0 + ( $boundings->height ) * $start->y
 697                          ),
 698                          new ezcGraphCoordinate(
 699                              $boundings->x0 + ( $boundings->width ) * $end->x,
 700                              $boundings->y0 + ( $boundings->height ) * $end->y
 701                          ),
 702                          new ezcGraphCoordinate(
 703                              $boundings->x0 + ( $boundings->width ) * $end->x,
 704                              $boundings->y0 + ( $boundings->height ) * $axisPosition
 705                          ),
 706                          new ezcGraphCoordinate(
 707                              $boundings->x0 + ( $boundings->width ) * $start->x,
 708                              $boundings->y0 + ( $boundings->height ) * $axisPosition
 709                          ),
 710                      ),
 711                      $fillColor,
 712                      true
 713                  );
 714              }
 715              else
 716              {
 717                  // values are on differente sides of the axis - split the filled polygon
 718                  $startDiff = abs( $axisPosition - $start->y );
 719                  $endDiff = abs( $axisPosition - $end->y );
 720  
 721                  $cuttingPosition = $startDiff / ( $endDiff + $startDiff );
 722                  $cuttingPoint = new ezcGraphCoordinate(
 723                      $start->x + ( $end->x - $start->x ) * $cuttingPosition,
 724                      $axisPosition
 725                  );
 726  
 727                  $this->driver->drawPolygon(
 728                      array(
 729                          new ezcGraphCoordinate(
 730                              $boundings->x0 + ( $boundings->width ) * $start->x,
 731                              $boundings->y0 + ( $boundings->height ) * $axisPosition
 732                          ),
 733                          new ezcGraphCoordinate(
 734                              $boundings->x0 + ( $boundings->width ) * $start->x,
 735                              $boundings->y0 + ( $boundings->height ) * $start->y
 736                          ),
 737                          new ezcGraphCoordinate(
 738                              $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x,
 739                              $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y
 740                          ),
 741                      ),
 742                      $fillColor,
 743                      true
 744                  );
 745  
 746                  $this->driver->drawPolygon(
 747                      array(
 748                          new ezcGraphCoordinate(
 749                              $boundings->x0 + ( $boundings->width ) * $end->x,
 750                              $boundings->y0 + ( $boundings->height ) * $axisPosition
 751                          ),
 752                          new ezcGraphCoordinate(
 753                              $boundings->x0 + ( $boundings->width ) * $end->x,
 754                              $boundings->y0 + ( $boundings->height ) * $end->y
 755                          ),
 756                          new ezcGraphCoordinate(
 757                              $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x,
 758                              $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y
 759                          ),
 760                      ),
 761                      $fillColor,
 762                      true
 763                  );
 764              }
 765          }
 766  
 767          // Draw line
 768          $this->driver->drawLine(
 769              new ezcGraphCoordinate(
 770                  $boundings->x0 + ( $boundings->width ) * $start->x,
 771                  $boundings->y0 + ( $boundings->height ) * $start->y
 772              ),
 773              new ezcGraphCoordinate(
 774                  $boundings->x0 + ( $boundings->width ) * $end->x,
 775                  $boundings->y0 + ( $boundings->height ) * $end->y
 776              ),
 777              $color,
 778              $thickness
 779          );
 780  
 781          // Draw line symbol
 782          if ( $symbol !== ezcGraph::NO_SYMBOL )
 783          {
 784              if ( $symbolColor === null )
 785              {
 786                  $symbolColor = $color;
 787              }
 788      
 789              $this->linePostSymbols[] = array(
 790                  'boundings' => new ezcGraphBoundings(
 791                      $boundings->x0 + ( $boundings->width ) * $end->x - $this->options->symbolSize / 2,
 792                      $boundings->y0 + ( $boundings->height ) * $end->y - $this->options->symbolSize / 2,
 793                      $boundings->x0 + ( $boundings->width ) * $end->x + $this->options->symbolSize / 2,
 794                      $boundings->y0 + ( $boundings->height ) * $end->y + $this->options->symbolSize / 2
 795                  ),
 796                  'color' => $symbolColor,
 797                  'context' => $context,
 798                  'symbol' => $symbol,
 799              );
 800          }
 801      }
 802  
 803      /**
 804       * Returns a coordinate in the given bounding box for the given angle
 805       * radius with the center as base point.
 806       * 
 807       * @param ezcGraphBoundings $boundings
 808       * @param ezcGraphCoordinate $center
 809       * @param float $angle
 810       * @param float $radius
 811       * @return float
 812       */
 813      protected function getCoordinateFromAngleAndRadius(
 814          ezcGraphBoundings $boundings,
 815          ezcGraphCoordinate $center,
 816          $angle,
 817          $radius
 818      )
 819      {
 820          $direction = new ezcGraphCoordinate(
 821              sin( $angle ) * $boundings->width / 2,
 822              -cos( $angle ) * $boundings->height / 2
 823          );
 824  
 825          $offset = new ezcGraphCoordinate(
 826              sin( $angle ) * $this->xAxisSpace,
 827              -cos( $angle ) * $this->yAxisSpace
 828          );
 829  
 830          $direction->x -= 2 * $offset->x;
 831          $direction->y -= 2 * $offset->y;
 832  
 833          return new ezcGraphCoordinate(
 834              $boundings->x0 +
 835                  $center->x +
 836                  $offset->x +
 837                  $direction->x * $radius,
 838              $boundings->y0 +
 839                  $center->y +
 840                  $offset->y +
 841                  $direction->y * $radius
 842          );
 843      }
 844  
 845      /**
 846       * Draw radar chart data line
 847       *
 848       * Draws a line as a data element in a radar chart
 849       * 
 850       * @param ezcGraphBoundings $boundings Chart boundings
 851       * @param ezcGraphContext $context Context of call
 852       * @param ezcGraphColor $color Color of line
 853       * @param ezcGraphCoordinate $center Center of radar chart
 854       * @param ezcGraphCoordinate $start Starting point
 855       * @param ezcGraphCoordinate $end Ending point
 856       * @param int $dataNumber Number of dataset
 857       * @param int $dataCount Count of datasets in chart
 858       * @param int $symbol Symbol to draw for line
 859       * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor
 860       * @param ezcGraphColor $fillColor Color to fill line with
 861       * @param float $thickness Line thickness
 862       * @return void
 863       */
 864      public function drawRadarDataLine(
 865          ezcGraphBoundings $boundings,
 866          ezcGraphContext $context,
 867          ezcGraphColor $color,
 868          ezcGraphCoordinate $center,
 869          ezcGraphCoordinate $start,
 870          ezcGraphCoordinate $end,
 871          $dataNumber = 1,
 872          $dataCount = 1,
 873          $symbol = ezcGraph::NO_SYMBOL,
 874          ezcGraphColor $symbolColor = null,
 875          ezcGraphColor $fillColor = null,
 876          $thickness = 1.
 877      )
 878      {
 879          // Calculate line points from chart coordinates
 880          $start = $this->getCoordinateFromAngleAndRadius(
 881              $boundings,
 882              $center,
 883              $start->x * 2 * M_PI,
 884              $start->y
 885          );
 886          $end = $this->getCoordinateFromAngleAndRadius(
 887              $boundings,
 888              $center,
 889              $end->x * 2 * M_PI,
 890              $end->y
 891          );
 892  
 893          // Fill line
 894          if ( $fillColor !== null )
 895          {
 896              $this->driver->drawPolygon(
 897                  array(
 898                      $start,
 899                      $end,
 900                      new ezcGraphCoordinate(
 901                          $boundings->x0 + $center->x,
 902                          $boundings->y0 + $center->y
 903                      ),
 904                  ),
 905                  $fillColor,
 906                  true
 907              );
 908          }
 909  
 910          // Draw line
 911          $this->driver->drawLine(
 912              $start,
 913              $end,
 914              $color,
 915              $thickness
 916          );
 917  
 918          if ( $symbol !== ezcGraph::NO_SYMBOL )
 919          {
 920              $this->drawSymbol(
 921                  new ezcGraphBoundings(
 922                      $end->x - $this->options->symbolSize / 2,
 923                      $end->y - $this->options->symbolSize / 2,
 924                      $end->x + $this->options->symbolSize / 2,
 925                      $end->y + $this->options->symbolSize / 2
 926                  ),
 927                  $symbolColor,
 928                  $symbol
 929              );
 930          }
 931      }
 932      
 933      /**
 934       * Draws a highlight textbox for a datapoint.
 935       *
 936       * A highlight textbox for line and bar charts means a box with the current 
 937       * value in the graph.
 938       * 
 939       * @param ezcGraphBoundings $boundings Chart boundings
 940       * @param ezcGraphContext $context Context of call
 941       * @param ezcGraphCoordinate $end Ending point
 942       * @param float $axisPosition Position of axis for drawing filled lines
 943       * @param int $dataNumber Number of dataset
 944       * @param int $dataCount Count of datasets in chart
 945       * @param ezcGraphFontOptions $font Font used for highlight string
 946       * @param string $text Acutual value
 947       * @param int $size Size of highlight text
 948       * @param ezcGraphColor $markLines
 949       * @param int $xOffset
 950       * @param int $yOffset
 951       * @param float $stepSize
 952       * @param int $type
 953       * @return void
 954       */
 955      public function drawDataHighlightText(
 956          ezcGraphBoundings $boundings,
 957          ezcGraphContext $context,
 958          ezcGraphCoordinate $end,
 959          $axisPosition = 0.,
 960          $dataNumber = 1,
 961          $dataCount = 1,
 962          ezcGraphFontOptions $font,
 963          $text,
 964          $size,
 965          ezcGraphColor $markLines = null,
 966          $xOffset = 0,
 967          $yOffset = 0,
 968          $stepSize = 0.,
 969          $type = ezcGraph::LINE )
 970      {
 971          // Bar specific calculations
 972          if ( $type !== ezcGraph::LINE )
 973          {
 974              $margin = $stepSize * $this->options->barMargin;
 975              $padding = $stepSize * $this->options->barPadding;
 976              $barWidth = ( $stepSize - $margin ) / $dataCount - $padding;
 977              $offset = -( $dataNumber +  ( $dataCount - 1 ) / -2 ) * ( $barWidth + $padding );
 978          }
 979  
 980          $this->driver->options->font = $font;
 981          $width = $boundings->width / $dataCount;
 982          
 983          $dataPoint = new ezcGraphCoordinate(
 984              $boundings->x0 + ( $boundings->width ) * $end->x + $xOffset +
 985                  ( $type === ezcGraph::LINE ? 0 : $offset ),
 986              $boundings->y0 + ( $boundings->height ) * $end->y + $yOffset
 987          );
 988  
 989          if ( $end->y < $axisPosition )
 990          {
 991              $this->driver->drawTextBox(
 992                  $text,
 993                  new ezcGraphCoordinate(
 994                      $dataPoint->x - $width / 2,
 995                      $dataPoint->y - $size - $font->padding - $this->options->symbolSize
 996                  ),
 997                  $width,
 998                  $size,
 999                  ezcGraph::CENTER | ezcGraph::BOTTOM
1000              );
1001          }
1002          else
1003          {
1004              $this->driver->drawTextBox(
1005                  $text,
1006                  new ezcGraphCoordinate(
1007                      $dataPoint->x - $width / 2,
1008                      $dataPoint->y + $font->padding + $this->options->symbolSize
1009                  ),
1010                  $width,
1011                  $size,
1012                  ezcGraph::CENTER | ezcGraph::TOP
1013              );
1014          }
1015      }
1016      
1017      /**
1018       * Draw legend
1019       *
1020       * Will draw a legend in the bounding box
1021       * 
1022       * @param ezcGraphBoundings $boundings Bounding of legend
1023       * @param ezcGraphChartElementLegend $legend Legend to draw;
1024       * @param int $type Type of legend: Protrait or landscape
1025       * @return void
1026       */
1027      public function drawLegend(
1028          ezcGraphBoundings $boundings,
1029          ezcGraphChartElementLegend $legend,
1030          $type = ezcGraph::VERTICAL )
1031      {
1032          $labels = $legend->labels;
1033          
1034          // Calculate boundings of each label
1035          if ( $type & ezcGraph::VERTICAL )
1036          {
1037              $labelWidth = $boundings->width;
1038              $labelHeight = min( 
1039                  ( $boundings->height ) / count( $labels ) - $legend->spacing, 
1040                  $legend->symbolSize + 2 * $legend->padding
1041              );
1042          }
1043          else
1044          {
1045              $labelWidth = ( $boundings->width ) / count( $labels ) - $legend->spacing;
1046              $labelHeight = min(
1047                  $boundings->height,
1048                  $legend->symbolSize + 2 * $legend->padding
1049              );
1050          }
1051  
1052          $symbolSize = $labelHeight - 2 * $legend->padding;
1053  
1054          // Draw all labels
1055          $labelPosition = new ezcGraphCoordinate( $boundings->x0, $boundings->y0 );
1056          foreach ( $labels as $label )
1057          {
1058              $this->elements['legend_url'][$label['label']] = $label['url'];
1059  
1060              $this->elements['legend'][$label['label']]['symbol'] = $this->drawSymbol(
1061                  new ezcGraphBoundings(
1062                      $labelPosition->x + $legend->padding,
1063                      $labelPosition->y + $legend->padding,
1064                      $labelPosition->x + $legend->padding + $symbolSize,
1065                      $labelPosition->y + $legend->padding + $symbolSize
1066                  ),
1067                  $label['color'],
1068                  $label['symbol']
1069              );
1070  
1071              $this->elements['legend'][$label['label']]['text'] = $this->driver->drawTextBox(
1072                  $label['label'],
1073                  new ezcGraphCoordinate(
1074                      $labelPosition->x + 2 * $legend->padding + $symbolSize,
1075                      $labelPosition->y + $legend->padding
1076                  ),
1077                  $labelWidth - $symbolSize - 3 * $legend->padding,
1078                  $labelHeight - 2 * $legend->padding,
1079                  ezcGraph::LEFT | ezcGraph::MIDDLE
1080              );
1081  
1082              $labelPosition->x += ( $type === ezcGraph::VERTICAL ? 0 : $labelWidth + $legend->spacing );
1083              $labelPosition->y += ( $type === ezcGraph::VERTICAL ? $labelHeight + $legend->spacing : 0 );
1084          }
1085      }
1086      
1087      /**
1088       * Draw box
1089       *
1090       * Box are wrapping each major chart element and draw border, background
1091       * and title to each chart element.
1092       *
1093       * Optionally a padding and margin for each box can be defined.
1094       * 
1095       * @param ezcGraphBoundings $boundings Boundings of the box
1096       * @param ezcGraphColor $background Background color
1097       * @param ezcGraphColor $borderColor Border color
1098       * @param int $borderWidth Border width
1099       * @param int $margin Margin
1100       * @param int $padding Padding
1101       * @param mixed $title Title of the box
1102       * @param int $titleSize Size of title in the box
1103       * @return ezcGraphBoundings Remaining inner boundings
1104       */
1105      public function drawBox(
1106          ezcGraphBoundings $boundings,
1107          ezcGraphColor $background = null,
1108          ezcGraphColor $borderColor = null,
1109          $borderWidth = 0,
1110          $margin = 0,
1111          $padding = 0,
1112          $title = false,
1113          $titleSize = 16 )
1114      {
1115          // Apply margin
1116          $boundings->x0 += $margin;
1117          $boundings->y0 += $margin;
1118          $boundings->x1 -= $margin;
1119          $boundings->y1 -= $margin;
1120          
1121          if ( $background instanceof ezcGraphColor )
1122          {
1123              // Draw box background
1124              $this->driver->drawPolygon(
1125                  array(
1126                      new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
1127                      new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
1128                      new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
1129                      new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
1130                  ),
1131                  $background,
1132                  true
1133              );
1134          }
1135  
1136          if ( ( $borderColor instanceof ezcGraphColor ) &&
1137               ( $borderWidth > 0 ) )
1138          {
1139              // Draw border
1140              $this->driver->drawPolygon(
1141                  array(
1142                      new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
1143                      new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
1144                      new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
1145                      new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
1146                  ),
1147                  $borderColor,
1148                  false,
1149                  $borderWidth
1150              );
1151  
1152              // Reduce local boundings by borderWidth
1153              $boundings->x0 += $borderWidth;
1154              $boundings->y0 += $borderWidth;
1155              $boundings->x1 -= $borderWidth;
1156              $boundings->y1 -= $borderWidth;
1157          }
1158  
1159          // Apply padding
1160          $boundings->x0 += $padding;
1161          $boundings->y0 += $padding;
1162          $boundings->x1 -= $padding;
1163          $boundings->y1 -= $padding;
1164  
1165          // Add box title
1166          if ( $title !== false )
1167          {
1168              switch ( $this->options->titlePosition )
1169              {
1170                  case ezcGraph::TOP:
1171                      $this->driver->drawTextBox(
1172                          $title,
1173                          new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
1174                          $boundings->width,
1175                          $titleSize,
1176                          $this->options->titleAlignement
1177                      );
1178  
1179                      $boundings->y0 += $titleSize + $padding;
1180                      $boundings->y1 -= $titleSize + $padding;
1181                      break;
1182                  case ezcGraph::BOTTOM:
1183                      $this->driver->drawTextBox(
1184                          $title,
1185                          new ezcGraphCoordinate( $boundings->x0, $boundings->y1 - $titleSize ),
1186                          $boundings->width,
1187                          $titleSize,
1188                          $this->options->titleAlignement
1189                      );
1190  
1191                      $boundings->y1 -= $titleSize + $padding;
1192                      break;
1193              }
1194          }
1195  
1196          return $boundings;
1197      }
1198      
1199      /**
1200       * Draw text
1201       *
1202       * Draws the provided text in the boundings
1203       * 
1204       * @param ezcGraphBoundings $boundings Boundings of text
1205       * @param string $text Text
1206       * @param int $align Alignement of text
1207       * @param ezcGraphRotation $rotation
1208       * @return void
1209       */
1210      public function drawText(
1211          ezcGraphBoundings $boundings,
1212          $text,
1213          $align = ezcGraph::LEFT,
1214          ezcGraphRotation $rotation = null )
1215      {
1216          $this->driver->drawTextBox(
1217              $text,
1218              new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
1219              $boundings->width,
1220              $boundings->height,
1221              $align,
1222              $rotation
1223          );
1224      }
1225  
1226      /**
1227       * Draw grid line
1228       *
1229       * Draw line for the grid in the chart background
1230       * 
1231       * @param ezcGraphCoordinate $start Start point
1232       * @param ezcGraphCoordinate $end End point
1233       * @param ezcGraphColor $color Color of the grid line
1234       * @return void
1235       */
1236      public function drawGridLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
1237      {
1238          $this->driver->drawLine(
1239              $start,
1240              $end,
1241              $color,
1242              1
1243          );
1244      }
1245  
1246      /**
1247       * Draw step line
1248       *
1249       * Draw a step (marker for label position) on a axis.
1250       * 
1251       * @param ezcGraphCoordinate $start Start point
1252       * @param ezcGraphCoordinate $end End point
1253       * @param ezcGraphColor $color Color of the grid line
1254       * @return void
1255       */
1256      public function drawStepLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
1257      {
1258          $this->driver->drawLine(
1259              $start,
1260              $end,
1261              $color,
1262              1
1263          );
1264      }
1265      
1266      /**
1267       * Draw axis
1268       *
1269       * Draws an axis form the provided start point to the end point. A specific 
1270       * angle of the axis is not required.
1271       *
1272       * For the labeleing of the axis a sorted array with major steps and an 
1273       * array with minor steps is expected, which are build like this:
1274       *  array(
1275       *      array(
1276       *          'position' => (float),
1277       *          'label' => (string),
1278       *      )
1279       *  )
1280       * where the label is optional.
1281       *
1282       * The label renderer class defines how the labels are rendered. For more
1283       * documentation on this topic have a look at the basic label renderer 
1284       * class.
1285       *
1286       * Additionally it can be specified if a major and minor grid are rendered 
1287       * by defining a color for them. The axis label is used to add a caption 
1288       * for the axis.
1289       * 
1290       * @param ezcGraphBoundings $boundings Boundings of axis
1291       * @param ezcGraphCoordinate $start Start point of axis
1292       * @param ezcGraphCoordinate $end Endpoint of axis
1293       * @param ezcGraphChartElementAxis $axis Axis to render
1294       * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer
1295       * @return void
1296       */
1297      public function drawAxis(
1298          ezcGraphBoundings $boundings,
1299          ezcGraphCoordinate $start,
1300          ezcGraphCoordinate $end,
1301          ezcGraphChartElementAxis $axis,
1302          ezcGraphAxisLabelRenderer $labelClass = null,
1303          ezcGraphBoundings $innerBoundings = null )
1304      {
1305          // Legacy axis drawing for BC reasons
1306          if ( $innerBoundings === null )
1307          {
1308              return $this->legacyDrawAxis( $boundings, $start, $end, $axis, $labelClass );
1309          }
1310  
1311          // Calculate axis start and end points depending on inner boundings
1312          if ( ( $axis->position === ezcGraph::TOP ) ||
1313               ( $axis->position === ezcGraph::BOTTOM ) )
1314          {
1315              $innerStart = new ezcGraphCoordinate( 
1316                  $start->x + $boundings->x0,
1317                  ( $axis->position === ezcGraph::TOP ? $innerBoundings->y0 : $innerBoundings->y1 )
1318              );
1319              $innerEnd = new ezcGraphCoordinate(
1320                  $end->x   + $boundings->x0,
1321                  ( $axis->position === ezcGraph::TOP ? $innerBoundings->y1 : $innerBoundings->y0 )
1322              );
1323          }
1324          else
1325          {
1326              $innerStart = new ezcGraphCoordinate( 
1327                  ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x0 : $innerBoundings->x1 ),
1328                  $start->y + $boundings->y0
1329              );
1330              $innerEnd = new ezcGraphCoordinate(
1331                  ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x1 : $innerBoundings->x0 ),
1332                  $end->y   + $boundings->y0
1333              );
1334          }
1335  
1336          // Shorten axis, if requested
1337          if ( $this->options->shortAxis )
1338          {
1339              $start = clone $innerStart;
1340              $end   = clone $innerEnd;
1341          }
1342          else
1343          {
1344              $start->x += $boundings->x0;
1345              $start->y += $boundings->y0;
1346              $end->x += $boundings->x0;
1347              $end->y += $boundings->y0;
1348          }
1349  
1350          // Determine normalized direction
1351          $direction = new ezcGraphVector(
1352              $start->x - $end->x,
1353              $start->y - $end->y
1354          );
1355          $direction->unify();
1356  
1357          // Draw axis
1358          $this->driver->drawLine(
1359              $start,
1360              $end,
1361              $axis->border,
1362              1
1363          );
1364  
1365          // Draw small arrowhead
1366          $this->drawAxisArrowHead(
1367              $end,
1368              $direction,
1369              max(
1370                  $axis->minArrowHeadSize,
1371                  min(
1372                      $axis->maxArrowHeadSize,
1373                      abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) )
1374                  )
1375              ),
1376              $axis->border
1377          );
1378  
1379          // Draw axis label, if set
1380          $this->drawAxisLabel( $end, $innerBoundings, $axis );
1381  
1382          // If font should not be synchronized, use font configuration from
1383          // each axis
1384          if ( $this->options->syncAxisFonts === false )
1385          {
1386              $this->driver->options->font = $axis->font;
1387          }
1388  
1389          $labelClass->renderLabels(
1390              $this,
1391              $boundings,
1392              $innerStart,
1393              $innerEnd,
1394              $axis,
1395              $innerBoundings
1396          );
1397      }
1398  
1399      /**
1400       * Draw axis label
1401       *
1402       * Draw labels at the end of an axis.
1403       * 
1404       * @param ezcGraphCoordinate $position 
1405       * @param ezcGraphBoundings $boundings 
1406       * @param ezcGraphChartElementAxis $axis 
1407       * @return void
1408       */
1409      protected function drawAxisLabel( ezcGraphCoordinate $position, ezcGraphBoundings $boundings, ezcGraphChartElementAxis $axis )
1410      {
1411          if ( $axis->label === false )
1412          {
1413              return;
1414          }
1415  
1416          $width = $boundings->width;
1417          switch ( $axis->position )
1418          {
1419              case ezcGraph::TOP:
1420                  $this->driver->drawTextBox(
1421                      $axis->label,
1422                      new ezcGraphCoordinate(
1423                          $position->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ),
1424                          $position->y - $axis->labelMargin - $axis->labelSize
1425                      ),
1426                      $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
1427                      $axis->labelSize,
1428                      ezcGraph::TOP | ezcGraph::RIGHT,
1429                      new ezcGraphRotation( $axis->labelRotation, new ezcGraphCoordinate(
1430                          $position->x + $axis->labelSize / 2,
1431                          $position->y - $axis->labelSize / 2
1432                      ) )
1433                  );
1434                  break;
1435              case ezcGraph::BOTTOM:
1436                  $this->driver->drawTextBox(
1437                      $axis->label,
1438                      new ezcGraphCoordinate(
1439                          $position->x + $axis->labelMargin,
1440                          $position->y + $axis->labelMargin
1441                      ),
1442                      $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
1443                      $axis->labelSize,
1444                      ezcGraph::TOP | ezcGraph::LEFT,
1445                      new ezcGraphRotation( $axis->labelRotation, new ezcGraphCoordinate(
1446                          $position->x + $axis->labelSize / 2,
1447                          $position->y + $axis->labelSize / 2
1448                      ) )
1449                  );
1450                  break;
1451              case ezcGraph::LEFT:
1452                  $this->driver->drawTextBox(
1453                      $axis->label,
1454                      new ezcGraphCoordinate(
1455                          $position->x - $width,
1456                          $position->y - $axis->labelSize - $axis->labelMargin
1457                      ),
1458                      $width - $axis->labelMargin,
1459                      $axis->labelSize,
1460                      ezcGraph::BOTTOM | ezcGraph::RIGHT,
1461                      new ezcGraphRotation( $axis->labelRotation, new ezcGraphCoordinate(
1462                          $position->x - $axis->labelSize / 2,
1463                          $position->y - $axis->labelSize / 2
1464                      ) )
1465                  );
1466                  break;
1467              case ezcGraph::RIGHT:
1468                  $this->driver->drawTextBox(
1469                      $axis->label,
1470                      new ezcGraphCoordinate(
1471                          $position->x,
1472                          $position->y - $axis->labelSize - $axis->labelMargin
1473                      ),
1474                      $width - $axis->labelMargin,
1475                      $axis->labelSize,
1476                      ezcGraph::BOTTOM | ezcGraph::LEFT,
1477                      new ezcGraphRotation( $axis->labelRotation, new ezcGraphCoordinate(
1478                          $position->x + $axis->labelSize / 2,
1479                          $position->y - $axis->labelSize / 2
1480                      ) )
1481                  );
1482                  break;
1483          }
1484      }
1485  
1486      /**
1487       * Draw axis
1488       *
1489       * Draws an axis form the provided start point to the end point. A specific 
1490       * angle of the axis is not required.
1491       *
1492       * For the labeleing of the axis a sorted array with major steps and an 
1493       * array with minor steps is expected, which are build like this:
1494       *  array(
1495       *      array(
1496       *          'position' => (float),
1497       *          'label' => (string),
1498       *      )
1499       *  )
1500       * where the label is optional.
1501       *
1502       * The label renderer class defines how the labels are rendered. For more
1503       * documentation on this topic have a look at the basic label renderer 
1504       * class.
1505       *
1506       * Additionally it can be specified if a major and minor grid are rendered 
1507       * by defining a color for them. The axis label is used to add a caption 
1508       * for the axis.
1509       *
1510       * This function is deprecated and will be removed in favor of its
1511       * reimplementation using the innerBoundings parameter.
1512       * 
1513       * @param ezcGraphBoundings $boundings Boundings of axis
1514       * @param ezcGraphCoordinate $start Start point of axis
1515       * @param ezcGraphCoordinate $end Endpoint of axis
1516       * @param ezcGraphChartElementAxis $axis Axis to render
1517       * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer
1518       * @apichange
1519       * @return void
1520       */
1521      protected function legacyDrawAxis(
1522          ezcGraphBoundings $boundings,
1523          ezcGraphCoordinate $start,
1524          ezcGraphCoordinate $end,
1525          ezcGraphChartElementAxis $axis,
1526          ezcGraphAxisLabelRenderer $labelClass = null )
1527      {
1528          // Store axis space for use by label renderer
1529          switch ( $axis->position )
1530          {
1531              case ezcGraph::TOP:
1532              case ezcGraph::BOTTOM:
1533                  $this->xAxisSpace = ( $boundings->width ) * $axis->axisSpace;
1534                  break;
1535              case ezcGraph::LEFT:
1536              case ezcGraph::RIGHT:
1537                  $this->yAxisSpace = ( $boundings->height ) * $axis->axisSpace;
1538                  break;
1539          }
1540  
1541          // Clone boundings because of internal modifications
1542          $boundings = clone $boundings;
1543  
1544          $start->x += $boundings->x0;
1545          $start->y += $boundings->y0;
1546          $end->x += $boundings->x0;
1547          $end->y += $boundings->y0;
1548  
1549          // Shorten drawn axis, if requested.
1550          if ( ( $this->options->shortAxis === true ) &&
1551               ( ( $axis->position === ezcGraph::TOP ) ||
1552                 ( $axis->position === ezcGraph::BOTTOM ) ) )
1553          {
1554              $axisStart = clone $start;
1555              $axisEnd   = clone $end;
1556  
1557              $axisStart->y += $boundings->height * $axis->axisSpace *
1558                  ( $axis->position === ezcGraph::TOP ? 1 : -1 );
1559              $axisEnd->y   -= $boundings->height * $axis->axisSpace *
1560                  ( $axis->position === ezcGraph::TOP ? 1 : -1 );
1561          }
1562          elseif ( ( $this->options->shortAxis === true ) &&
1563               ( ( $axis->position === ezcGraph::LEFT ) ||
1564                 ( $axis->position === ezcGraph::RIGHT ) ) )
1565          {
1566              $axisStart = clone $start;
1567              $axisEnd   = clone $end;
1568  
1569              $axisStart->x += $boundings->width * $axis->axisSpace *
1570                  ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
1571              $axisEnd->x   -= $boundings->width * $axis->axisSpace *
1572                  ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
1573          }
1574          else
1575          {
1576              $axisStart = $start;
1577              $axisEnd   = $end;
1578          }
1579  
1580          // Determine normalized direction
1581          $direction = new ezcGraphVector(
1582              $start->x - $end->x,
1583              $start->y - $end->y
1584          );
1585          $direction->unify();
1586  
1587          // Draw axis
1588          $this->driver->drawLine(
1589              $axisStart,
1590              $axisEnd,
1591              $axis->border,
1592              1
1593          );
1594  
1595          // Draw small arrowhead
1596          $this->drawAxisArrowHead(
1597              $axisEnd,
1598              $direction,
1599              max(
1600                  $axis->minArrowHeadSize,
1601                  min(
1602                      $axis->maxArrowHeadSize,
1603                      abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) )
1604                  )
1605              ),
1606              $axis->border
1607          );
1608  
1609          // Draw axis label, if set
1610          $this->drawAxisLabel( $end, $boundings, $axis );
1611  
1612          // Collect axis labels and draw, when all axisSpaces are collected
1613          $this->axisLabels[] = array(
1614              'object' => $labelClass,
1615              'boundings' => $boundings,
1616              'start' => clone $start,
1617              'end' => clone $end,
1618              'axis' => $axis,
1619          );
1620  
1621          if ( $this->xAxisSpace && $this->yAxisSpace )
1622          {
1623              $this->drawAxisLabels();
1624          }
1625      }
1626  
1627      /**
1628       * Draw all left axis labels
1629       * 
1630       * @return void
1631       */
1632      protected function drawAxisLabels()
1633      {
1634          foreach ( $this->axisLabels as $nr => $axisLabel )
1635          {
1636              // If font should not be synchronized, use font configuration from
1637              // each axis
1638              if ( $this->options->syncAxisFonts === false )
1639              {
1640                  $this->driver->options->font = $axisLabel['axis']->font;
1641              }
1642  
1643              $start = $axisLabel['start'];
1644              $end = $axisLabel['end'];
1645  
1646              $direction = new ezcGraphVector(
1647                  $end->x - $start->x,
1648                  $end->y - $start->y
1649              );
1650              $direction->unify();
1651  
1652              // Convert elipse to circle for correct angle calculation
1653              $direction->y *= ( $this->xAxisSpace / $this->yAxisSpace );
1654              $angle = $direction->angle( new ezcGraphVector( 0, 1 ) );
1655  
1656              $movement = new ezcGraphVector(
1657                  sin( $angle ) * $this->xAxisSpace * ( $direction->x < 0 ? -1 : 1 ),
1658                  cos( $angle ) * $this->yAxisSpace
1659              );
1660  
1661              $start->x += $movement->x;
1662              $start->y += $movement->y;
1663              $end->x -= $movement->x;
1664              $end->y -= $movement->y;
1665  
1666              $axisLabel['object']->renderLabels(
1667                  $this,
1668                  $axisLabel['boundings'],
1669                  $start,
1670                  $end,
1671                  $axisLabel['axis']
1672              );
1673  
1674              // Prevent from redrawing axis on more then 2 axis.
1675              unset( $this->axisLabels[$nr] );
1676          }
1677      }
1678  
1679      /**
1680       * Draw background image
1681       *
1682       * Draws a background image at the defined position. If repeat is set the
1683       * background image will be repeated like any texture.
1684       * 
1685       * @param ezcGraphBoundings $boundings Boundings for the background image
1686       * @param string $file Filename of background image
1687       * @param int $position Position of background image
1688       * @param int $repeat Type of repetition
1689       * @return void
1690       */
1691      public function drawBackgroundImage(
1692          ezcGraphBoundings $boundings,
1693          $file,
1694          $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE
1695          $repeat = ezcGraph::NO_REPEAT )
1696      {
1697          $imageData = getimagesize( $file );
1698          $imageWidth = $imageData[0];
1699          $imageHeight = $imageData[1];
1700  
1701          $imageWidth = min( $imageWidth, $boundings->width );
1702          $imageHeight = min( $imageHeight, $boundings->height );
1703  
1704          $imagePosition = new ezcGraphCoordinate( 
1705              $boundings->x0, 
1706              $boundings->y0
1707          );
1708  
1709          // Determine x position
1710          switch ( true ) {
1711              case ( $repeat & ezcGraph::HORIZONTAL ):
1712                  // If is repeated on this axis fall back to position zero
1713              case ( $position & ezcGraph::LEFT ):
1714                  $imagePosition->x = $boundings->x0;
1715                  break;
1716              case ( $position & ezcGraph::RIGHT ):
1717                  $imagePosition->x = max( 
1718                      $boundings->x1 - $imageWidth,
1719                      $boundings->x0
1720                  );
1721                  break;
1722              default:
1723                  $imagePosition->x = max(
1724                      $boundings->x0 + ( $boundings->width - $imageWidth ) / 2,
1725                      $boundings->x0
1726                  );
1727                  break;
1728          }
1729  
1730          // Determine y position
1731          switch ( true ) {
1732              case ( $repeat & ezcGraph::VERTICAL ):
1733                  // If is repeated on this axis fall back to position zero
1734              case ( $position & ezcGraph::TOP ):
1735                  $imagePosition->y = $boundings->y0;
1736                  break;
1737              case ( $position & ezcGraph::BOTTOM ):
1738                  $imagePosition->y = max( 
1739                      $boundings->y1 - $imageHeight,
1740                      $boundings->y0
1741                  );
1742                  break;
1743              default:
1744                  $imagePosition->y = max(
1745                      $boundings->y0 + ( $boundings->height - $imageHeight ) / 2,
1746                      $boundings->y0
1747                  );
1748                  break;
1749          }
1750  
1751          // Texturize backround based on position and repetition
1752          $position = new ezcGraphCoordinate(
1753              $imagePosition->x,
1754              $imagePosition->y
1755          );
1756          
1757          do 
1758          {
1759              $position->y = $imagePosition->y;
1760  
1761              do 
1762              {
1763                  $this->driver->drawImage( 
1764                      $file, 
1765                      $position, 
1766                      $imageWidth, 
1767                      $imageHeight 
1768                  );
1769  
1770                  $position->y += $imageHeight;
1771              }
1772              while ( ( $position->y < $boundings->y1 ) &&
1773                      ( $repeat & ezcGraph::VERTICAL ) );
1774              
1775              $position->x += $imageWidth;
1776          }
1777          while ( ( $position->x < $boundings->x1 ) &&
1778                  ( $repeat & ezcGraph::HORIZONTAL ) );
1779      }
1780  
1781      /**
1782       * Call all postprocessing functions
1783       * 
1784       * @return void
1785       */
1786      protected function finish()
1787      {
1788          $this->finishCircleSectors();
1789          $this->finishPieSegmentLabels();
1790          $this->finishLineSymbols();
1791  
1792          return true;
1793      }
1794  
1795      /**
1796       * Reset renderer properties
1797       *
1798       * Reset all renderer properties, which were calculated during the
1799       * rendering process, to offer a clean environment for rerendering.
1800       * 
1801       * @return void
1802       */
1803      protected function resetRenderer()
1804      {
1805          parent::resetRenderer();
1806  
1807          // Also reset special 2D renderer options
1808          $this->pieSegmentLabels = array(
1809              0 => array(),
1810              1 => array(),
1811          );
1812          $this->pieSegmentBoundings = false;
1813          $this->linePostSymbols     = array();
1814          $this->axisLabels          = array();
1815          $this->circleSectors       = array();
1816      }
1817  
1818      /**
1819       * Render odometer chart
1820       * 
1821       * @param ezcGraphBoundings $boundings 
1822       * @param ezcGraphChartElementAxis $axis
1823       * @param ezcGraphOdometerChartOptions $options
1824       * @return ezcGraphBoundings
1825       */
1826      public function drawOdometer( 
1827          ezcGraphBoundings $boundings, 
1828          ezcGraphChartElementAxis $axis,
1829          ezcGraphOdometerChartOptions $options )
1830      {
1831          $height = $boundings->height * $options->odometerHeight;
1832  
1833          // Draw axis
1834          $oldAxisSpace = $axis->axisSpace;
1835          $axis->axisSpace = 0;
1836  
1837          $axis->render( $this, $boundings );
1838  
1839          // Reset axisspaces to correct values
1840          $this->xAxisSpace = $boundings->width * $oldAxisSpace;
1841          $this->yAxisSpace = ( $boundings->height - $height ) / 2;
1842  
1843          $this->drawAxisLabels();
1844  
1845          // Reduce size of chart boundings respecting requested odometer height
1846          $boundings->x0 += $this->xAxisSpace;
1847          $boundings->x1 -= $this->xAxisSpace;
1848          $boundings->y0 += $this->yAxisSpace;
1849          $boundings->y1 -= $this->yAxisSpace;
1850  
1851          $gradient = new ezcGraphLinearGradient(
1852              new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
1853              new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
1854              $options->startColor,
1855              $options->endColor
1856          );
1857  
1858          // Simply draw box with gradient and optional border
1859          $this->drawBox(
1860              $boundings,
1861              $gradient,
1862              $options->borderColor,
1863              $options->borderWidth
1864          );
1865  
1866          // Return modified chart boundings
1867          return $boundings;
1868      }
1869  
1870      /**
1871       * Draw a single odometer marker.
1872       *
1873       * @param ezcGraphBoundings $boundings
1874       * @param ezcGraphCoordinate $position
1875       * @param int $symbol
1876       * @param ezcGraphColor $color
1877       * @param int $width
1878       */
1879      public function drawOdometerMarker(
1880          ezcGraphBoundings $boundings,
1881          ezcGraphCoordinate $position,
1882          $symbol,
1883          ezcGraphColor $color,
1884          $width )
1885      {
1886          $this->driver->drawLine(
1887              new ezcGraphCoordinate(
1888                  $xPos = $boundings->x0 + ( $position->x * $boundings->width ),
1889                  $boundings->y0
1890              ),
1891              new ezcGraphCoordinate(
1892                  $xPos,
1893                  $boundings->y1
1894              ),
1895              $color,
1896              $width
1897          );
1898      }
1899  }
1900  
1901  ?>


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