1<?php 2 3namespace CpChart; 4 5use Exception; 6 7/** 8 * This class exists only to try and reduce the number of methods and properties 9 * in the Draw class. Basically all methods not named 'drawX' were moved in here, 10 * as well as all the class fields. 11 */ 12abstract class BaseDraw 13{ 14 /** 15 * Width of the picture 16 * @var int 17 */ 18 public $XSize; 19 20 /** 21 * Height of the picture 22 * @var int 23 */ 24 public $YSize; 25 26 /** 27 * GD picture object 28 * @var resource 29 */ 30 public $Picture; 31 32 /** 33 * Turn antialias on or off 34 * @var boolean 35 */ 36 public $Antialias = true; 37 38 /** 39 * Quality of the antialiasing implementation (0-1) 40 * @var int 41 */ 42 public $AntialiasQuality = 0; 43 44 /** 45 * Already drawn pixels mask (Filled circle implementation) 46 * @var array 47 */ 48 public $Mask = []; 49 50 /** 51 * Just to know if we need to flush the alpha channels when rendering 52 * @var boolean 53 */ 54 public $TransparentBackground = false; 55 56 /** 57 * Graph area X origin 58 * @var int 59 */ 60 public $GraphAreaX1; 61 62 /** 63 * Graph area Y origin 64 * @var int 65 */ 66 public $GraphAreaY1; 67 68 /** 69 * Graph area bottom right X position 70 * @var int 71 */ 72 public $GraphAreaX2; 73 74 /** 75 * Graph area bottom right Y position 76 * @var int 77 */ 78 public $GraphAreaY2; 79 80 /** 81 * Minimum height for scale divs 82 * @var int 83 */ 84 public $ScaleMinDivHeight = 20; 85 86 /** 87 * @var string 88 */ 89 public $FontName = "GeosansLight.ttf"; 90 91 /** 92 * @var int 93 */ 94 public $FontSize = 12; 95 96 /** 97 * Return the bounding box of the last written string 98 * @var array 99 */ 100 public $FontBox; 101 102 /** 103 * @var int 104 */ 105 public $FontColorR = 0; 106 107 /** 108 * @var int 109 */ 110 public $FontColorG = 0; 111 112 /** 113 * @var int 114 */ 115 public $FontColorB = 0; 116 117 /** 118 * @var int 119 */ 120 public $FontColorA = 100; 121 122 /** 123 * Turn shadows on or off 124 * @var boolean 125 */ 126 public $Shadow = false; 127 128 /** 129 * X Offset of the shadow 130 * @var int 131 */ 132 public $ShadowX; 133 134 /** 135 * Y Offset of the shadow 136 * @var int 137 */ 138 public $ShadowY; 139 140 /** 141 * R component of the shadow 142 * @var int 143 */ 144 public $ShadowR; 145 146 /** 147 * G component of the shadow 148 * @var int 149 */ 150 public $ShadowG; 151 152 /** 153 * B component of the shadow 154 * @var int 155 */ 156 public $ShadowB; 157 158 /** 159 * Alpha level of the shadow 160 * @var int 161 */ 162 public $Shadowa; 163 164 /** 165 * Array containing the image map 166 * @var array 167 */ 168 public $ImageMap = []; 169 170 /** 171 * Name of the session array 172 * @var int 173 */ 174 public $ImageMapIndex = "pChart"; 175 176 /** 177 * Save the current imagemap storage mode 178 * @var int 179 */ 180 public $ImageMapStorageMode; 181 182 /** 183 * Automatic deletion of the image map temp files 184 * @var boolean 185 */ 186 public $ImageMapAutoDelete = true; 187 188 /** 189 * Attached dataset 190 * @var Data 191 */ 192 public $DataSet; 193 194 /** 195 * Last generated chart info 196 * Last layout : regular or stacked 197 * @var int 198 */ 199 public $LastChartLayout = CHART_LAST_LAYOUT_REGULAR; 200 201 /** 202 * @var string 203 */ 204 private $resourcePath; 205 206 public function __construct() 207 { 208 $this->resourcePath = sprintf('%s/../resources', __DIR__); 209 $this->FontName = $this->loadFont($this->FontName, 'fonts'); 210 } 211 212 /** 213 * Set the path to the folder containing library resources (fonts, data, palettes). 214 * 215 * @param string $path 216 * @throws Exception 217 */ 218 public function setResourcePath($path) 219 { 220 $escapedPath = rtrim($path, '/'); 221 if (!file_exists($escapedPath)) { 222 throw new Exception(sprintf( 223 "The path '%s' to resources' folder does not exist!", 224 $escapedPath 225 )); 226 } 227 228 $this->resourcePath = $escapedPath; 229 } 230 231 /** 232 * Check if requested resource exists and return the path to it if yes. 233 * @param string $name 234 * @param string $type 235 * @return string 236 * @throws Exception 237 */ 238 protected function loadFont($name, $type) 239 { 240 if (file_exists($name)) { 241 return $name; 242 } 243 244 $path = sprintf('%s/%s/%s', $this->resourcePath, $type, $name); 245 if (file_exists($path)) { 246 return $path; 247 } 248 249 throw new Exception( 250 sprintf('The requested resource %s (%s) has not been found!', $name, $type) 251 ); 252 } 253 254 /** 255 * Allocate a color with transparency 256 * @param resource $Picture 257 * @param int $R 258 * @param int $G 259 * @param int $B 260 * @param int $Alpha 261 * @return int 262 */ 263 public function allocateColor($Picture, $R, $G, $B, $Alpha = 100) 264 { 265 if ($R < 0) { 266 $R = 0; 267 } if ($R > 255) { 268 $R = 255; 269 } 270 if ($G < 0) { 271 $G = 0; 272 } if ($G > 255) { 273 $G = 255; 274 } 275 if ($B < 0) { 276 $B = 0; 277 } if ($B > 255) { 278 $B = 255; 279 } 280 if ($Alpha < 0) { 281 $Alpha = 0; 282 } 283 if ($Alpha > 100) { 284 $Alpha = 100; 285 } 286 287 $Alpha = $this->convertAlpha($Alpha); 288 return imagecolorallocatealpha($Picture, $R, $G, $B, $Alpha); 289 } 290 291 /** 292 * Convert apha to base 10 293 * @param int|float $AlphaValue 294 * @return integer 295 */ 296 public function convertAlpha($AlphaValue) 297 { 298 return (127 / 100) * (100 - $AlphaValue); 299 } 300 301 /** 302 * @param string $FileName 303 * @return array 304 */ 305 public function getPicInfo($FileName) 306 { 307 $Infos = getimagesize($FileName); 308 $Width = $Infos[0]; 309 $Height = $Infos[1]; 310 $Type = $Infos["mime"]; 311 312 if ($Type == "image/png") { 313 $Type = 1; 314 } 315 if ($Type == "image/gif") { 316 $Type = 2; 317 } 318 if ($Type == "image/jpeg ") { 319 $Type = 3; 320 } 321 322 return [$Width, $Height, $Type]; 323 } 324 325 /** 326 * Compute the scale, check for the best visual factors 327 * @param int $XMin 328 * @param int $XMax 329 * @param int $MaxDivs 330 * @param array $Factors 331 * @param int $AxisID 332 * @return mixed 333 */ 334 public function computeScale($XMin, $XMax, $MaxDivs, array $Factors, $AxisID = 0) 335 { 336 /* Compute each factors */ 337 $Results = []; 338 foreach ($Factors as $Key => $Factor) { 339 $Results[$Factor] = $this->processScale($XMin, $XMax, $MaxDivs, [$Factor], $AxisID); 340 } 341 /* Remove scales that are creating to much decimals */ 342 $GoodScaleFactors = []; 343 foreach ($Results as $Key => $Result) { 344 $Decimals = preg_split("/\./", $Result["RowHeight"]); 345 if ((!isset($Decimals[1])) || (strlen($Decimals[1]) < 6)) { 346 $GoodScaleFactors[] = $Key; 347 } 348 } 349 350 /* Found no correct scale, shame,... returns the 1st one as default */ 351 if (!count($GoodScaleFactors)) { 352 return $Results[$Factors[0]]; 353 } 354 355 /* Find the factor that cause the maximum number of Rows */ 356 $MaxRows = 0; 357 $BestFactor = 0; 358 foreach ($GoodScaleFactors as $Key => $Factor) { 359 if ($Results[$Factor]["Rows"] > $MaxRows) { 360 $MaxRows = $Results[$Factor]["Rows"]; 361 $BestFactor = $Factor; 362 } 363 } 364 365 /* Return the best visual scale */ 366 return $Results[$BestFactor]; 367 } 368 369 /** 370 * Compute the best matching scale based on size & factors 371 * @param int $XMin 372 * @param int $XMax 373 * @param int $MaxDivs 374 * @param array $Factors 375 * @param int $AxisID 376 * @return array 377 */ 378 public function processScale($XMin, $XMax, $MaxDivs, array $Factors, $AxisID) 379 { 380 $ScaleHeight = abs(ceil($XMax) - floor($XMin)); 381 382 $Format = null; 383 if (isset($this->DataSet->Data["Axis"][$AxisID]["Format"])) { 384 $Format = $this->DataSet->Data["Axis"][$AxisID]["Format"]; 385 } 386 387 $Mode = AXIS_FORMAT_DEFAULT; 388 if (isset($this->DataSet->Data["Axis"][$AxisID]["Display"])) { 389 $Mode = $this->DataSet->Data["Axis"][$AxisID]["Display"]; 390 } 391 392 $Scale = []; 393 if ($XMin != $XMax) { 394 $Found = false; 395 $Rescaled = false; 396 $Scaled10Factor = .0001; 397 $Result = 0; 398 while (!$Found) { 399 foreach ($Factors as $Key => $Factor) { 400 if (!$Found) { 401 $XMinRescaled = $XMin; 402 if (!($this->modulo($XMin, $Factor * $Scaled10Factor) == 0) 403 || ($XMin != floor($XMin)) 404 ) { 405 $XMinRescaled = floor($XMin / ($Factor * $Scaled10Factor)) 406 * $Factor 407 * $Scaled10Factor 408 ; 409 } 410 411 $XMaxRescaled = $XMax; 412 if (!($this->modulo($XMax, $Factor * $Scaled10Factor) == 0) 413 || ($XMax != floor($XMax)) 414 ) { 415 $XMaxRescaled = floor($XMax / ($Factor * $Scaled10Factor)) 416 * $Factor 417 * $Scaled10Factor 418 + ($Factor * $Scaled10Factor) 419 ; 420 } 421 422 $ScaleHeightRescaled = abs($XMaxRescaled - $XMinRescaled); 423 424 if (!$Found 425 && floor($ScaleHeightRescaled / ($Factor * $Scaled10Factor)) <= $MaxDivs 426 ) { 427 $Found = true; 428 $Rescaled = true; 429 $Result = $Factor * $Scaled10Factor; 430 } 431 } 432 } 433 $Scaled10Factor = $Scaled10Factor * 10; 434 } 435 436 /* ReCall Min / Max / Height */ 437 if ($Rescaled) { 438 $XMin = $XMinRescaled; 439 $XMax = $XMaxRescaled; 440 $ScaleHeight = $ScaleHeightRescaled; 441 } 442 443 /* Compute rows size */ 444 $Rows = floor($ScaleHeight / $Result); 445 if ($Rows == 0) { 446 $Rows = 1; 447 } 448 $RowHeight = $ScaleHeight / $Rows; 449 450 /* Return the results */ 451 $Scale["Rows"] = $Rows; 452 $Scale["RowHeight"] = $RowHeight; 453 $Scale["XMin"] = $XMin; 454 $Scale["XMax"] = $XMax; 455 456 /* Compute the needed decimals for the metric view to avoid repetition of the same X Axis labels */ 457 if ($Mode == AXIS_FORMAT_METRIC && $Format == null) { 458 $Done = false; 459 $GoodDecimals = 0; 460 for ($Decimals = 0; $Decimals <= 10; $Decimals++) { 461 if (!$Done) { 462 $LastLabel = "zob"; 463 $ScaleOK = true; 464 for ($i = 0; $i <= $Rows; $i++) { 465 $Value = $XMin + $i * $RowHeight; 466 $Label = $this->scaleFormat($Value, AXIS_FORMAT_METRIC, $Decimals); 467 468 if ($LastLabel == $Label) { 469 $ScaleOK = false; 470 } 471 $LastLabel = $Label; 472 } 473 if ($ScaleOK) { 474 $Done = true; 475 $GoodDecimals = $Decimals; 476 } 477 } 478 } 479 $Scale["Format"] = $GoodDecimals; 480 } 481 } else { 482 /* If all values are the same we keep a +1/-1 scale */ 483 $Rows = 2; 484 $XMin = $XMax - 1; 485 $XMax = $XMax + 1; 486 $RowHeight = 1; 487 488 /* Return the results */ 489 $Scale["Rows"] = $Rows; 490 $Scale["RowHeight"] = $RowHeight; 491 $Scale["XMin"] = $XMin; 492 $Scale["XMax"] = $XMax; 493 } 494 495 return $Scale; 496 } 497 498 /** 499 * 500 * @param int|float $Value1 501 * @param int|float $Value2 502 * @return double 503 */ 504 public function modulo($Value1, $Value2) 505 { 506 if (floor($Value2) == 0) { 507 return 0; 508 } 509 if (floor($Value2) != 0) { 510 return $Value1 % $Value2; 511 } 512 513 $MinValue = min($Value1, $Value2); 514 $Factor = 10; 515 while (floor($MinValue * $Factor) == 0) { 516 $Factor = $Factor * 10; 517 } 518 519 return ($Value1 * $Factor) % ($Value2 * $Factor); 520 } 521 522 /** 523 * @param mixed $Value 524 * @param mixed $LastValue 525 * @param integer $LabelingMethod 526 * @param integer $ID 527 * @param boolean $LabelSkip 528 * @return boolean 529 */ 530 public function isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip) 531 { 532 if ($LabelingMethod == LABELING_DIFFERENT && $Value != $LastValue) { 533 return true; 534 } 535 if ($LabelingMethod == LABELING_DIFFERENT && $Value == $LastValue) { 536 return false; 537 } 538 if ($LabelingMethod == LABELING_ALL && $LabelSkip == 0) { 539 return true; 540 } 541 if ($LabelingMethod == LABELING_ALL && ($ID + $LabelSkip) % ($LabelSkip + 1) != 1) { 542 return false; 543 } 544 545 return true; 546 } 547 548 /** 549 * Returns the number of drawable series 550 * @return int 551 */ 552 public function countDrawableSeries() 553 { 554 $count = 0; 555 $Data = $this->DataSet->getData(); 556 557 foreach ($Data["Series"] as $SerieName => $Serie) { 558 if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { 559 $count++; 560 } 561 } 562 563 return $count; 564 } 565 566 /** 567 * Fix box coordinates 568 * @param int $Xa 569 * @param int $Ya 570 * @param int $Xb 571 * @param int $Yb 572 * @return integer[] 573 */ 574 public function fixBoxCoordinates($Xa, $Ya, $Xb, $Yb) 575 { 576 return [min($Xa, $Xb), min($Ya, $Yb), max($Xa, $Xb), max($Ya, $Yb)]; 577 } 578 579 /** 580 * Apply AALias correction to the rounded box boundaries 581 * @param int|float $Value 582 * @param int $Mode 583 * @return int|float 584 */ 585 public function offsetCorrection($Value, $Mode) 586 { 587 $Value = round($Value, 1); 588 589 if ($Value == 0 && $Mode != 1) { 590 return 0; 591 } 592 593 if ($Mode == 1) { 594 if ($Value == .5) { 595 return .5; 596 } 597 if ($Value == .8) { 598 return .6; 599 } 600 if (in_array($Value, [.4, .7])) { 601 return .7; 602 } 603 if (in_array($Value, [.2, .3, .6])) { 604 return .8; 605 } 606 if (in_array($Value, [0, 1, .1, .9])) { 607 return .9; 608 } 609 } 610 611 if ($Mode == 2) { 612 if ($Value == .1) { 613 return .1; 614 } 615 if ($Value == .2) { 616 return .2; 617 } 618 if ($Value == .3) { 619 return .3; 620 } 621 if ($Value == .4) { 622 return .4; 623 } 624 if ($Value == .5) { 625 return .5; 626 } 627 if ($Value == .7) { 628 return .7; 629 } 630 if (in_array($Value, [.6, .8])) { 631 return .8; 632 } 633 if (in_array($Value, [1, .9])) { 634 return .9; 635 } 636 } 637 638 if ($Mode == 3) { 639 if (in_array($Value, [1, .1])) { 640 return .1; 641 } 642 if ($Value == .2) { 643 return .2; 644 } 645 if ($Value == .3) { 646 return .3; 647 } 648 if (in_array($Value, [.4, .8])) { 649 return .4; 650 } 651 if ($Value == .5) { 652 return .9; 653 } 654 if ($Value == .6) { 655 return .6; 656 } 657 if ($Value == .7) { 658 return .7; 659 } 660 if ($Value == .9) { 661 return .5; 662 } 663 } 664 665 if ($Mode == 4) { 666 if ($Value == 1) { 667 return -1; 668 } 669 if (in_array($Value, [.1, .4, .7, .8, .9])) { 670 return .1; 671 } 672 if ($Value == .2) { 673 return .2; 674 } 675 if ($Value == .3) { 676 return .3; 677 } 678 if ($Value == .5) { 679 return -.1; 680 } 681 if ($Value == .6) { 682 return .8; 683 } 684 } 685 } 686 687 /** 688 * Get the legend box size 689 * @param array $Format 690 * @return array 691 */ 692 public function getLegendSize(array $Format = []) 693 { 694 $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName; 695 $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize; 696 $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5; 697 $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL; 698 $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5; 699 $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5; 700 $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth; 701 $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight; 702 $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5; 703 704 $Data = $this->DataSet->getData(); 705 706 foreach ($Data["Series"] as $SerieName => $Serie) { 707 if ($Serie["isDrawable"] == true 708 && $SerieName != $Data["Abscissa"] 709 && isset($Serie["Picture"]) 710 ) { 711 list($PicWidth, $PicHeight) = $this->getPicInfo($Serie["Picture"]); 712 if ($IconAreaWidth < $PicWidth) { 713 $IconAreaWidth = $PicWidth; 714 } 715 if ($IconAreaHeight < $PicHeight) { 716 $IconAreaHeight = $PicHeight; 717 } 718 } 719 } 720 721 $YStep = max($this->FontSize, $IconAreaHeight) + 5; 722 $XStep = $IconAreaWidth + 5; 723 $XStep = $XSpacing; 724 725 $X = 100; 726 $Y = 100; 727 728 $Boundaries = []; 729 $Boundaries["L"] = $X; 730 $Boundaries["T"] = $Y; 731 $Boundaries["R"] = 0; 732 $Boundaries["B"] = 0; 733 $vY = $Y; 734 $vX = $X; 735 foreach ($Data["Series"] as $SerieName => $Serie) { 736 if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) { 737 if ($Mode == LEGEND_VERTICAL) { 738 $BoxArray = $this->getTextBox( 739 $vX + $IconAreaWidth + 4, 740 $vY + $IconAreaHeight / 2, 741 $FontName, 742 $FontSize, 743 0, 744 $Serie["Description"] 745 ); 746 747 if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { 748 $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; 749 } 750 if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { 751 $Boundaries["R"] = $BoxArray[1]["X"] + 2; 752 } 753 if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { 754 $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; 755 } 756 757 $Lines = preg_split("/\n/", $Serie["Description"]); 758 $vY = $vY + max($this->FontSize * count($Lines), $IconAreaHeight) + 5; 759 } elseif ($Mode == LEGEND_HORIZONTAL) { 760 $Lines = preg_split("/\n/", $Serie["Description"]); 761 $Width = []; 762 foreach ($Lines as $Key => $Value) { 763 $BoxArray = $this->getTextBox( 764 $vX + $IconAreaWidth + 6, 765 $Y + $IconAreaHeight / 2 + (($this->FontSize + 3) * $Key), 766 $FontName, 767 $FontSize, 768 0, 769 $Value 770 ); 771 772 if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) { 773 $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2; 774 } 775 if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) { 776 $Boundaries["R"] = $BoxArray[1]["X"] + 2; 777 } 778 if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) { 779 $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2; 780 } 781 782 $Width[] = $BoxArray[1]["X"]; 783 } 784 785 $vX = max($Width) + $XStep; 786 } 787 } 788 } 789 $vY = $vY - $YStep; 790 $vX = $vX - $XStep; 791 792 $TopOffset = $Y - $Boundaries["T"]; 793 if ($Boundaries["B"] - ($vY + $IconAreaHeight) < $TopOffset) { 794 $Boundaries["B"] = $vY + $IconAreaHeight + $TopOffset; 795 } 796 797 $Width = ($Boundaries["R"] + $Margin) - ($Boundaries["L"] - $Margin); 798 $Height = ($Boundaries["B"] + $Margin) - ($Boundaries["T"] - $Margin); 799 800 return ["Width" => $Width, "Height" => $Height]; 801 } 802 803 /** 804 * Return the abscissa margin 805 * @param array $Data 806 * @return int 807 */ 808 public function getAbscissaMargin(array $Data) 809 { 810 foreach ($Data["Axis"] as $Values) { 811 if ($Values["Identity"] == AXIS_X) { 812 return $Values["Margin"]; 813 } 814 } 815 return 0; 816 } 817 818 /** 819 * Returns a random color 820 * @param int $Alpha 821 * @return array 822 */ 823 public function getRandomColor($Alpha = 100) 824 { 825 return [ 826 "R" => rand(0, 255), 827 "G" => rand(0, 255), 828 "B" => rand(0, 255), 829 "Alpha" => $Alpha 830 ]; 831 } 832 833 /** 834 * Validate a palette 835 * @param mixed $Colors 836 * @param int|float $Surrounding 837 * @return array 838 */ 839 public function validatePalette($Colors, $Surrounding = null) 840 { 841 $Result = []; 842 843 if (!is_array($Colors)) { 844 return $this->getRandomColor(); 845 } 846 847 foreach ($Colors as $Key => $Values) { 848 if (isset($Values["R"])) { 849 $Result[$Key]["R"] = $Values["R"]; 850 } else { 851 $Result[$Key]["R"] = rand(0, 255); 852 } 853 854 if (isset($Values["G"])) { 855 $Result[$Key]["G"] = $Values["G"]; 856 } else { 857 $Result[$Key]["G"] = rand(0, 255); 858 } 859 860 if (isset($Values["B"])) { 861 $Result[$Key]["B"] = $Values["B"]; 862 } else { 863 $Result[$Key]["B"] = rand(0, 255); 864 } 865 if (isset($Values["Alpha"])) { 866 $Result[$Key]["Alpha"] = $Values["Alpha"]; 867 } else { 868 $Result[$Key]["Alpha"] = 100; 869 } 870 871 if (null !== $Surrounding) { 872 $Result[$Key]["BorderR"] = $Result[$Key]["R"] + $Surrounding; 873 $Result[$Key]["BorderG"] = $Result[$Key]["G"] + $Surrounding; 874 $Result[$Key]["BorderB"] = $Result[$Key]["B"] + $Surrounding; 875 } else { 876 if (isset($Values["BorderR"])) { 877 $Result[$Key]["BorderR"] = $Values["BorderR"]; 878 } else { 879 $Result[$Key]["BorderR"] = $Result[$Key]["R"]; 880 } 881 if (isset($Values["BorderG"])) { 882 $Result[$Key]["BorderG"] = $Values["BorderG"]; 883 } else { 884 $Result[$Key]["BorderG"] = $Result[$Key]["G"]; 885 } 886 if (isset($Values["BorderB"])) { 887 $Result[$Key]["BorderB"] = $Values["BorderB"]; 888 } else { 889 $Result[$Key]["BorderB"] = $Result[$Key]["B"]; 890 } 891 if (isset($Values["BorderAlpha"])) { 892 $Result[$Key]["BorderAlpha"] = $Values["BorderAlpha"]; 893 } else { 894 $Result[$Key]["BorderAlpha"] = $Result[$Key]["Alpha"]; 895 } 896 } 897 } 898 899 return $Result; 900 } 901 902 /** 903 * @param mixed $Values 904 * @param array $Option 905 * @param boolean $ReturnOnly0Height 906 * @return int|float|array 907 */ 908 public function scaleComputeY($Values, array $Option = [], $ReturnOnly0Height = false) 909 { 910 $AxisID = isset($Option["AxisID"]) ? $Option["AxisID"] : 0; 911 $SerieName = isset($Option["SerieName"]) ? $Option["SerieName"] : null; 912 913 $Data = $this->DataSet->getData(); 914 if (!isset($Data["Axis"][$AxisID])) { 915 return -1; 916 } 917 918 if ($SerieName != null) { 919 $AxisID = $Data["Series"][$SerieName]["Axis"]; 920 } 921 if (!is_array($Values)) { 922 $tmp = $Values; 923 $Values = []; 924 $Values[0] = $tmp; 925 } 926 927 $Result = []; 928 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { 929 $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Data["Axis"][$AxisID]["Margin"] * 2; 930 $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; 931 $Step = $Height / $ScaleHeight; 932 933 if ($ReturnOnly0Height) { 934 foreach ($Values as $Key => $Value) { 935 if ($Value == VOID) { 936 $Result[] = VOID; 937 } else { 938 $Result[] = $Step * $Value; 939 } 940 } 941 } else { 942 foreach ($Values as $Key => $Value) { 943 if ($Value == VOID) { 944 $Result[] = VOID; 945 } else { 946 $Result[] = $this->GraphAreaY2 947 - $Data["Axis"][$AxisID]["Margin"] 948 - ($Step * ($Value - $Data["Axis"][$AxisID]["ScaleMin"])) 949 ; 950 } 951 } 952 } 953 } else { 954 $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Data["Axis"][$AxisID]["Margin"] * 2; 955 $ScaleWidth = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"]; 956 $Step = $Width / $ScaleWidth; 957 958 if ($ReturnOnly0Height) { 959 foreach ($Values as $Key => $Value) { 960 if ($Value == VOID) { 961 $Result[] = VOID; 962 } else { 963 $Result[] = $Step * $Value; 964 } 965 } 966 } else { 967 foreach ($Values as $Key => $Value) { 968 if ($Value == VOID) { 969 $Result[] = VOID; 970 } else { 971 $Result[] = $this->GraphAreaX1 972 + $Data["Axis"][$AxisID]["Margin"] 973 + ($Step * ($Value - $Data["Axis"][$AxisID]["ScaleMin"])) 974 ; 975 } 976 } 977 } 978 } 979 return count($Result) == 1 ? reset($Result) : $Result; 980 } 981 982 /** 983 * Format the axis values 984 * @param mixed $Value 985 * @param int $Mode 986 * @param array $Format 987 * @param string $Unit 988 * @return string 989 */ 990 public function scaleFormat($Value, $Mode = null, $Format = null, $Unit = null) 991 { 992 if ($Value == VOID) { 993 return ""; 994 } 995 996 if ($Mode == AXIS_FORMAT_TRAFFIC) { 997 if ($Value == 0) { 998 return "0B"; 999 } 1000 $Units = ["B", "KB", "MB", "GB", "TB", "PB"]; 1001 $Sign = ""; 1002 if ($Value < 0) { 1003 $Value = abs($Value); 1004 $Sign = "-"; 1005 } 1006 1007 $Value = number_format($Value / pow(1024, ($Scale = floor(log($Value, 1024)))), 2, ",", "."); 1008 return $Sign . $Value . " " . $Units[$Scale]; 1009 } 1010 1011 if ($Mode == AXIS_FORMAT_CUSTOM) { 1012 if (is_callable($Format)) { 1013 return call_user_func($Format, $Value); 1014 } 1015 } 1016 1017 if ($Mode == AXIS_FORMAT_DATE) { 1018 $Pattern = "d/m/Y"; 1019 if ($Format !== null) { 1020 $Pattern = $Format; 1021 } 1022 1023 return gmdate($Pattern, $Value); 1024 } 1025 1026 if ($Mode == AXIS_FORMAT_TIME) { 1027 $Pattern = "H:i:s"; 1028 if ($Format !== null) { 1029 $Pattern = $Format; 1030 } 1031 1032 return gmdate($Pattern, $Value); 1033 } 1034 1035 if ($Mode == AXIS_FORMAT_CURRENCY) { 1036 return $Format . number_format($Value, 2); 1037 } 1038 1039 if ($Mode == AXIS_FORMAT_METRIC) { 1040 if (abs($Value) > 1000000000) { 1041 return round($Value / 1000000000, $Format) . "g" . $Unit; 1042 } 1043 if (abs($Value) > 1000000) { 1044 return round($Value / 1000000, $Format) . "m" . $Unit; 1045 } elseif (abs($Value) >= 1000) { 1046 return round($Value / 1000, $Format) . "k" . $Unit; 1047 } 1048 } 1049 return $Value . $Unit; 1050 } 1051 1052 /** 1053 * @return array|null 1054 */ 1055 public function scaleGetXSettings() 1056 { 1057 $Data = $this->DataSet->getData(); 1058 foreach ($Data["Axis"] as $Settings) { 1059 if ($Settings["Identity"] == AXIS_X) { 1060 return [$Settings["Margin"], $Settings["Rows"]]; 1061 } 1062 } 1063 } 1064 1065 /** 1066 * Write Max value on a chart 1067 * @param int $Type 1068 * @param array $Format 1069 */ 1070 public function writeBounds($Type = BOUND_BOTH, $Format = null) 1071 { 1072 $MaxLabelTxt = isset($Format["MaxLabelTxt"]) ? $Format["MaxLabelTxt"] : "max="; 1073 $MinLabelTxt = isset($Format["MinLabelTxt"]) ? $Format["MinLabelTxt"] : "min="; 1074 $Decimals = isset($Format["Decimals"]) ? $Format["Decimals"] : 1; 1075 $ExcludedSeries = isset($Format["ExcludedSeries"]) ? $Format["ExcludedSeries"] : ""; 1076 $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4; 1077 $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL; 1078 $MaxDisplayR = isset($Format["MaxDisplayR"]) ? $Format["MaxDisplayR"] : 0; 1079 $MaxDisplayG = isset($Format["MaxDisplayG"]) ? $Format["MaxDisplayG"] : 0; 1080 $MaxDisplayB = isset($Format["MaxDisplayB"]) ? $Format["MaxDisplayB"] : 0; 1081 $MinDisplayR = isset($Format["MinDisplayR"]) ? $Format["MinDisplayR"] : 255; 1082 $MinDisplayG = isset($Format["MinDisplayG"]) ? $Format["MinDisplayG"] : 255; 1083 $MinDisplayB = isset($Format["MinDisplayB"]) ? $Format["MinDisplayB"] : 255; 1084 $MinLabelPos = isset($Format["MinLabelPos"]) ? $Format["MinLabelPos"] : BOUND_LABEL_POS_AUTO; 1085 $MaxLabelPos = isset($Format["MaxLabelPos"]) ? $Format["MaxLabelPos"] : BOUND_LABEL_POS_AUTO; 1086 $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : true; 1087 $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : false; 1088 $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5; 1089 $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : true; 1090 $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3; 1091 $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0; 1092 $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0; 1093 $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0; 1094 $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20; 1095 $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : ""; 1096 $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255; 1097 $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255; 1098 $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255; 1099 $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100; 1100 1101 $CaptionSettings = [ 1102 "DrawBox" => $DrawBox, 1103 "DrawBoxBorder" => $DrawBoxBorder, 1104 "BorderOffset" => $BorderOffset, 1105 "BoxRounded" => $BoxRounded, 1106 "RoundedRadius" => $RoundedRadius, 1107 "BoxR" => $BoxR, 1108 "BoxG" => $BoxG, 1109 "BoxB" => $BoxB, 1110 "BoxAlpha" => $BoxAlpha, 1111 "BoxSurrounding" => $BoxSurrounding, 1112 "BoxBorderR" => $BoxBorderR, 1113 "BoxBorderG" => $BoxBorderG, 1114 "BoxBorderB" => $BoxBorderB, 1115 "BoxBorderAlpha" => $BoxBorderAlpha 1116 ]; 1117 1118 list($XMargin, $XDivs) = $this->scaleGetXSettings(); 1119 1120 $Data = $this->DataSet->getData(); 1121 foreach ($Data["Series"] as $SerieName => $Serie) { 1122 if ($Serie["isDrawable"] == true 1123 && $SerieName != $Data["Abscissa"] 1124 && !isset($ExcludedSeries[$SerieName]) 1125 ) { 1126 $R = $Serie["Color"]["R"]; 1127 $G = $Serie["Color"]["G"]; 1128 $B = $Serie["Color"]["B"]; 1129 1130 $MinValue = $this->DataSet->getMin($SerieName); 1131 $MaxValue = $this->DataSet->getMax($SerieName); 1132 1133 $MinPos = VOID; 1134 $MaxPos = VOID; 1135 foreach ($Serie["Data"] as $Key => $Value) { 1136 if ($Value == $MinValue && $MinPos == VOID) { 1137 $MinPos = $Key; 1138 } 1139 if ($Value == $MaxValue) { 1140 $MaxPos = $Key; 1141 } 1142 } 1143 1144 $AxisID = $Serie["Axis"]; 1145 $Mode = $Data["Axis"][$AxisID]["Display"]; 1146 $Format = $Data["Axis"][$AxisID]["Format"]; 1147 $Unit = $Data["Axis"][$AxisID]["Unit"]; 1148 1149 $PosArray = $this->scaleComputeY( 1150 $Serie["Data"], 1151 ["AxisID" => $Serie["Axis"]] 1152 ); 1153 1154 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { 1155 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; 1156 $X = $this->GraphAreaX1 + $XMargin; 1157 $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0; 1158 1159 if ($Type == BOUND_MAX || $Type == BOUND_BOTH) { 1160 if ($MaxLabelPos == BOUND_LABEL_POS_TOP 1161 || ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) 1162 ) { 1163 $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; 1164 $Align = TEXT_ALIGN_BOTTOMMIDDLE; 1165 } 1166 if ($MaxLabelPos == BOUND_LABEL_POS_BOTTOM 1167 || ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) 1168 ) { 1169 $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; 1170 $Align = TEXT_ALIGN_TOPMIDDLE; 1171 } 1172 1173 $XPos = $X + $MaxPos * $XStep + $SerieOffset; 1174 $Label = sprintf( 1175 '%s%s', 1176 $MaxLabelTxt, 1177 $this->scaleFormat(round($MaxValue, $Decimals), $Mode, $Format, $Unit) 1178 ); 1179 1180 $TxtPos = $this->getTextBox($XPos, $YPos, $this->FontName, $this->FontSize, 0, $Label); 1181 $XOffset = 0; 1182 $YOffset = 0; 1183 if ($TxtPos[0]["X"] < $this->GraphAreaX1) { 1184 $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"]) / 2); 1185 } 1186 if ($TxtPos[1]["X"] > $this->GraphAreaX2) { 1187 $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2) / 2); 1188 } 1189 if ($TxtPos[2]["Y"] < $this->GraphAreaY1) { 1190 $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; 1191 } 1192 if ($TxtPos[0]["Y"] > $this->GraphAreaY2) { 1193 $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); 1194 } 1195 1196 $CaptionSettings["R"] = $MaxDisplayR; 1197 $CaptionSettings["G"] = $MaxDisplayG; 1198 $CaptionSettings["B"] = $MaxDisplayB; 1199 $CaptionSettings["Align"] = $Align; 1200 1201 $this->drawText($XPos + $XOffset, $YPos + $YOffset, $Label, $CaptionSettings); 1202 } 1203 1204 if ($Type == BOUND_MIN || $Type == BOUND_BOTH) { 1205 if ($MinLabelPos == BOUND_LABEL_POS_TOP 1206 || ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) 1207 ) { 1208 $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; 1209 $Align = TEXT_ALIGN_BOTTOMMIDDLE; 1210 } 1211 if ($MinLabelPos == BOUND_LABEL_POS_BOTTOM 1212 || ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) 1213 ) { 1214 $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; 1215 $Align = TEXT_ALIGN_TOPMIDDLE; 1216 } 1217 1218 $XPos = $X + $MinPos * $XStep + $SerieOffset; 1219 $Label = sprintf( 1220 '%s%s', 1221 $MinLabelTxt, 1222 $this->scaleFormat(round($MinValue, $Decimals), $Mode, $Format, $Unit) 1223 ); 1224 1225 $TxtPos = $this->getTextBox($XPos, $YPos, $this->FontName, $this->FontSize, 0, $Label); 1226 $XOffset = 0; 1227 $YOffset = 0; 1228 if ($TxtPos[0]["X"] < $this->GraphAreaX1) { 1229 $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"]) / 2); 1230 } 1231 if ($TxtPos[1]["X"] > $this->GraphAreaX2) { 1232 $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2) / 2); 1233 } 1234 if ($TxtPos[2]["Y"] < $this->GraphAreaY1) { 1235 $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; 1236 } 1237 if ($TxtPos[0]["Y"] > $this->GraphAreaY2) { 1238 $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); 1239 } 1240 1241 $CaptionSettings["R"] = $MinDisplayR; 1242 $CaptionSettings["G"] = $MinDisplayG; 1243 $CaptionSettings["B"] = $MinDisplayB; 1244 $CaptionSettings["Align"] = $Align; 1245 1246 $this->drawText( 1247 $XPos + $XOffset, 1248 $YPos - $DisplayOffset + $YOffset, 1249 $Label, 1250 $CaptionSettings 1251 ); 1252 } 1253 } else { 1254 $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; 1255 $X = $this->GraphAreaY1 + $XMargin; 1256 $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0; 1257 1258 if ($Type == BOUND_MAX || $Type == BOUND_BOTH) { 1259 if ($MaxLabelPos == BOUND_LABEL_POS_TOP 1260 || ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) 1261 ) { 1262 $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; 1263 $Align = TEXT_ALIGN_MIDDLELEFT; 1264 } 1265 if ($MaxLabelPos == BOUND_LABEL_POS_BOTTOM 1266 || ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) 1267 ) { 1268 $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; 1269 $Align = TEXT_ALIGN_MIDDLERIGHT; 1270 } 1271 1272 $XPos = $X + $MaxPos * $XStep + $SerieOffset; 1273 $Label = $MaxLabelTxt . $this->scaleFormat($MaxValue, $Mode, $Format, $Unit); 1274 1275 $TxtPos = $this->getTextBox($YPos, $XPos, $this->FontName, $this->FontSize, 0, $Label); 1276 $XOffset = 0; 1277 $YOffset = 0; 1278 if ($TxtPos[0]["X"] < $this->GraphAreaX1) { 1279 $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; 1280 } 1281 if ($TxtPos[1]["X"] > $this->GraphAreaX2) { 1282 $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); 1283 } 1284 if ($TxtPos[2]["Y"] < $this->GraphAreaY1) { 1285 $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"]) / 2; 1286 } 1287 if ($TxtPos[0]["Y"] > $this->GraphAreaY2) { 1288 $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2) / 2); 1289 } 1290 1291 $CaptionSettings["R"] = $MaxDisplayR; 1292 $CaptionSettings["G"] = $MaxDisplayG; 1293 $CaptionSettings["B"] = $MaxDisplayB; 1294 $CaptionSettings["Align"] = $Align; 1295 1296 $this->drawText($YPos + $XOffset, $XPos + $YOffset, $Label, $CaptionSettings); 1297 } 1298 1299 if ($Type == BOUND_MIN || $Type == BOUND_BOTH) { 1300 if ($MinLabelPos == BOUND_LABEL_POS_TOP 1301 || ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) 1302 ) { 1303 $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; 1304 $Align = TEXT_ALIGN_MIDDLELEFT; 1305 } 1306 if ($MinLabelPos == BOUND_LABEL_POS_BOTTOM 1307 || ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) 1308 ) { 1309 $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; 1310 $Align = TEXT_ALIGN_MIDDLERIGHT; 1311 } 1312 1313 $XPos = $X + $MinPos * $XStep + $SerieOffset; 1314 $Label = $MinLabelTxt . $this->scaleFormat($MinValue, $Mode, $Format, $Unit); 1315 1316 $TxtPos = $this->getTextBox($YPos, $XPos, $this->FontName, $this->FontSize, 0, $Label); 1317 $XOffset = 0; 1318 $YOffset = 0; 1319 if ($TxtPos[0]["X"] < $this->GraphAreaX1) { 1320 $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; 1321 } 1322 if ($TxtPos[1]["X"] > $this->GraphAreaX2) { 1323 $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); 1324 } 1325 if ($TxtPos[2]["Y"] < $this->GraphAreaY1) { 1326 $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"]) / 2; 1327 } 1328 if ($TxtPos[0]["Y"] > $this->GraphAreaY2) { 1329 $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2) / 2); 1330 } 1331 1332 $CaptionSettings["R"] = $MinDisplayR; 1333 $CaptionSettings["G"] = $MinDisplayG; 1334 $CaptionSettings["B"] = $MinDisplayB; 1335 $CaptionSettings["Align"] = $Align; 1336 1337 $this->drawText($YPos + $XOffset, $XPos + $YOffset, $Label, $CaptionSettings); 1338 } 1339 } 1340 } 1341 } 1342 } 1343 1344 /** 1345 * Write labels 1346 * @param string $SeriesName 1347 * @param array $Indexes 1348 * @param array $Format 1349 */ 1350 public function writeLabel($SeriesName, $Indexes, array $Format = []) 1351 { 1352 $OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : null; 1353 $ForceLabels = isset($Format["ForceLabels"]) ? $Format["ForceLabels"] : null; 1354 $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX; 1355 $DrawVerticalLine = isset($Format["DrawVerticalLine"]) ? $Format["DrawVerticalLine"] : false; 1356 $VerticalLineR = isset($Format["VerticalLineR"]) ? $Format["VerticalLineR"] : 0; 1357 $VerticalLineG = isset($Format["VerticalLineG"]) ? $Format["VerticalLineG"] : 0; 1358 $VerticalLineB = isset($Format["VerticalLineB"]) ? $Format["VerticalLineB"] : 0; 1359 $VerticalLineAlpha = isset($Format["VerticalLineAlpha"]) ? $Format["VerticalLineAlpha"] : 40; 1360 $VerticalLineTicks = isset($Format["VerticalLineTicks"]) ? $Format["VerticalLineTicks"] : 2; 1361 1362 $Data = $this->DataSet->getData(); 1363 list($XMargin, $XDivs) = $this->scaleGetXSettings(); 1364 1365 if (!is_array($Indexes)) { 1366 $Index = $Indexes; 1367 $Indexes = []; 1368 $Indexes[] = $Index; 1369 } 1370 if (!is_array($SeriesName)) { 1371 $SerieName = $SeriesName; 1372 $SeriesName = []; 1373 $SeriesName[] = $SerieName; 1374 } 1375 if ($ForceLabels != null && !is_array($ForceLabels)) { 1376 $ForceLabel = $ForceLabels; 1377 $ForceLabels = []; 1378 $ForceLabels[] = $ForceLabel; 1379 } 1380 1381 foreach ($Indexes as $Key => $Index) { 1382 $Series = []; 1383 1384 if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) { 1385 if ($XDivs == 0) { 1386 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4; 1387 } else { 1388 $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs; 1389 } 1390 $X = $this->GraphAreaX1 + $XMargin + $Index * $XStep; 1391 1392 if ($DrawVerticalLine) { 1393 $this->drawLine( 1394 $X, 1395 $this->GraphAreaY1 + $Data["YMargin"], 1396 $X, 1397 $this->GraphAreaY2 - $Data["YMargin"], 1398 [ 1399 "R" => $VerticalLineR, 1400 "G" => $VerticalLineG, 1401 "B" => $VerticalLineB, 1402 "Alpha" => $VerticalLineAlpha, 1403 "Ticks" => $VerticalLineTicks 1404 ] 1405 ); 1406 } 1407 1408 $MinY = $this->GraphAreaY2; 1409 foreach ($SeriesName as $SerieName) { 1410 if (isset($Data["Series"][$SerieName]["Data"][$Index])) { 1411 $AxisID = $Data["Series"][$SerieName]["Axis"]; 1412 $XAxisMode = $Data["XAxisDisplay"]; 1413 $XAxisFormat = $Data["XAxisFormat"]; 1414 $XAxisUnit = $Data["XAxisUnit"]; 1415 $AxisMode = $Data["Axis"][$AxisID]["Display"]; 1416 $AxisFormat = $Data["Axis"][$AxisID]["Format"]; 1417 $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; 1418 $XLabel = ""; 1419 1420 if (isset($Data["Abscissa"]) 1421 && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) 1422 ) { 1423 $XLabel = $this->scaleFormat( 1424 $Data["Series"][$Data["Abscissa"]]["Data"][$Index], 1425 $XAxisMode, 1426 $XAxisFormat, 1427 $XAxisUnit 1428 ); 1429 } 1430 1431 if ($OverrideTitle != null) { 1432 $Description = $OverrideTitle; 1433 } elseif (count($SeriesName) == 1) { 1434 $Description = $Data["Series"][$SerieName]["Description"] . " - " . $XLabel; 1435 } elseif (isset($Data["Abscissa"]) 1436 && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) 1437 ) { 1438 $Description = $XLabel; 1439 } 1440 1441 $Serie = [ 1442 "R" => $Data["Series"][$SerieName]["Color"]["R"], 1443 "G" => $Data["Series"][$SerieName]["Color"]["G"], 1444 "B" => $Data["Series"][$SerieName]["Color"]["B"], 1445 "Alpha" => $Data["Series"][$SerieName]["Color"]["Alpha"] 1446 ]; 1447 if (count($SeriesName) == 1 1448 && isset($Data["Series"][$SerieName]["XOffset"]) 1449 ) { 1450 $SerieOffset = $Data["Series"][$SerieName]["XOffset"]; 1451 } else { 1452 $SerieOffset = 0; 1453 } 1454 $Value = $Data["Series"][$SerieName]["Data"][$Index]; 1455 if ($Value == VOID) { 1456 $Value = "NaN"; 1457 } 1458 1459 if ($ForceLabels != null) { 1460 $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set"; 1461 } else { 1462 $Caption = $this->scaleFormat($Value, $AxisMode, $AxisFormat, $AxisUnit); 1463 } 1464 1465 if ($this->LastChartLayout == CHART_LAST_LAYOUT_STACKED) { 1466 if ($Value >= 0) { 1467 $LookFor = "+"; 1468 } else { 1469 $LookFor = "-"; 1470 } 1471 1472 $Value = 0; 1473 $Done = false; 1474 foreach ($Data["Series"] as $Name => $SerieLookup) { 1475 if ($SerieLookup["isDrawable"] == true 1476 && $Name != $Data["Abscissa"] && !$Done 1477 ) { 1478 if (isset($Data["Series"][$Name]["Data"][$Index]) 1479 && $Data["Series"][$Name]["Data"][$Index] != VOID 1480 ) { 1481 if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+") { 1482 $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; 1483 } 1484 if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-") { 1485 $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; 1486 } 1487 if ($Name == $SerieName) { 1488 $Done = true; 1489 } 1490 } 1491 } 1492 } 1493 } 1494 1495 $X = floor($this->GraphAreaX1 + $XMargin + $Index * $XStep + $SerieOffset); 1496 $Y = floor($this->scaleComputeY($Value, ["AxisID" => $AxisID])); 1497 1498 if ($Y < $MinY) { 1499 $MinY = $Y; 1500 } 1501 1502 if ($DrawPoint == LABEL_POINT_CIRCLE) { 1503 $this->drawFilledCircle( 1504 $X, 1505 $Y, 1506 3, 1507 [ 1508 "R" => 255, 1509 "G" => 255, 1510 "B" => 255, 1511 "BorderR" => 0, 1512 "BorderG" => 0, 1513 "BorderB" => 0 1514 ] 1515 ); 1516 } elseif ($DrawPoint == LABEL_POINT_BOX) { 1517 $this->drawFilledRectangle( 1518 $X - 2, 1519 $Y - 2, 1520 $X + 2, 1521 $Y + 2, 1522 [ 1523 "R" => 255, 1524 "G" => 255, 1525 "B" => 255, 1526 "BorderR" => 0, 1527 "BorderG" => 0, 1528 "BorderB" => 0 1529 ] 1530 ); 1531 } 1532 $Series[] = ["Format" => $Serie, "Caption" => $Caption]; 1533 } 1534 } 1535 $this->drawLabelBox($X, $MinY - 3, $Description, $Series, $Format); 1536 } else { 1537 if ($XDivs == 0) { 1538 $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4; 1539 } else { 1540 $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs; 1541 } 1542 $Y = $this->GraphAreaY1 + $XMargin + $Index * $XStep; 1543 1544 if ($DrawVerticalLine) { 1545 $this->drawLine( 1546 $this->GraphAreaX1 + $Data["YMargin"], 1547 $Y, 1548 $this->GraphAreaX2 - $Data["YMargin"], 1549 $Y, 1550 [ 1551 "R" => $VerticalLineR, 1552 "G" => $VerticalLineG, 1553 "B" => $VerticalLineB, 1554 "Alpha" => $VerticalLineAlpha, 1555 "Ticks" => $VerticalLineTicks 1556 ] 1557 ); 1558 } 1559 1560 $MinX = $this->GraphAreaX2; 1561 foreach ($SeriesName as $Key => $SerieName) { 1562 if (isset($Data["Series"][$SerieName]["Data"][$Index])) { 1563 $AxisID = $Data["Series"][$SerieName]["Axis"]; 1564 $XAxisMode = $Data["XAxisDisplay"]; 1565 $XAxisFormat = $Data["XAxisFormat"]; 1566 $XAxisUnit = $Data["XAxisUnit"]; 1567 $AxisMode = $Data["Axis"][$AxisID]["Display"]; 1568 $AxisFormat = $Data["Axis"][$AxisID]["Format"]; 1569 $AxisUnit = $Data["Axis"][$AxisID]["Unit"]; 1570 $XLabel = ""; 1571 1572 if (isset($Data["Abscissa"]) 1573 && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) 1574 ) { 1575 $XLabel = $this->scaleFormat( 1576 $Data["Series"][$Data["Abscissa"]]["Data"][$Index], 1577 $XAxisMode, 1578 $XAxisFormat, 1579 $XAxisUnit 1580 ); 1581 } 1582 1583 if ($OverrideTitle != null) { 1584 $Description = $OverrideTitle; 1585 } elseif (count($SeriesName) == 1) { 1586 if (isset($Data["Abscissa"]) 1587 && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) 1588 ) { 1589 $Description = $Data["Series"][$SerieName]["Description"] . " - " . $XLabel; 1590 } 1591 } elseif (isset($Data["Abscissa"]) 1592 && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) 1593 ) { 1594 $Description = $XLabel; 1595 } 1596 $Serie = []; 1597 if (isset($Data["Extended"]["Palette"][$Index])) { 1598 $Serie["R"] = $Data["Extended"]["Palette"][$Index]["R"]; 1599 $Serie["G"] = $Data["Extended"]["Palette"][$Index]["G"]; 1600 $Serie["B"] = $Data["Extended"]["Palette"][$Index]["B"]; 1601 $Serie["Alpha"] = $Data["Extended"]["Palette"][$Index]["Alpha"]; 1602 } else { 1603 $Serie["R"] = $Data["Series"][$SerieName]["Color"]["R"]; 1604 $Serie["G"] = $Data["Series"][$SerieName]["Color"]["G"]; 1605 $Serie["B"] = $Data["Series"][$SerieName]["Color"]["B"]; 1606 $Serie["Alpha"] = $Data["Series"][$SerieName]["Color"]["Alpha"]; 1607 } 1608 1609 if (count($SeriesName) == 1 && isset($Data["Series"][$SerieName]["XOffset"])) { 1610 $SerieOffset = $Data["Series"][$SerieName]["XOffset"]; 1611 } else { 1612 $SerieOffset = 0; 1613 } 1614 1615 $Value = $Data["Series"][$SerieName]["Data"][$Index]; 1616 if ($ForceLabels != null) { 1617 $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set"; 1618 } else { 1619 $Caption = $this->scaleFormat($Value, $AxisMode, $AxisFormat, $AxisUnit); 1620 } 1621 if ($Value == VOID) { 1622 $Value = "NaN"; 1623 } 1624 1625 if ($this->LastChartLayout == CHART_LAST_LAYOUT_STACKED) { 1626 if ($Value >= 0) { 1627 $LookFor = "+"; 1628 } else { 1629 $LookFor = "-"; 1630 } 1631 1632 $Value = 0; 1633 $Done = false; 1634 foreach ($Data["Series"] as $Name => $SerieLookup) { 1635 if ($SerieLookup["isDrawable"] == true 1636 && $Name != $Data["Abscissa"] 1637 && !$Done 1638 ) { 1639 if (isset($Data["Series"][$Name]["Data"][$Index]) 1640 && $Data["Series"][$Name]["Data"][$Index] != VOID 1641 ) { 1642 if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+") { 1643 $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; 1644 } 1645 if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-") { 1646 $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; 1647 } 1648 if ($Name == $SerieName) { 1649 $Done = true; 1650 } 1651 } 1652 } 1653 } 1654 } 1655 1656 $X = floor($this->scaleComputeY($Value, ["AxisID" => $AxisID])); 1657 $Y = floor($this->GraphAreaY1 + $XMargin + $Index * $XStep + $SerieOffset); 1658 1659 if ($X < $MinX) { 1660 $MinX = $X; 1661 } 1662 1663 if ($DrawPoint == LABEL_POINT_CIRCLE) { 1664 $this->drawFilledCircle( 1665 $X, 1666 $Y, 1667 3, 1668 [ 1669 "R" => 255, 1670 "G" => 255, 1671 "B" => 255, 1672 "BorderR" => 0, 1673 "BorderG" => 0, 1674 "BorderB" => 0 1675 ] 1676 ); 1677 } elseif ($DrawPoint == LABEL_POINT_BOX) { 1678 $this->drawFilledRectangle( 1679 $X - 2, 1680 $Y - 2, 1681 $X + 2, 1682 $Y + 2, 1683 [ 1684 "R" => 255, 1685 "G" => 255, 1686 "B" => 255, 1687 "BorderR" => 0, 1688 "BorderG" => 0, 1689 "BorderB" => 0 1690 ] 1691 ); 1692 } 1693 $Series[] = ["Format" => $Serie, "Caption" => $Caption]; 1694 } 1695 } 1696 $this->drawLabelBox($MinX, $Y - 3, $Description, $Series, $Format); 1697 } 1698 } 1699 } 1700} 1701