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 */ 27 class awPlot extends awComponent { 28 29 /** 30 * Values for Y axis 31 * 32 * @var array 33 */ 34 var $datay; 35 36 /** 37 * Values for X axis 38 * 39 * @var array 40 */ 41 var $datax; 42 43 /** 44 * Grid properties 45 * 46 * @var Grid 47 */ 48 var $grid; 49 50 /** 51 * X axis 52 * 53 * @var Axis 54 */ 55 var $xAxis; 56 57 /** 58 * Y axis 59 * 60 * @var Axis 61 */ 62 var $yAxis; 63 64 /** 65 * Position of X axis 66 * 67 * @var int 68 */ 69 var $xAxisPosition = PLOT_BOTTOM; 70 71 /** 72 * Set X axis on zero ? 73 * 74 * @var bool 75 */ 76 var $xAxisZero = TRUE; 77 78 /** 79 * Set Y axis on zero ? 80 * 81 * @var bool 82 */ 83 var $yAxisZero = FALSE; 84 85 /** 86 * Position of Y axis 87 * 88 * @var int 89 */ 90 var $yAxisPosition = PLOT_LEFT; 91 92 /** 93 * Change min value for Y axis 94 * 95 * @var mixed 96 */ 97 var $yMin = NULL; 98 99 /** 100 * Change max value for Y axis 101 * 102 * @var mixed 103 */ 104 var $yMax = NULL; 105 106 /** 107 * Change min value for X axis 108 * 109 * @var mixed 110 */ 111 var $xMin = NULL; 112 113 /** 114 * Change max value for X axis 115 * 116 * @var mixed 117 */ 118 var $xMax = NULL; 119 120 /** 121 * Left axis 122 * 123 * @var int 124 */ 125 126 127 /** 128 * RIGHT axis 129 * 130 * @var int 131 */ 132 133 134 /** 135 * Top axis 136 * 137 * @var int 138 */ 139 140 141 /** 142 * Bottom axis 143 * 144 * @var int 145 */ 146 147 148 /** 149 * Both left/right or top/bottom axis 150 * 151 * @var int 152 */ 153 154 155 /** 156 * Build the plot 157 * 158 */ 159 function awPlot() { 160 161 parent::awComponent(); 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(TICK_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(TICK_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 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 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 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 function setXAxis($axis) { 251 $this->xAxisPosition = $axis; 252 } 253 254 /** 255 * Get X axis 256 * 257 * @return int 258 */ 259 function getXAxis() { 260 return $this->xAxisPosition; 261 } 262 263 /** 264 * Set X axis on zero 265 * 266 * @param bool $zero 267 */ 268 function setXAxisZero($zero) { 269 $this->xAxisZero = (bool)$zero; 270 } 271 272 /** 273 * Set Y axis on zero 274 * 275 * @param bool $zero 276 */ 277 function setYAxisZero($zero) { 278 $this->yAxisZero = (bool)$zero; 279 } 280 281 /** 282 * Change Y axis 283 * 284 * @param int $axis 285 */ 286 function setYAxis($axis) { 287 $this->yAxisPosition = $axis; 288 } 289 290 /** 291 * Get Y axis 292 * 293 * @return int 294 */ 295 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 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 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 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 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 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 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 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 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 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 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 function init($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 462 $this->xAxis->ticks['major']->setNumber($this->getXAxisNumber()); 463 $this->yAxis->ticks['major']->setNumber($this->yAxis->getLabelNumber()); 464 465 466 // Center X axis on zero 467 if($this->xAxisZero) { 468 $this->xAxis->setYCenter($this->yAxis, 0); 469 } 470 471 // Center Y axis on zero 472 if($this->yAxisZero) { 473 $this->yAxis->setXCenter($this->xAxis, 0); 474 } 475 476 // Set axis labels 477 $labels = array(); 478 for($i = 0, $count = $this->getXAxisNumber(); $i < $count; $i++) { 479 $labels[] = $i; 480 } 481 $this->xAxis->label->set($labels); 482 483 parent::init($drawer); 484 485 list($x1, $y1, $x2, $y2) = $this->getPosition(); 486 487 list($leftSpace, $rightSpace) = $this->getSpace($x2 - $x1, $y2 - $y1); 488 489 // Create the grid 490 $this->createGrid(); 491 492 // Draw the grid 493 $this->grid->setSpace($leftSpace, $rightSpace, 0, 0); 494 $this->grid->draw($drawer, $x1, $y1, $x2, $y2); 495 496 } 497 498 function drawEnvelope($drawer) { 499 500 list($x1, $y1, $x2, $y2) = $this->getPosition(); 501 502 if($this->getXCenter()) { 503 $size = $this->xAxis->getDistance(0, 1); 504 $this->xAxis->label->move($size / 2, 0); 505 $this->xAxis->label->hideLast(TRUE); 506 } 507 508 // Draw top axis 509 if($this->xAxisPosition === PLOT_TOP or $this->xAxisPosition === PLOT_BOTH) { 510 $top = $this->xAxis; 511 if($this->xAxisZero === FALSE) { 512 $top->line->setY($y1, $y1); 513 } 514 $top->label->setAlign(NULL, LABEL_TOP); 515 $top->label->move(0, -3); 516 $top->title->move(0, -25); 517 $top->draw($drawer); 518 } 519 520 // Draw bottom axis 521 if($this->xAxisPosition === PLOT_BOTTOM or $this->xAxisPosition === PLOT_BOTH) { 522 $bottom = $this->xAxis; 523 if($this->xAxisZero === FALSE) { 524 $bottom->line->setY($y2, $y2); 525 } 526 $bottom->label->setAlign(NULL, LABEL_BOTTOM); 527 $bottom->label->move(0, 3); 528 $bottom->reverseTickStyle(); 529 $bottom->title->move(0, 25); 530 $bottom->draw($drawer); 531 } 532 533 // Draw left axis 534 if($this->yAxisPosition === PLOT_LEFT or $this->yAxisPosition === PLOT_BOTH) { 535 $left = $this->yAxis; 536 if($this->yAxisZero === FALSE) { 537 $left->line->setX($x1, $x1); 538 } 539 $left->label->setAlign(LABEL_RIGHT); 540 $left->label->move(-6, 0); 541 $left->title->move(-25, 0); 542 $left->draw($drawer); 543 } 544 545 // Draw right axis 546 if($this->yAxisPosition === PLOT_RIGHT or $this->yAxisPosition === PLOT_BOTH) { 547 $right = $this->yAxis; 548 if($this->yAxisZero === FALSE) { 549 $right->line->setX($x2, $x2); 550 } 551 $right->label->setAlign(LABEL_LEFT); 552 $right->label->move(6, 0); 553 $right->reverseTickStyle(); 554 $right->title->move(25, 0); 555 $right->draw($drawer); 556 } 557 558 } 559 560 function createGrid() { 561 562 $max = $this->getRealYMax(); 563 $min = $this->getRealYMin(); 564 565 $number = $this->yAxis->getLabelNumber() - 1; 566 567 if($number < 1) { 568 return; 569 } 570 571 // Horizontal lines of the grid 572 573 $h = array(); 574 for($i = 0; $i <= $number; $i++) { 575 $h[] = $i / $number; 576 } 577 578 // Vertical lines 579 580 $major = $this->yAxis->tick('major'); 581 $interval = $major->getInterval(); 582 $number = $this->getXAxisNumber() - 1; 583 584 $w = array(); 585 586 if($number > 0) { 587 588 for($i = 0; $i <= $number; $i++) { 589 if($i%$interval === 0) { 590 $w[] = $i / $number; 591 } 592 } 593 594 } 595 596 $this->grid->setGrid($w, $h); 597 598 } 599 600 /** 601 * Change values of Y axis 602 * This method ignores not numeric values 603 * 604 * @param array $datay 605 * @param array $datax 606 */ 607 function setValues($datay, $datax = NULL) { 608 609 $this->checkArray($datay); 610 611 foreach($datay as $key => $value) { 612 unset($datay[$key]); 613 $datay[(int)$key] = $value; 614 } 615 616 if($datax === NULL) { 617 $datax = array(); 618 for($i = 0; $i < count($datay); $i++) { 619 $datax[] = $i; 620 } 621 } else { 622 foreach($datax as $key => $value) { 623 unset($datax[$key]); 624 $datax[(int)$key] = $value; 625 } 626 } 627 628 $this->checkArray($datax); 629 630 if(count($datay) === count($datax)) { 631 632 // Set values 633 $this->datay = $datay; 634 $this->datax = $datax; 635 // Update axis with the new awvalues 636 $this->updateAxis(); 637 } else { 638 trigger_error("Plots must have the same number of X and Y points", E_USER_ERROR); 639 } 640 641 } 642 643 /** 644 * Return begin and end values 645 * 646 * @return array 647 */ 648 function getLimit() { 649 650 $i = 0; 651 while(array_key_exists($i, $this->datay) and $this->datay[$i] === NULL) { 652 $i++; 653 } 654 $start = $i; 655 $i = count($this->datay) - 1; 656 while(array_key_exists($i, $this->datay) and $this->datay[$i] === NULL) { 657 $i--; 658 } 659 $stop = $i; 660 661 return array($start, $stop); 662 663 } 664 665 /** 666 * Return TRUE if labels must be centered on X axis, FALSE otherwise 667 * 668 * @return bool 669 */ 670 671 672 function updateAxis() { 673 674 $this->xAxis->setRange( 675 $this->getXMin(), 676 $this->getXMax() 677 ); 678 $this->yAxis->setRange( 679 $this->getRealYMin(), 680 $this->getRealYMax() 681 ); 682 683 } 684 685 function checkArray(&$array) { 686 687 if(is_array($array) === FALSE) { 688 trigger_error("You tried to set a value that is not an array", E_USER_ERROR); 689 } 690 691 foreach($array as $key => $value) { 692 if(is_numeric($value) === FALSE and is_null($value) === FALSE) { 693 trigger_error("Expected numeric values for the plot", E_USER_ERROR); 694 } 695 } 696 697 if(count($array) < 1) { 698 trigger_error("Your plot must have at least 1 value", E_USER_ERROR); 699 } 700 701 } 702 703} 704 705registerClass('Plot', TRUE); 706 707class awPlotAxis { 708 709 /** 710 * Left axis 711 * 712 * @var Axis 713 */ 714 var $left; 715 716 /** 717 * Right axis 718 * 719 * @var Axis 720 */ 721 var $right; 722 723 /** 724 * Top axis 725 * 726 * @var Axis 727 */ 728 var $top; 729 730 /** 731 * Bottom axis 732 * 733 * @var Axis 734 */ 735 var $bottom; 736 737 /** 738 * Build the group of axis 739 */ 740 function awPlotAxis() { 741 742 $this->left = new awAxis; 743 $this->left->auto(TRUE); 744 $this->left->label->setAlign(LABEL_RIGHT); 745 $this->left->label->move(-6, 0); 746 $this->yAxis($this->left); 747 $this->left->setTickStyle(TICK_OUT); 748 $this->left->title->move(-25, 0); 749 750 $this->right = new awAxis; 751 $this->right->auto(TRUE); 752 $this->right->label->setAlign(LABEL_LEFT); 753 $this->right->label->move(6, 0); 754 $this->yAxis($this->right); 755 $this->right->setTickStyle(TICK_IN); 756 $this->right->title->move(25, 0); 757 758 $this->top = new awAxis; 759 $this->top->label->setAlign(NULL, LABEL_TOP); 760 $this->top->label->move(0, -3); 761 $this->xAxis($this->top); 762 $this->top->setTickStyle(TICK_OUT); 763 $this->top->title->move(0, -25); 764 765 $this->bottom = new awAxis; 766 $this->bottom->label->setAlign(NULL, LABEL_BOTTOM); 767 $this->bottom->label->move(0, 3); 768 $this->xAxis($this->bottom); 769 $this->bottom->setTickStyle(TICK_IN); 770 $this->bottom->title->move(0, 25); 771 772 } 773 774 function xAxis(&$axis) { 775 776 $axis->addTick('major', new awTick(0, 5)); 777 $axis->addTick('minor', new awTick(0, 3)); 778 $axis->label->setFont(new awDejaVuSans(7)); 779 780 } 781 782 function yAxis(&$axis) { 783 784 $axis->addTick('major', new awTick(0, 5)); 785 $axis->addTick('minor', new awTick(0, 3)); 786 $axis->setNumberByTick('minor', 'major', 3); 787 $axis->label->setFont(new awDejaVuSans(7)); 788 $axis->title->setAngle(90); 789 790 } 791 792} 793 794registerClass('PlotAxis'); 795 796/** 797 * A graph with axis can contain some groups of components 798 * 799 * @package Artichow 800 */ 801class awPlotGroup extends awComponentGroup { 802 803 /** 804 * Grid properties 805 * 806 * @var Grid 807 */ 808 var $grid; 809 810 /** 811 * Left, right, top and bottom axis 812 * 813 * @var PlotAxis 814 */ 815 var $axis; 816 817 /** 818 * Set the X axis on zero 819 * 820 * @var bool 821 */ 822 var $xAxisZero = TRUE; 823 824 /** 825 * Set the Y axis on zero 826 * 827 * @var bool 828 */ 829 var $yAxisZero = FALSE; 830 831 /** 832 * Real axis used for Y axis 833 * 834 * @var string 835 */ 836 var $yRealAxis = PLOT_LEFT; 837 838 /** 839 * Real axis used for X axis 840 * 841 * @var string 842 */ 843 var $xRealAxis = PLOT_BOTTOM; 844 845 /** 846 * Change min value for Y axis 847 * 848 * @var mixed 849 */ 850 var $yMin = NULL; 851 852 /** 853 * Change max value for Y axis 854 * 855 * @var mixed 856 */ 857 var $yMax = NULL; 858 859 /** 860 * Change min value for X axis 861 * 862 * @var mixed 863 */ 864 var $xMin = NULL; 865 866 /** 867 * Change max value for X axis 868 * 869 * @var mixed 870 */ 871 var $xMax = NULL; 872 873 /** 874 * Build the PlotGroup 875 * 876 */ 877 function awPlotGroup() { 878 879 parent::awComponentGroup(); 880 881 $this->grid = new awGrid; 882 $this->grid->setBackgroundColor(new awWhite); 883 884 $this->axis = new awPlotAxis; 885 886 } 887 888 /** 889 * Set the X axis on zero or not 890 * 891 * @param bool $zero 892 */ 893 function setXAxisZero($zero) { 894 $this->xAxisZero = (bool)$zero; 895 } 896 897 /** 898 * Set the Y axis on zero or not 899 * 900 * @param bool $zero 901 */ 902 function setYAxisZero($zero) { 903 $this->yAxisZero = (bool)$zero; 904 } 905 906 /** 907 * Change min value for Y axis 908 * Set NULL for auto selection. 909 * 910 * @param float $value 911 */ 912 function setYMin($value) { 913 $this->axis->left->auto(FALSE); 914 $this->axis->right->auto(FALSE); 915 $this->yMin = $value; 916 } 917 918 /** 919 * Change max value for Y axis 920 * Set NULL for auto selection. 921 * 922 * @param float $value 923 */ 924 function setYMax($value) { 925 $this->axis->left->auto(FALSE); 926 $this->axis->right->auto(FALSE); 927 $this->yMax = $value; 928 } 929 930 /** 931 * Change min value for X axis 932 * Set NULL for auto selection. 933 * 934 * @param float $value 935 */ 936 function setXMin($value) { 937 $this->xMin = $value; 938 } 939 940 /** 941 * Change max value for X axis 942 * Set NULL for auto selection. 943 * 944 * @param float $value 945 */ 946 function setXMax($value) { 947 $this->xMax = $value; 948 } 949 950 /** 951 * Get min value for X axis 952 * 953 * @return float $value 954 */ 955 function getXMin() { 956 957 return $this->getX('min'); 958 959 } 960 961 /** 962 * Get max value for X axis 963 * 964 * @return float $value 965 */ 966 function getXMax() { 967 968 return $this->getX('max'); 969 970 } 971 972 function getX($type) { 973 974 switch($type) { 975 case 'max' : 976 if($this->xMax !== NULL) { 977 return $this->xMax; 978 } 979 break; 980 case 'min' : 981 if($this->xMin !== NULL) { 982 return $this->xMin; 983 } 984 break; 985 } 986 987 $value = NULL; 988 $get = 'getX'.ucfirst($type); 989 990 for($i = 0; $i < count($this->components); $i++) { 991 992 $component = $this->components[$i]; 993 994 if($value === NULL) { 995 $value = $component->$get(); 996 } else { 997 $value = $type($value, $component->$get()); 998 } 999 1000 } 1001 1002 return $value; 1003 1004 } 1005 1006 /** 1007 * Get min value with spaces for Y axis 1008 * 1009 * @param string $axis Axis name 1010 * @return float $value 1011 */ 1012 function getRealYMin($axis = NULL) { 1013 1014 if($axis === NULL) { 1015 return NULL; 1016 } 1017 1018 $min = $this->getRealY('min', $axis); 1019 $max = $this->getRealY('max', $axis); 1020 1021 if($this->space->bottom !== NULL) { 1022 $interval = ($min - $max) * $this->space->bottom / 100; 1023 return $min + $interval; 1024 } else { 1025 return $min; 1026 } 1027 1028 } 1029 1030 /** 1031 * Get max value with spaces for Y axis 1032 * 1033 * @param string $axis Axis name 1034 * @return float $value 1035 */ 1036 function getRealYMax($axis = NULL) { 1037 1038 if($axis === NULL) { 1039 return NULL; 1040 } 1041 1042 $min = $this->getRealY('min', $axis); 1043 $max = $this->getRealY('max', $axis); 1044 1045 if($this->space->top !== NULL) { 1046 $interval = ($max - $min) * $this->space->top / 100; 1047 return $max + $interval; 1048 } else { 1049 return $max; 1050 } 1051 1052 } 1053 1054 function getRealY($type, $axis) { 1055 1056 switch($type) { 1057 case 'max' : 1058 if($this->yMax !== NULL) { 1059 return $this->yMax; 1060 } 1061 break; 1062 case 'min' : 1063 if($this->yMin !== NULL) { 1064 return $this->yMin; 1065 } 1066 break; 1067 } 1068 1069 $value = NULL; 1070 $get = 'getY'.ucfirst($type); 1071 1072 for($i = 0; $i < count($this->components); $i++) { 1073 1074 $component = $this->components[$i]; 1075 1076 switch($axis) { 1077 1078 case PLOT_LEFT : 1079 case PLOT_RIGHT : 1080 $test = ($component->getYAxis() === $axis); 1081 break; 1082 default : 1083 $test = FALSE; 1084 1085 } 1086 1087 if($test) { 1088 if($value === NULL) { 1089 $value = $component->$get(); 1090 } else { 1091 $value = $type($value, $component->$get()); 1092 } 1093 } 1094 1095 } 1096 1097 return $value; 1098 1099 } 1100 1101 function init($drawer) { 1102 1103 list($x1, $y1, $x2, $y2) = $this->getPosition(); 1104 1105 // Get PlotGroup space 1106 list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1); 1107 1108 // Count values in the group 1109 $values = $this->getXAxisNumber(); 1110 1111 // Init the PlotGroup 1112 $this->axis->top->line->setX($x1, $x2); 1113 $this->axis->bottom->line->setX($x1, $x2); 1114 $this->axis->left->line->setY($y2, $y1); 1115 $this->axis->right->line->setY($y2, $y1); 1116 1117 $this->axis->top->setPadding($leftSpace, $rightSpace); 1118 $this->axis->bottom->setPadding($leftSpace, $rightSpace); 1119 1120 $xMin = $this->getXMin(); 1121 $xMax = $this->getXMax(); 1122 1123 $this->axis->top->setRange($xMin, $xMax); 1124 $this->axis->bottom->setRange($xMin, $xMax); 1125 1126 for($i = 0; $i < count($this->components); $i++) { 1127 1128 1129 $component = &$this->components[$i]; 1130 1131 $component->auto($this->auto); 1132 1133 // Copy space to the component 1134 1135 $component->setSpace($this->space->left, $this->space->right, $this->space->top, $this->space->bottom); 1136 1137 $component->xAxis->setPadding($leftSpace, $rightSpace); 1138 $component->xAxis->line->setX($x1, $x2); 1139 1140 $component->yAxis->line->setY($y2, $y1); 1141 1142 } 1143 1144 // Set Y axis range 1145 foreach(array('left', 'right') as $axis) { 1146 1147 if($this->isAxisUsed($axis)) { 1148 1149 $min = $this->getRealYMin($axis); 1150 $max = $this->getRealYMax($axis); 1151 1152 $interval = $max - $min; 1153 1154 $this->axis->{$axis}->setRange( 1155 $min - $interval * $this->space->bottom / 100, 1156 $max + $interval * $this->space->top / 100 1157 ); 1158 1159 // Auto-scaling mode 1160 $this->axis->{$axis}->autoScale(); 1161 1162 } 1163 1164 } 1165 1166 if($this->axis->left->getLabelNumber() === NULL) { 1167 $number = round(($y2 - $y1) / 75) + 2; 1168 $this->axis->left->setLabelNumber($number); 1169 } 1170 1171 if($this->axis->right->getLabelNumber() === NULL) { 1172 $number = round(($y2 - $y1) / 75) + 2; 1173 $this->axis->right->setLabelNumber($number); 1174 } 1175 1176 // Center labels on X axis if needed 1177 $test = array(PLOT_TOP => FALSE, PLOT_BOTTOM => FALSE); 1178 1179 for($i = 0; $i < count($this->components); $i++) { 1180 1181 1182 $component = &$this->components[$i]; 1183 1184 1185 if($component->getValues() !== NULL) { 1186 1187 $axis = $component->getXAxis(); 1188 1189 if($test[$axis] === FALSE) { 1190 1191 // Center labels for bar plots 1192 if($component->getXCenter()) { 1193 $size = $this->axis->{$axis}->getDistance(0, 1); 1194 $this->axis->{$axis}->label->move($size / 2, 0); 1195 $this->axis->{$axis}->label->hideLast(TRUE); 1196 $test[$axis] = TRUE; 1197 } 1198 1199 } 1200 1201 } 1202 1203 1204 } 1205 1206 // Set axis labels 1207 $labels = array(); 1208 for($i = $xMin; $i <= $xMax; $i++) { 1209 $labels[] = $i; 1210 } 1211 if($this->axis->top->label->count() === 0) { 1212 $this->axis->top->label->set($labels); 1213 } 1214 if($this->axis->bottom->label->count() === 0) { 1215 $this->axis->bottom->label->set($labels); 1216 } 1217 1218 // Set ticks 1219 1220 $this->axis->top->ticks['major']->setNumber($values); 1221 $this->axis->bottom->ticks['major']->setNumber($values); 1222 $this->axis->left->ticks['major']->setNumber($this->axis->left->getLabelNumber()); 1223 $this->axis->right->ticks['major']->setNumber($this->axis->right->getLabelNumber()); 1224 1225 1226 // Set X axis on zero 1227 if($this->xAxisZero) { 1228 $axis = $this->selectYAxis(); 1229 $this->axis->bottom->setYCenter($axis, 0); 1230 $this->axis->top->setYCenter($axis, 0); 1231 } 1232 1233 // Set Y axis on zero 1234 if($this->yAxisZero) { 1235 $axis = $this->selectXAxis(); 1236 $this->axis->left->setXCenter($axis, 1); 1237 $this->axis->right->setXCenter($axis, 1); 1238 } 1239 1240 parent::init($drawer); 1241 1242 list($leftSpace, $rightSpace, $topSpace, $bottomSpace) = $this->getSpace($x2 - $x1, $y2 - $y1); 1243 1244 // Create the grid 1245 $this->createGrid(); 1246 1247 // Draw the grid 1248 $this->grid->setSpace($leftSpace, $rightSpace, 0, 0); 1249 $this->grid->draw($drawer, $x1, $y1, $x2, $y2); 1250 1251 } 1252 1253 function drawComponent($drawer, $x1, $y1, $x2, $y2, $aliasing) { 1254 1255 $xMin = $this->getXMin(); 1256 $xMax = $this->getXMax(); 1257 1258 $maxLeft = $this->getRealYMax(PLOT_LEFT); 1259 $maxRight = $this->getRealYMax(PLOT_RIGHT); 1260 1261 $minLeft = $this->getRealYMin(PLOT_LEFT); 1262 $minRight = $this->getRealYMin(PLOT_RIGHT); 1263 1264 foreach($this->components as $component) { 1265 1266 $min = $component->getYMin(); 1267 $max = $component->getYMax(); 1268 1269 // Set component minimum and maximum 1270 if($component->getYAxis() === PLOT_LEFT) { 1271 1272 list($min, $max) = $this->axis->left->getRange(); 1273 1274 $component->setYMin($min); 1275 $component->setYMax($max); 1276 1277 } else { 1278 1279 list($min, $max) = $this->axis->right->getRange(); 1280 1281 $component->setYMin($min); 1282 $component->setYMax($max); 1283 1284 } 1285 1286 $component->setXAxisZero($this->xAxisZero); 1287 $component->setYAxisZero($this->yAxisZero); 1288 1289 $component->xAxis->setRange($xMin, $xMax); 1290 1291 $component->drawComponent( 1292 $drawer, 1293 $x1, $y1, 1294 $x2, $y2, 1295 $aliasing 1296 ); 1297 1298 $component->setYMin($min); 1299 $component->setYMax($max); 1300 1301 } 1302 1303 } 1304 1305 function drawEnvelope($drawer) { 1306 1307 list($x1, $y1, $x2, $y2) = $this->getPosition(); 1308 1309 // Hide unused axis 1310 foreach(array(PLOT_LEFT, PLOT_RIGHT, PLOT_TOP, PLOT_BOTTOM) as $axis) { 1311 if($this->isAxisUsed($axis) === FALSE) { 1312 $this->axis->{$axis}->hide(TRUE); 1313 } 1314 } 1315 1316 // Draw top axis 1317 $top = $this->axis->top; 1318 if($this->xAxisZero === FALSE) { 1319 $top->line->setY($y1, $y1); 1320 } 1321 $top->draw($drawer); 1322 1323 // Draw bottom axis 1324 $bottom = $this->axis->bottom; 1325 if($this->xAxisZero === FALSE) { 1326 $bottom->line->setY($y2, $y2); 1327 } 1328 $bottom->draw($drawer); 1329 1330 // Draw left axis 1331 $left = $this->axis->left; 1332 if($this->yAxisZero === FALSE) { 1333 $left->line->setX($x1, $x1); 1334 } 1335 $left->draw($drawer); 1336 1337 // Draw right axis 1338 $right = $this->axis->right; 1339 if($this->yAxisZero === FALSE) { 1340 $right->line->setX($x2, $x2); 1341 } 1342 $right->draw($drawer); 1343 1344 } 1345 1346 /** 1347 * Is the specified axis used ? 1348 * 1349 * @param string $axis Axis name 1350 * @return bool 1351 */ 1352 function isAxisUsed($axis) { 1353 1354 for($i = 0; $i < count($this->components); $i++) { 1355 1356 $component = $this->components[$i]; 1357 1358 switch($axis) { 1359 1360 case PLOT_LEFT : 1361 case PLOT_RIGHT : 1362 if($component->getYAxis() === $axis) { 1363 return TRUE; 1364 } 1365 break; 1366 1367 case PLOT_TOP : 1368 case PLOT_BOTTOM : 1369 if($component->getXAxis() === $axis) { 1370 return TRUE; 1371 } 1372 break; 1373 1374 } 1375 1376 } 1377 1378 return FALSE; 1379 1380 } 1381 1382 function createGrid() { 1383 1384 $max = $this->getRealYMax(PLOT_LEFT); 1385 $min = $this->getRealYMin(PLOT_RIGHT); 1386 1387 // Select axis (left if possible, right otherwise) 1388 $axis = $this->selectYAxis(); 1389 1390 $number = $axis->getLabelNumber() - 1; 1391 1392 if($number < 1) { 1393 return; 1394 } 1395 1396 // Horizontal lines of grid 1397 1398 $h = array(); 1399 for($i = 0; $i <= $number; $i++) { 1400 $h[] = $i / $number; 1401 } 1402 1403 // Vertical lines 1404 1405 $major = $axis->tick('major'); 1406 $interval = $major->getInterval(); 1407 $number = $this->getXAxisNumber() - 1; 1408 1409 $w = array(); 1410 1411 if($number > 0) { 1412 1413 for($i = 0; $i <= $number; $i++) { 1414 if($i%$interval === 0) { 1415 $w[] = $i / $number; 1416 } 1417 } 1418 1419 } 1420 1421 $this->grid->setGrid($w, $h); 1422 1423 } 1424 1425 function selectYAxis(){ 1426 1427 // Select axis (left if possible, right otherwise) 1428 if($this->isAxisUsed(PLOT_LEFT)) { 1429 $axis = $this->axis->left; 1430 } else { 1431 $axis = $this->axis->right; 1432 } 1433 1434 return $axis; 1435 1436 } 1437 1438 function selectXAxis(){ 1439 1440 // Select axis (bottom if possible, top otherwise) 1441 if($this->isAxisUsed(PLOT_BOTTOM)) { 1442 $axis = $this->axis->bottom; 1443 } else { 1444 $axis = $this->axis->top; 1445 } 1446 1447 return $axis; 1448 1449 } 1450 1451 function getXAxisNumber() { 1452 $offset = $this->components[0]; 1453 $max = $offset->getXAxisNumber(); 1454 for($i = 1; $i < count($this->components); $i++) { 1455 $offset = $this->components[$i]; 1456 $max = max($max, $offset->getXAxisNumber()); 1457 } 1458 return $max; 1459 } 1460 1461} 1462 1463registerClass('PlotGroup'); 1464?>