1<?php 2/* 3 * This work is hereby released into the Public Domain. 4 * To view a copy of the public domain dedication, 5 * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to 6 * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. 7 * 8 */ 9 10require_once dirname(__FILE__)."/Component.class.php"; 11 12/* <php4> */ 13 14define("PLOT_LEFT", 'left'); 15define("PLOT_RIGHT", 'right'); 16define("PLOT_TOP", 'top'); 17define("PLOT_BOTTOM", 'bottom'); 18define("PLOT_BOTH", 'both'); 19 20/* </php4> */ 21 22/** 23 * Graph using X and Y axis 24 * 25 * @package Artichow 26 */ 27abstract class awPlot extends awComponent { 28 29 /** 30 * Values for Y axis 31 * 32 * @var array 33 */ 34 protected $datay; 35 36 /** 37 * Values for X axis 38 * 39 * @var array 40 */ 41 protected $datax; 42 43 /** 44 * Grid properties 45 * 46 * @var Grid 47 */ 48 public $grid; 49 50 /** 51 * X axis 52 * 53 * @var Axis 54 */ 55 public $xAxis; 56 57 /** 58 * Y axis 59 * 60 * @var Axis 61 */ 62 public $yAxis; 63 64 /** 65 * Position of X axis 66 * 67 * @var int 68 */ 69 protected $xAxisPosition = awPlot::BOTTOM; 70 71 /** 72 * Set X axis on zero ? 73 * 74 * @var bool 75 */ 76 protected $xAxisZero = TRUE; 77 78 /** 79 * Set Y axis on zero ? 80 * 81 * @var bool 82 */ 83 protected $yAxisZero = FALSE; 84 85 /** 86 * Position of Y axis 87 * 88 * @var int 89 */ 90 protected $yAxisPosition = awPlot::LEFT; 91 92 /** 93 * Change min value for Y axis 94 * 95 * @var mixed 96 */ 97 private $yMin = NULL; 98 99 /** 100 * Change max value for Y axis 101 * 102 * @var mixed 103 */ 104 private $yMax = NULL; 105 106 /** 107 * Change min value for X axis 108 * 109 * @var mixed 110 */ 111 private $xMin = NULL; 112 113 /** 114 * Change max value for X axis 115 * 116 * @var mixed 117 */ 118 private $xMax = NULL; 119 120 /** 121 * Left axis 122 * 123 * @var int 124 */ 125 const LEFT = 'left'; 126 127 /** 128 * RIGHT axis 129 * 130 * @var int 131 */ 132 const RIGHT = 'right'; 133 134 /** 135 * Top axis 136 * 137 * @var int 138 */ 139 const TOP = 'top'; 140 141 /** 142 * Bottom axis 143 * 144 * @var int 145 */ 146 const BOTTOM = 'bottom'; 147 148 /** 149 * Both left/right or top/bottom axis 150 * 151 * @var int 152 */ 153 const BOTH = 'both'; 154 155 /** 156 * Build the plot 157 * 158 */ 159 public function __construct() { 160 161 parent::__construct(); 162 163 $this->grid = new awGrid; 164 $this->grid->setBackgroundColor(new awWhite); 165 166 $this->padding->add(20, 0, 0, 20); 167 168 $this->xAxis = new awAxis; 169 $this->xAxis->addTick('major', new awTick(0, 5)); 170 $this->xAxis->addTick('minor', new awTick(0, 3)); 171 $this->xAxis->setTickStyle(awTick::OUT); 172 $this->xAxis->label->setFont(new awDejaVuSans(7)); 173 174 $this->yAxis = new awAxis; 175 $this->yAxis->auto(TRUE); 176 $this->yAxis->addTick('major', new awTick(0, 5)); 177 $this->yAxis->addTick('minor', new awTick(0, 3)); 178 $this->yAxis->setTickStyle(awTick::OUT); 179 $this->yAxis->setNumberByTick('minor', 'major', 3); 180 $this->yAxis->label->setFont(new awDejaVuSans(7)); 181 $this->yAxis->title->setAngle(90); 182 183 } 184 185 /** 186 * Get plot values 187 * 188 * @return array 189 */ 190 public function getValues() { 191 return $this->datay; 192 } 193 194 /** 195 * Reduce number of values in the plot 196 * 197 * @param int $number Reduce number of values to $number 198 */ 199 public function reduce($number) { 200 201 $count = count($this->datay); 202 $ratio = ceil($count / $number); 203 204 if($ratio > 1) { 205 206 $tmpy = $this->datay; 207 $datay = array(); 208 209 $datax = array(); 210 $cbLabel = $this->xAxis->label->getCallbackFunction(); 211 212 for($i = 0; $i < $count; $i += $ratio) { 213 214 $slice = array_slice($tmpy, $i, $ratio); 215 $datay[] = array_sum($slice) / count($slice); 216 217 // Reduce data on X axis if needed 218 if($cbLabel !== NULL) { 219 $datax[] = $cbLabel($i + round($ratio / 2)); 220 } 221 222 } 223 224 $this->setValues($datay); 225 226 if($cbLabel !== NULL) { 227 $this->xAxis->setLabelText($datax); 228 } 229 230 231 } 232 233 } 234 235 /** 236 * Count values in the plot 237 * 238 * @return int 239 */ 240 public function getXAxisNumber() { 241 list($min, $max) = $this->xAxis->getRange(); 242 return ($max - $min + 1); 243 } 244 245 /** 246 * Change X axis 247 * 248 * @param int $axis 249 */ 250 public function setXAxis($axis) { 251 $this->xAxisPosition = $axis; 252 } 253 254 /** 255 * Get X axis 256 * 257 * @return int 258 */ 259 public function getXAxis() { 260 return $this->xAxisPosition; 261 } 262 263 /** 264 * Set X axis on zero 265 * 266 * @param bool $zero 267 */ 268 public function setXAxisZero($zero) { 269 $this->xAxisZero = (bool)$zero; 270 } 271 272 /** 273 * Set Y axis on zero 274 * 275 * @param bool $zero 276 */ 277 public function setYAxisZero($zero) { 278 $this->yAxisZero = (bool)$zero; 279 } 280 281 /** 282 * Change Y axis 283 * 284 * @param int $axis 285 */ 286 public function setYAxis($axis) { 287 $this->yAxisPosition = $axis; 288 } 289 290 /** 291 * Get Y axis 292 * 293 * @return int 294 */ 295 public function getYAxis() { 296 return $this->yAxisPosition; 297 } 298 299 /** 300 * Change min value for Y axis 301 * Set NULL for auto selection. 302 * 303 * @param float $value 304 */ 305 public function setYMin($value) { 306 $this->yMin = $value; 307 $this->yAxis->auto(FALSE); 308 $this->updateAxis(); 309 } 310 311 /** 312 * Change max value for Y axis 313 * Set NULL for auto selection. 314 * 315 * @param float $value 316 */ 317 public function setYMax($value) { 318 $this->yMax = $value; 319 $this->yAxis->auto(FALSE); 320 $this->updateAxis(); 321 } 322 323 /** 324 * Change min value for X axis 325 * Set NULL for auto selection. 326 * 327 * @param float $value 328 */ 329 public function setXMin($value) { 330 $this->xMin = $value; 331 $this->updateAxis(); 332 } 333 334 /** 335 * Change max value for X axis 336 * Set NULL for auto selection. 337 * 338 * @param float $value 339 */ 340 public function setXMax($value) { 341 $this->xMax = $value; 342 $this->updateAxis(); 343 } 344 345 /** 346 * Get min value for Y axis 347 * 348 * @return float $value 349 */ 350 public function getYMin() { 351 if($this->auto) { 352 if(is_null($this->yMin)) { 353 $min = array_min($this->datay); 354 if($min > 0) { 355 return 0; 356 } 357 } 358 } 359 return is_null($this->yMin) ? array_min($this->datay) : (float)$this->yMin; 360 } 361 362 /** 363 * Get max value for Y axis 364 * 365 * @return float $value 366 */ 367 public function getYMax() { 368 if($this->auto) { 369 if(is_null($this->yMax)) { 370 $max = array_max($this->datay); 371 if($max < 0) { 372 return 0; 373 } 374 } 375 } 376 return is_null($this->yMax) ? array_max($this->datay) : (float)$this->yMax; 377 } 378 379 /** 380 * Get min value for X axis 381 * 382 * @return float $value 383 */ 384 public function getXMin() { 385 return floor(is_null($this->xMin) ? array_min($this->datax) : $this->xMin); 386 } 387 388 /** 389 * Get max value for X axis 390 * 391 * @return float $value 392 */ 393 public function getXMax() { 394 return (ceil(is_null($this->xMax) ? array_max($this->datax) : (float)$this->xMax)) + ($this->getXCenter() ? 1 : 0); 395 } 396 397 /** 398 * Get min value with spaces for Y axis 399 * 400 * @return float $value 401 */ 402 public function getRealYMin() { 403 $min = $this->getYMin(); 404 if($this->space->bottom !== NULL) { 405 $interval = ($this->getYMax() - $min) * $this->space->bottom / 100; 406 return $min - $interval; 407 } else { 408 return is_null($this->yMin) ? $min : (float)$this->yMin; 409 } 410 } 411 412 /** 413 * Get max value with spaces for Y axis 414 * 415 * @return float $value 416 */ 417 public function getRealYMax() { 418 $max = $this->getYMax(); 419 if($this->space->top !== NULL) { 420 $interval = ($max - $this->getYMin()) * $this->space->top / 100; 421 return $max + $interval; 422 } else { 423 return is_null($this->yMax) ? $max : (float)$this->yMax; 424 } 425 } 426 427 public function init(awDrawer $drawer) { 428 429 list($x1, $y1, $x2, $y2) = $this->getPosition(); 430 431 // Get space informations 432 list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1); 433 434 $this->xAxis->setPadding($leftSpace, $rightSpace); 435 436 if($this->space->bottom > 0 or $this->space->top > 0) { 437 438 list($min, $max) = $this->yAxis->getRange(); 439 $interval = $max - $min; 440 441 $this->yAxis->setRange( 442 $min - $interval * $this->space->bottom / 100, 443 $max + $interval * $this->space->top / 100 444 ); 445 446 } 447 448 // Auto-scaling mode 449 $this->yAxis->autoScale(); 450 451 // Number of labels is not specified 452 if($this->yAxis->getLabelNumber() === NULL) { 453 $number = round(($y2 - $y1) / 75) + 2; 454 $this->yAxis->setLabelNumber($number); 455 } 456 457 $this->xAxis->line->setX($x1, $x2); 458 $this->yAxis->line->setY($y2, $y1); 459 460 // Set ticks 461 /* <php5> */ 462 $this->xAxis->tick('major')->setNumber($this->getXAxisNumber()); 463 $this->yAxis->tick('major')->setNumber($this->yAxis->getLabelNumber()); 464 /* </php5> */ 465 /* <php4> -- 466 $this->xAxis->ticks['major']->setNumber($this->getXAxisNumber()); 467 $this->yAxis->ticks['major']->setNumber($this->yAxis->getLabelNumber()); 468 -- </php4> */ 469 470 // Center X axis on zero 471 if($this->xAxisZero) { 472 $this->xAxis->setYCenter($this->yAxis, 0); 473 } 474 475 // Center Y axis on zero 476 if($this->yAxisZero) { 477 $this->yAxis->setXCenter($this->xAxis, 0); 478 } 479 480 // Set axis labels 481 $labels = array(); 482 for($i = 0, $count = $this->getXAxisNumber(); $i < $count; $i++) { 483 $labels[] = $i; 484 } 485 $this->xAxis->label->set($labels); 486 487 parent::init($drawer); 488 489 list($x1, $y1, $x2, $y2) = $this->getPosition(); 490 491 list($leftSpace, $rightSpace) = $this->getSpace($x2 - $x1, $y2 - $y1); 492 493 // Create the grid 494 $this->createGrid(); 495 496 // Draw the grid 497 $this->grid->setSpace($leftSpace, $rightSpace, 0, 0); 498 $this->grid->draw($drawer, $x1, $y1, $x2, $y2); 499 500 } 501 502 public function drawEnvelope(awDrawer $drawer) { 503 504 list($x1, $y1, $x2, $y2) = $this->getPosition(); 505 506 if($this->getXCenter()) { 507 $size = $this->xAxis->getDistance(0, 1); 508 $this->xAxis->label->move($size / 2, 0); 509 $this->xAxis->label->hideLast(TRUE); 510 } 511 512 // Draw top axis 513 if($this->xAxisPosition === awPlot::TOP or $this->xAxisPosition === awPlot::BOTH) { 514 $top = clone $this->xAxis; 515 if($this->xAxisZero === FALSE) { 516 $top->line->setY($y1, $y1); 517 } 518 $top->label->setAlign(NULL, awLabel::TOP); 519 $top->label->move(0, -3); 520 $top->title->move(0, -25); 521 $top->draw($drawer); 522 } 523 524 // Draw bottom axis 525 if($this->xAxisPosition === awPlot::BOTTOM or $this->xAxisPosition === awPlot::BOTH) { 526 $bottom = clone $this->xAxis; 527 if($this->xAxisZero === FALSE) { 528 $bottom->line->setY($y2, $y2); 529 } 530 $bottom->label->setAlign(NULL, awLabel::BOTTOM); 531 $bottom->label->move(0, 3); 532 $bottom->reverseTickStyle(); 533 $bottom->title->move(0, 25); 534 $bottom->draw($drawer); 535 } 536 537 // Draw left axis 538 if($this->yAxisPosition === awPlot::LEFT or $this->yAxisPosition === awPlot::BOTH) { 539 $left = clone $this->yAxis; 540 if($this->yAxisZero === FALSE) { 541 $left->line->setX($x1, $x1); 542 } 543 $left->label->setAlign(awLabel::RIGHT); 544 $left->label->move(-6, 0); 545 $left->title->move(-25, 0); 546 $left->draw($drawer); 547 } 548 549 // Draw right axis 550 if($this->yAxisPosition === awPlot::RIGHT or $this->yAxisPosition === awPlot::BOTH) { 551 $right = clone $this->yAxis; 552 if($this->yAxisZero === FALSE) { 553 $right->line->setX($x2, $x2); 554 } 555 $right->label->setAlign(awLabel::LEFT); 556 $right->label->move(6, 0); 557 $right->reverseTickStyle(); 558 $right->title->move(25, 0); 559 $right->draw($drawer); 560 } 561 562 } 563 564 protected function createGrid() { 565 566 $max = $this->getRealYMax(); 567 $min = $this->getRealYMin(); 568 569 $number = $this->yAxis->getLabelNumber() - 1; 570 571 if($number < 1) { 572 return; 573 } 574 575 // Horizontal lines of the grid 576 577 $h = array(); 578 for($i = 0; $i <= $number; $i++) { 579 $h[] = $i / $number; 580 } 581 582 // Vertical lines 583 584 $major = $this->yAxis->tick('major'); 585 $interval = $major->getInterval(); 586 $number = $this->getXAxisNumber() - 1; 587 588 $w = array(); 589 590 if($number > 0) { 591 592 for($i = 0; $i <= $number; $i++) { 593 if($i%$interval === 0) { 594 $w[] = $i / $number; 595 } 596 } 597 598 } 599 600 $this->grid->setGrid($w, $h); 601 602 } 603 604 /** 605 * Change values of Y axis 606 * This method ignores not numeric values 607 * 608 * @param array $datay 609 * @param array $datax 610 */ 611 public function setValues($datay, $datax = NULL) { 612 613 $this->checkArray($datay); 614 615 foreach($datay as $key => $value) { 616 unset($datay[$key]); 617 $datay[(int)$key] = $value; 618 } 619 620 if($datax === NULL) { 621 $datax = array(); 622 for($i = 0; $i < count($datay); $i++) { 623 $datax[] = $i; 624 } 625 } else { 626 foreach($datax as $key => $value) { 627 unset($datax[$key]); 628 $datax[(int)$key] = $value; 629 } 630 } 631 632 $this->checkArray($datax); 633 634 if(count($datay) === count($datax)) { 635 636 // Set values 637 $this->datay = $datay; 638 $this->datax = $datax; 639 // Update axis with the new awvalues 640 $this->updateAxis(); 641 } else { 642 trigger_error("Plots must have the same number of X and Y points", E_USER_ERROR); 643 } 644 645 } 646 647 /** 648 * Return begin and end values 649 * 650 * @return array 651 */ 652 protected function getLimit() { 653 654 $i = 0; 655 while(array_key_exists($i, $this->datay) and $this->datay[$i] === NULL) { 656 $i++; 657 } 658 $start = $i; 659 $i = count($this->datay) - 1; 660 while(array_key_exists($i, $this->datay) and $this->datay[$i] === NULL) { 661 $i--; 662 } 663 $stop = $i; 664 665 return array($start, $stop); 666 667 } 668 669 /** 670 * Return TRUE if labels must be centered on X axis, FALSE otherwise 671 * 672 * @return bool 673 */ 674 abstract public function getXCenter(); 675 676 private function updateAxis() { 677 678 $this->xAxis->setRange( 679 $this->getXMin(), 680 $this->getXMax() 681 ); 682 $this->yAxis->setRange( 683 $this->getRealYMin(), 684 $this->getRealYMax() 685 ); 686 687 } 688 689 private function checkArray(&$array) { 690 691 if(is_array($array) === FALSE) { 692 trigger_error("You tried to set a value that is not an array", E_USER_ERROR); 693 } 694 695 foreach($array as $key => $value) { 696 if(is_numeric($value) === FALSE and is_null($value) === FALSE) { 697 trigger_error("Expected numeric values for the plot", E_USER_ERROR); 698 } 699 } 700 701 if(count($array) < 1) { 702 trigger_error("Your plot must have at least 1 value", E_USER_ERROR); 703 } 704 705 } 706 707} 708 709registerClass('Plot', TRUE); 710 711class awPlotAxis { 712 713 /** 714 * Left axis 715 * 716 * @var Axis 717 */ 718 public $left; 719 720 /** 721 * Right axis 722 * 723 * @var Axis 724 */ 725 public $right; 726 727 /** 728 * Top axis 729 * 730 * @var Axis 731 */ 732 public $top; 733 734 /** 735 * Bottom axis 736 * 737 * @var Axis 738 */ 739 public $bottom; 740 741 /** 742 * Build the group of axis 743 */ 744 public function __construct() { 745 746 $this->left = new awAxis; 747 $this->left->auto(TRUE); 748 $this->left->label->setAlign(awLabel::RIGHT); 749 $this->left->label->move(-6, 0); 750 $this->yAxis($this->left); 751 $this->left->setTickStyle(awTick::OUT); 752 $this->left->title->move(-25, 0); 753 754 $this->right = new awAxis; 755 $this->right->auto(TRUE); 756 $this->right->label->setAlign(awLabel::LEFT); 757 $this->right->label->move(6, 0); 758 $this->yAxis($this->right); 759 $this->right->setTickStyle(awTick::IN); 760 $this->right->title->move(25, 0); 761 762 $this->top = new awAxis; 763 $this->top->label->setAlign(NULL, awLabel::TOP); 764 $this->top->label->move(0, -3); 765 $this->xAxis($this->top); 766 $this->top->setTickStyle(awTick::OUT); 767 $this->top->title->move(0, -25); 768 769 $this->bottom = new awAxis; 770 $this->bottom->label->setAlign(NULL, awLabel::BOTTOM); 771 $this->bottom->label->move(0, 3); 772 $this->xAxis($this->bottom); 773 $this->bottom->setTickStyle(awTick::IN); 774 $this->bottom->title->move(0, 25); 775 776 } 777 778 protected function xAxis(awAxis $axis) { 779 780 $axis->addTick('major', new awTick(0, 5)); 781 $axis->addTick('minor', new awTick(0, 3)); 782 $axis->label->setFont(new awDejaVuSans(7)); 783 784 } 785 786 protected function yAxis(awAxis $axis) { 787 788 $axis->addTick('major', new awTick(0, 5)); 789 $axis->addTick('minor', new awTick(0, 3)); 790 $axis->setNumberByTick('minor', 'major', 3); 791 $axis->label->setFont(new awDejaVuSans(7)); 792 $axis->title->setAngle(90); 793 794 } 795 796} 797 798registerClass('PlotAxis'); 799 800/** 801 * A graph with axis can contain some groups of components 802 * 803 * @package Artichow 804 */ 805class awPlotGroup extends awComponentGroup { 806 807 /** 808 * Grid properties 809 * 810 * @var Grid 811 */ 812 public $grid; 813 814 /** 815 * Left, right, top and bottom axis 816 * 817 * @var PlotAxis 818 */ 819 public $axis; 820 821 /** 822 * Set the X axis on zero 823 * 824 * @var bool 825 */ 826 protected $xAxisZero = TRUE; 827 828 /** 829 * Set the Y axis on zero 830 * 831 * @var bool 832 */ 833 protected $yAxisZero = FALSE; 834 835 /** 836 * Real axis used for Y axis 837 * 838 * @var string 839 */ 840 private $yRealAxis = awPlot::LEFT; 841 842 /** 843 * Real axis used for X axis 844 * 845 * @var string 846 */ 847 private $xRealAxis = awPlot::BOTTOM; 848 849 /** 850 * Change min value for Y axis 851 * 852 * @var mixed 853 */ 854 private $yMin = NULL; 855 856 /** 857 * Change max value for Y axis 858 * 859 * @var mixed 860 */ 861 private $yMax = NULL; 862 863 /** 864 * Change min value for X axis 865 * 866 * @var mixed 867 */ 868 private $xMin = NULL; 869 870 /** 871 * Change max value for X axis 872 * 873 * @var mixed 874 */ 875 private $xMax = NULL; 876 877 /** 878 * Build the PlotGroup 879 * 880 */ 881 public function __construct() { 882 883 parent::__construct(); 884 885 $this->grid = new awGrid; 886 $this->grid->setBackgroundColor(new awWhite); 887 888 $this->axis = new awPlotAxis; 889 890 } 891 892 /** 893 * Set the X axis on zero or not 894 * 895 * @param bool $zero 896 */ 897 public function setXAxisZero($zero) { 898 $this->xAxisZero = (bool)$zero; 899 } 900 901 /** 902 * Set the Y axis on zero or not 903 * 904 * @param bool $zero 905 */ 906 public function setYAxisZero($zero) { 907 $this->yAxisZero = (bool)$zero; 908 } 909 910 /** 911 * Change min value for Y axis 912 * Set NULL for auto selection. 913 * 914 * @param float $value 915 */ 916 public function setYMin($value) { 917 $this->axis->left->auto(FALSE); 918 $this->axis->right->auto(FALSE); 919 $this->yMin = $value; 920 } 921 922 /** 923 * Change max value for Y axis 924 * Set NULL for auto selection. 925 * 926 * @param float $value 927 */ 928 public function setYMax($value) { 929 $this->axis->left->auto(FALSE); 930 $this->axis->right->auto(FALSE); 931 $this->yMax = $value; 932 } 933 934 /** 935 * Change min value for X axis 936 * Set NULL for auto selection. 937 * 938 * @param float $value 939 */ 940 public function setXMin($value) { 941 $this->xMin = $value; 942 } 943 944 /** 945 * Change max value for X axis 946 * Set NULL for auto selection. 947 * 948 * @param float $value 949 */ 950 public function setXMax($value) { 951 $this->xMax = $value; 952 } 953 954 /** 955 * Get min value for X axis 956 * 957 * @return float $value 958 */ 959 public function getXMin() { 960 961 return $this->getX('min'); 962 963 } 964 965 /** 966 * Get max value for X axis 967 * 968 * @return float $value 969 */ 970 public function getXMax() { 971 972 return $this->getX('max'); 973 974 } 975 976 private function getX($type) { 977 978 switch($type) { 979 case 'max' : 980 if($this->xMax !== NULL) { 981 return $this->xMax; 982 } 983 break; 984 case 'min' : 985 if($this->xMin !== NULL) { 986 return $this->xMin; 987 } 988 break; 989 } 990 991 $value = NULL; 992 $get = 'getX'.ucfirst($type); 993 994 for($i = 0; $i < count($this->components); $i++) { 995 996 $component = $this->components[$i]; 997 998 if($value === NULL) { 999 $value = $component->$get(); 1000 } else { 1001 $value = $type($value, $component->$get()); 1002 } 1003 1004 } 1005 1006 return $value; 1007 1008 } 1009 1010 /** 1011 * Get min value with spaces for Y axis 1012 * 1013 * @param string $axis Axis name 1014 * @return float $value 1015 */ 1016 public function getRealYMin($axis = NULL) { 1017 1018 if($axis === NULL) { 1019 return NULL; 1020 } 1021 1022 $min = $this->getRealY('min', $axis); 1023 $max = $this->getRealY('max', $axis); 1024 1025 if($this->space->bottom !== NULL) { 1026 $interval = ($min - $max) * $this->space->bottom / 100; 1027 return $min + $interval; 1028 } else { 1029 return $min; 1030 } 1031 1032 } 1033 1034 /** 1035 * Get max value with spaces for Y axis 1036 * 1037 * @param string $axis Axis name 1038 * @return float $value 1039 */ 1040 public function getRealYMax($axis = NULL) { 1041 1042 if($axis === NULL) { 1043 return NULL; 1044 } 1045 1046 $min = $this->getRealY('min', $axis); 1047 $max = $this->getRealY('max', $axis); 1048 1049 if($this->space->top !== NULL) { 1050 $interval = ($max - $min) * $this->space->top / 100; 1051 return $max + $interval; 1052 } else { 1053 return $max; 1054 } 1055 1056 } 1057 1058 private function getRealY($type, $axis) { 1059 1060 switch($type) { 1061 case 'max' : 1062 if($this->yMax !== NULL) { 1063 return $this->yMax; 1064 } 1065 break; 1066 case 'min' : 1067 if($this->yMin !== NULL) { 1068 return $this->yMin; 1069 } 1070 break; 1071 } 1072 1073 $value = NULL; 1074 $get = 'getY'.ucfirst($type); 1075 1076 for($i = 0; $i < count($this->components); $i++) { 1077 1078 $component = $this->components[$i]; 1079 1080 switch($axis) { 1081 1082 case awPlot::LEFT : 1083 case awPlot::RIGHT : 1084 $test = ($component->getYAxis() === $axis); 1085 break; 1086 default : 1087 $test = FALSE; 1088 1089 } 1090 1091 if($test) { 1092 if($value === NULL) { 1093 $value = $component->$get(); 1094 } else { 1095 $value = $type($value, $component->$get()); 1096 } 1097 } 1098 1099 } 1100 1101 return $value; 1102 1103 } 1104 1105 public function init(awDrawer $drawer) { 1106 1107 list($x1, $y1, $x2, $y2) = $this->getPosition(); 1108 1109 // Get PlotGroup space 1110 list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1); 1111 1112 // Count values in the group 1113 $values = $this->getXAxisNumber(); 1114 1115 // Init the PlotGroup 1116 $this->axis->top->line->setX($x1, $x2); 1117 $this->axis->bottom->line->setX($x1, $x2); 1118 $this->axis->left->line->setY($y2, $y1); 1119 $this->axis->right->line->setY($y2, $y1); 1120 1121 $this->axis->top->setPadding($leftSpace, $rightSpace); 1122 $this->axis->bottom->setPadding($leftSpace, $rightSpace); 1123 1124 $xMin = $this->getXMin(); 1125 $xMax = $this->getXMax(); 1126 1127 $this->axis->top->setRange($xMin, $xMax); 1128 $this->axis->bottom->setRange($xMin, $xMax); 1129 1130 for($i = 0; $i < count($this->components); $i++) { 1131 1132 /* <php5> */ 1133 $component = $this->components[$i]; 1134 /* </php5> */ 1135 /* <php4> -- 1136 $component = &$this->components[$i]; 1137 -- </php4> */ 1138 $component->auto($this->auto); 1139 1140 // Copy space to the component 1141 1142 $component->setSpace($this->space->left, $this->space->right, $this->space->top, $this->space->bottom); 1143 1144 $component->xAxis->setPadding($leftSpace, $rightSpace); 1145 $component->xAxis->line->setX($x1, $x2); 1146 1147 $component->yAxis->line->setY($y2, $y1); 1148 1149 } 1150 1151 // Set Y axis range 1152 foreach(array('left', 'right') as $axis) { 1153 1154 if($this->isAxisUsed($axis)) { 1155 1156 $min = $this->getRealYMin($axis); 1157 $max = $this->getRealYMax($axis); 1158 1159 $interval = $max - $min; 1160 1161 $this->axis->{$axis}->setRange( 1162 $min - $interval * $this->space->bottom / 100, 1163 $max + $interval * $this->space->top / 100 1164 ); 1165 1166 // Auto-scaling mode 1167 $this->axis->{$axis}->autoScale(); 1168 1169 } 1170 1171 } 1172 1173 if($this->axis->left->getLabelNumber() === NULL) { 1174 $number = round(($y2 - $y1) / 75) + 2; 1175 $this->axis->left->setLabelNumber($number); 1176 } 1177 1178 if($this->axis->right->getLabelNumber() === NULL) { 1179 $number = round(($y2 - $y1) / 75) + 2; 1180 $this->axis->right->setLabelNumber($number); 1181 } 1182 1183 // Center labels on X axis if needed 1184 $test = array(awPlot::TOP => FALSE, awPlot::BOTTOM => FALSE); 1185 1186 for($i = 0; $i < count($this->components); $i++) { 1187 1188 /* <php5> */ 1189 $component = $this->components[$i]; 1190 /* </php5> */ 1191 /* <php4> -- 1192 $component = &$this->components[$i]; 1193 -- </php4> */ 1194 1195 if($component->getValues() !== NULL) { 1196 1197 $axis = $component->getXAxis(); 1198 1199 if($test[$axis] === FALSE) { 1200 1201 // Center labels for bar plots 1202 if($component->getXCenter()) { 1203 $size = $this->axis->{$axis}->getDistance(0, 1); 1204 $this->axis->{$axis}->label->move($size / 2, 0); 1205 $this->axis->{$axis}->label->hideLast(TRUE); 1206 $test[$axis] = TRUE; 1207 } 1208 1209 } 1210 1211 } 1212 1213 1214 } 1215 1216 // Set axis labels 1217 $labels = array(); 1218 for($i = $xMin; $i <= $xMax; $i++) { 1219 $labels[] = $i; 1220 } 1221 if($this->axis->top->label->count() === 0) { 1222 $this->axis->top->label->set($labels); 1223 } 1224 if($this->axis->bottom->label->count() === 0) { 1225 $this->axis->bottom->label->set($labels); 1226 } 1227 1228 // Set ticks 1229 /* <php5> */ 1230 $this->axis->top->tick('major')->setNumber($values); 1231 $this->axis->bottom->tick('major')->setNumber($values); 1232 $this->axis->left->tick('major')->setNumber($this->axis->left->getLabelNumber()); 1233 $this->axis->right->tick('major')->setNumber($this->axis->right->getLabelNumber()); 1234 /* </php5> */ 1235 /* <php4> -- 1236 $this->axis->top->ticks['major']->setNumber($values); 1237 $this->axis->bottom->ticks['major']->setNumber($values); 1238 $this->axis->left->ticks['major']->setNumber($this->axis->left->getLabelNumber()); 1239 $this->axis->right->ticks['major']->setNumber($this->axis->right->getLabelNumber()); 1240 -- </php4> */ 1241 1242 // Set X axis on zero 1243 if($this->xAxisZero) { 1244 $axis = $this->selectYAxis(); 1245 $this->axis->bottom->setYCenter($axis, 0); 1246 $this->axis->top->setYCenter($axis, 0); 1247 } 1248 1249 // Set Y axis on zero 1250 if($this->yAxisZero) { 1251 $axis = $this->selectXAxis(); 1252 $this->axis->left->setXCenter($axis, 1); 1253 $this->axis->right->setXCenter($axis, 1); 1254 } 1255 1256 parent::init($drawer); 1257 1258 list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1); 1259 1260 // Create the grid 1261 $this->createGrid(); 1262 1263 // Draw the grid 1264 $this->grid->setSpace($leftSpace, $rightSpace, 0, 0); 1265 $this->grid->draw($drawer, $x1, $y1, $x2, $y2); 1266 1267 } 1268 1269 public function drawComponent(awDrawer $drawer, $x1, $y1, $x2, $y2, $aliasing) { 1270 1271 $xMin = $this->getXMin(); 1272 $xMax = $this->getXMax(); 1273 1274 $maxLeft = $this->getRealYMax(awPlot::LEFT); 1275 $maxRight = $this->getRealYMax(awPlot::RIGHT); 1276 1277 $minLeft = $this->getRealYMin(awPlot::LEFT); 1278 $minRight = $this->getRealYMin(awPlot::RIGHT); 1279 1280 foreach($this->components as $component) { 1281 1282 $min = $component->getYMin(); 1283 $max = $component->getYMax(); 1284 1285 // Set component minimum and maximum 1286 if($component->getYAxis() === awPlot::LEFT) { 1287 1288 list($min, $max) = $this->axis->left->getRange(); 1289 1290 $component->setYMin($min); 1291 $component->setYMax($max); 1292 1293 } else { 1294 1295 list($min, $max) = $this->axis->right->getRange(); 1296 1297 $component->setYMin($min); 1298 $component->setYMax($max); 1299 1300 } 1301 1302 $component->setXAxisZero($this->xAxisZero); 1303 $component->setYAxisZero($this->yAxisZero); 1304 1305 $component->xAxis->setRange($xMin, $xMax); 1306 1307 $component->drawComponent( 1308 $drawer, 1309 $x1, $y1, 1310 $x2, $y2, 1311 $aliasing 1312 ); 1313 1314 $component->setYMin($min); 1315 $component->setYMax($max); 1316 1317 } 1318 1319 } 1320 1321 public function drawEnvelope(awDrawer $drawer) { 1322 1323 list($x1, $y1, $x2, $y2) = $this->getPosition(); 1324 1325 // Hide unused axis 1326 foreach(array(awPlot::LEFT, awPlot::RIGHT, awPlot::TOP, awPlot::BOTTOM) as $axis) { 1327 if($this->isAxisUsed($axis) === FALSE) { 1328 $this->axis->{$axis}->hide(TRUE); 1329 } 1330 } 1331 1332 // Draw top axis 1333 $top = $this->axis->top; 1334 if($this->xAxisZero === FALSE) { 1335 $top->line->setY($y1, $y1); 1336 } 1337 $top->draw($drawer); 1338 1339 // Draw bottom axis 1340 $bottom = $this->axis->bottom; 1341 if($this->xAxisZero === FALSE) { 1342 $bottom->line->setY($y2, $y2); 1343 } 1344 $bottom->draw($drawer); 1345 1346 // Draw left axis 1347 $left = $this->axis->left; 1348 if($this->yAxisZero === FALSE) { 1349 $left->line->setX($x1, $x1); 1350 } 1351 $left->draw($drawer); 1352 1353 // Draw right axis 1354 $right = $this->axis->right; 1355 if($this->yAxisZero === FALSE) { 1356 $right->line->setX($x2, $x2); 1357 } 1358 $right->draw($drawer); 1359 1360 } 1361 1362 /** 1363 * Is the specified axis used ? 1364 * 1365 * @param string $axis Axis name 1366 * @return bool 1367 */ 1368 protected function isAxisUsed($axis) { 1369 1370 for($i = 0; $i < count($this->components); $i++) { 1371 1372 $component = $this->components[$i]; 1373 1374 switch($axis) { 1375 1376 case awPlot::LEFT : 1377 case awPlot::RIGHT : 1378 if($component->getYAxis() === $axis) { 1379 return TRUE; 1380 } 1381 break; 1382 1383 case awPlot::TOP : 1384 case awPlot::BOTTOM : 1385 if($component->getXAxis() === $axis) { 1386 return TRUE; 1387 } 1388 break; 1389 1390 } 1391 1392 } 1393 1394 return FALSE; 1395 1396 } 1397 1398 protected function createGrid() { 1399 1400 $max = $this->getRealYMax(awPlot::LEFT); 1401 $min = $this->getRealYMin(awPlot::RIGHT); 1402 1403 // Select axis (left if possible, right otherwise) 1404 $axis = $this->selectYAxis(); 1405 1406 $number = $axis->getLabelNumber() - 1; 1407 1408 if($number < 1) { 1409 return; 1410 } 1411 1412 // Horizontal lines of grid 1413 1414 $h = array(); 1415 for($i = 0; $i <= $number; $i++) { 1416 $h[] = $i / $number; 1417 } 1418 1419 // Vertical lines 1420 1421 $major = $axis->tick('major'); 1422 $interval = $major->getInterval(); 1423 $number = $this->getXAxisNumber() - 1; 1424 1425 $w = array(); 1426 1427 if($number > 0) { 1428 1429 for($i = 0; $i <= $number; $i++) { 1430 if($i%$interval === 0) { 1431 $w[] = $i / $number; 1432 } 1433 } 1434 1435 } 1436 1437 $this->grid->setGrid($w, $h); 1438 1439 } 1440 1441 protected function selectYAxis(){ 1442 1443 // Select axis (left if possible, right otherwise) 1444 if($this->isAxisUsed(awPlot::LEFT)) { 1445 $axis = $this->axis->left; 1446 } else { 1447 $axis = $this->axis->right; 1448 } 1449 1450 return $axis; 1451 1452 } 1453 1454 protected function selectXAxis(){ 1455 1456 // Select axis (bottom if possible, top otherwise) 1457 if($this->isAxisUsed(awPlot::BOTTOM)) { 1458 $axis = $this->axis->bottom; 1459 } else { 1460 $axis = $this->axis->top; 1461 } 1462 1463 return $axis; 1464 1465 } 1466 1467 protected function getXAxisNumber() { 1468 $offset = $this->components[0]; 1469 $max = $offset->getXAxisNumber(); 1470 for($i = 1; $i < count($this->components); $i++) { 1471 $offset = $this->components[$i]; 1472 $max = max($max, $offset->getXAxisNumber()); 1473 } 1474 return $max; 1475 } 1476 1477} 1478 1479registerClass('PlotGroup'); 1480?>