| [ Index ] |
PHP Cross Reference of MantisBT |
[Summary view] [Print] [Text view]
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 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Jul 28 15:48:31 2011 | Cross-referenced by PHPXref 0.7 |