site-accueil-insa/matomo/vendor/szymach/c-pchart/src/BaseDraw.php

1728 lines
61 KiB
PHP

<?php
namespace CpChart;
use Exception;
/**
* This class exists only to try and reduce the number of methods and properties
* in the Draw class. Basically all methods not named 'drawX' were moved in here,
* as well as all the class fields.
*/
abstract class BaseDraw
{
/**
* Width of the picture
* @var int
*/
public $XSize;
/**
* Height of the picture
* @var int
*/
public $YSize;
/**
* GD picture object
* @var resource
*/
public $Picture;
/**
* Turn antialias on or off
* @var boolean
*/
public $Antialias = true;
/**
* Quality of the antialiasing implementation (0-1)
* @var int
*/
public $AntialiasQuality = 0;
/**
* Already drawn pixels mask (Filled circle implementation)
* @var array
*/
public $Mask = [];
/**
* Just to know if we need to flush the alpha channels when rendering
* @var boolean
*/
public $TransparentBackground = false;
/**
* Graph area X origin
* @var int
*/
public $GraphAreaX1;
/**
* Graph area Y origin
* @var int
*/
public $GraphAreaY1;
/**
* Graph area bottom right X position
* @var int
*/
public $GraphAreaX2;
/**
* Graph area bottom right Y position
* @var int
*/
public $GraphAreaY2;
/**
* Minimum height for scale divs
* @var int
*/
public $ScaleMinDivHeight = 20;
/**
* @var string
*/
public $FontName = "GeosansLight.ttf";
/**
* @var int
*/
public $FontSize = 12;
/**
* Return the bounding box of the last written string
* @var array
*/
public $FontBox;
/**
* @var int
*/
public $FontColorR = 0;
/**
* @var int
*/
public $FontColorG = 0;
/**
* @var int
*/
public $FontColorB = 0;
/**
* @var int
*/
public $FontColorA = 100;
/**
* Turn shadows on or off
* @var boolean
*/
public $Shadow = false;
/**
* X Offset of the shadow
* @var int
*/
public $ShadowX;
/**
* Y Offset of the shadow
* @var int
*/
public $ShadowY;
/**
* R component of the shadow
* @var int
*/
public $ShadowR;
/**
* G component of the shadow
* @var int
*/
public $ShadowG;
/**
* B component of the shadow
* @var int
*/
public $ShadowB;
/**
* Alpha level of the shadow
* @var int
*/
public $Shadowa;
/**
* Array containing the image map
* @var array
*/
public $ImageMap = [];
/**
* Name of the session array
* @var int
*/
public $ImageMapIndex = "pChart";
/**
* Save the current imagemap storage mode
* @var int
*/
public $ImageMapStorageMode;
/**
* Automatic deletion of the image map temp files
* @var boolean
*/
public $ImageMapAutoDelete = true;
/**
* Attached dataset
* @var Data
*/
public $DataSet;
/**
* Last generated chart info
* Last layout : regular or stacked
* @var int
*/
public $LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
/**
* @var string
*/
private $resourcePath;
public function __construct()
{
$this->resourcePath = sprintf('%s/../resources', __DIR__);
$this->FontName = $this->loadFont($this->FontName, 'fonts');
}
/**
* Set the path to the folder containing library resources (fonts, data, palettes).
*
* @param string $path
* @throws Exception
*/
public function setResourcePath($path)
{
$escapedPath = rtrim($path, '/');
if (!file_exists($escapedPath)) {
throw new Exception(sprintf(
"The path '%s' to resources' folder does not exist!",
$escapedPath
));
}
$this->resourcePath = $escapedPath;
}
/**
* Check if requested resource exists and return the path to it if yes.
* @param string $name
* @param string $type
* @return string
* @throws Exception
*/
protected function loadFont($name, $type)
{
if (file_exists($name)) {
return $name;
}
$path = sprintf('%s/%s/%s', $this->resourcePath, $type, $name);
if (file_exists($path)) {
return $path;
}
throw new Exception(
sprintf('The requested resource %s (%s) has not been found!', $name, $type)
);
}
/**
* Allocate a color with transparency
* @param resource $Picture
* @param int $R
* @param int $G
* @param int $B
* @param int $Alpha
* @return int
*/
public function allocateColor($Picture, $R, $G, $B, $Alpha = 100)
{
if ($R < 0) {
$R = 0;
}
if ($R > 255) {
$R = 255;
}
if ($G < 0) {
$G = 0;
}
if ($G > 255) {
$G = 255;
}
if ($B < 0) {
$B = 0;
}
if ($B > 255) {
$B = 255;
}
if ($Alpha < 0) {
$Alpha = 0;
}
if ($Alpha > 100) {
$Alpha = 100;
}
$Alpha = $this->convertAlpha($Alpha);
return imagecolorallocatealpha($Picture, (int) $R, (int) $G, (int) $B, (int) $Alpha);
}
/**
* Convert apha to base 10
* @param int|float $AlphaValue
* @return integer
*/
public function convertAlpha($AlphaValue)
{
return floor((127 / 100) * (100 - $AlphaValue));
}
/**
* @param string $FileName
* @return array
*/
public function getPicInfo($FileName)
{
$Infos = getimagesize($FileName);
$Width = $Infos[0];
$Height = $Infos[1];
$Type = $Infos["mime"];
if ($Type == "image/png") {
$Type = 1;
}
if ($Type == "image/gif") {
$Type = 2;
}
if ($Type == "image/jpeg ") {
$Type = 3;
}
return [$Width, $Height, $Type];
}
/**
* Compute the scale, check for the best visual factors
* @param int $XMin
* @param int $XMax
* @param int $MaxDivs
* @param array $Factors
* @param int $AxisID
* @return mixed
*/
public function computeScale($XMin, $XMax, $MaxDivs, array $Factors, $AxisID = 0)
{
/* Compute each factors */
$Results = [];
foreach ($Factors as $Key => $Factor) {
$Results[$Factor] = $this->processScale($XMin, $XMax, $MaxDivs, [$Factor], $AxisID);
}
/* Remove scales that are creating to much decimals */
$GoodScaleFactors = [];
foreach ($Results as $Key => $Result) {
$Decimals = preg_split("/\./", $Result["RowHeight"]);
if ((!isset($Decimals[1])) || (strlen($Decimals[1]) < 6)) {
$GoodScaleFactors[] = $Key;
}
}
/* Found no correct scale, shame,... returns the 1st one as default */
if (!count($GoodScaleFactors)) {
return $Results[$Factors[0]];
}
/* Find the factor that cause the maximum number of Rows */
$MaxRows = 0;
$BestFactor = 0;
foreach ($GoodScaleFactors as $Key => $Factor) {
if ($Results[$Factor]["Rows"] > $MaxRows) {
$MaxRows = $Results[$Factor]["Rows"];
$BestFactor = $Factor;
}
}
/* Return the best visual scale */
return $Results[$BestFactor];
}
/**
* Compute the best matching scale based on size & factors
* @param int $XMin
* @param int $XMax
* @param int $MaxDivs
* @param array $Factors
* @param int $AxisID
* @return array
*/
public function processScale($XMin, $XMax, $MaxDivs, array $Factors, $AxisID)
{
$ScaleHeight = abs(ceil($XMax) - floor($XMin));
$Format = null;
if (isset($this->DataSet->Data["Axis"][$AxisID]["Format"])) {
$Format = $this->DataSet->Data["Axis"][$AxisID]["Format"];
}
$Mode = AXIS_FORMAT_DEFAULT;
if (isset($this->DataSet->Data["Axis"][$AxisID]["Display"])) {
$Mode = $this->DataSet->Data["Axis"][$AxisID]["Display"];
}
$Scale = [];
if ($XMin != $XMax) {
$Found = false;
$Rescaled = false;
$Scaled10Factor = .0001;
$Result = 0;
while (!$Found) {
foreach ($Factors as $Key => $Factor) {
if (!$Found) {
$XMinRescaled = $XMin;
if (!($this->modulo($XMin, $Factor * $Scaled10Factor) == 0)
|| ($XMin != floor($XMin))
) {
$XMinRescaled = floor($XMin / ($Factor * $Scaled10Factor))
* $Factor
* $Scaled10Factor
;
}
$XMaxRescaled = $XMax;
if (!($this->modulo($XMax, $Factor * $Scaled10Factor) == 0)
|| ($XMax != floor($XMax))
) {
$XMaxRescaled = floor($XMax / ($Factor * $Scaled10Factor))
* $Factor
* $Scaled10Factor
+ ($Factor * $Scaled10Factor)
;
}
$ScaleHeightRescaled = abs($XMaxRescaled - $XMinRescaled);
if (!$Found
&& floor($ScaleHeightRescaled / ($Factor * $Scaled10Factor)) <= $MaxDivs
) {
$Found = true;
$Rescaled = true;
$Result = $Factor * $Scaled10Factor;
}
}
}
$Scaled10Factor = $Scaled10Factor * 10;
}
/* ReCall Min / Max / Height */
if ($Rescaled) {
$XMin = $XMinRescaled;
$XMax = $XMaxRescaled;
$ScaleHeight = $ScaleHeightRescaled;
}
/* Compute rows size */
$Rows = floor($ScaleHeight / $Result);
if ($Rows == 0) {
$Rows = 1;
}
$RowHeight = $ScaleHeight / $Rows;
/* Return the results */
$Scale["Rows"] = $Rows;
$Scale["RowHeight"] = $RowHeight;
$Scale["XMin"] = $XMin;
$Scale["XMax"] = $XMax;
/* Compute the needed decimals for the metric view to avoid repetition of the same X Axis labels */
if ($Mode == AXIS_FORMAT_METRIC && $Format == null) {
$Done = false;
$GoodDecimals = 0;
for ($Decimals = 0; $Decimals <= 10; $Decimals++) {
if (!$Done) {
$LastLabel = "zob";
$ScaleOK = true;
for ($i = 0; $i <= $Rows; $i++) {
$Value = $XMin + $i * $RowHeight;
$Label = $this->scaleFormat($Value, AXIS_FORMAT_METRIC, $Decimals);
if ($LastLabel == $Label) {
$ScaleOK = false;
}
$LastLabel = $Label;
}
if ($ScaleOK) {
$Done = true;
$GoodDecimals = $Decimals;
}
}
}
$Scale["Format"] = $GoodDecimals;
}
} else {
/* If all values are the same we keep a +1/-1 scale */
$Rows = 2;
$XMin = $XMax - 1;
$XMax = $XMax + 1;
$RowHeight = 1;
/* Return the results */
$Scale["Rows"] = $Rows;
$Scale["RowHeight"] = $RowHeight;
$Scale["XMin"] = $XMin;
$Scale["XMax"] = $XMax;
}
return $Scale;
}
/**
*
* @param int|float $Value1
* @param int|float $Value2
* @return double
*/
public function modulo($Value1, $Value2)
{
if (floor($Value2) == 0) {
return 0;
}
if (floor($Value2) != 0) {
return (int) $Value1 % (int) $Value2;
}
$MinValue = min($Value1, $Value2);
$Factor = 10;
while (floor($MinValue * $Factor) == 0) {
$Factor = $Factor * 10;
}
return floor($Value1 * $Factor) % floor($Value2 * $Factor);
}
/**
* @param mixed $Value
* @param mixed $LastValue
* @param integer $LabelingMethod
* @param integer $ID
* @param boolean $LabelSkip
* @return boolean
*/
public function isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip)
{
if ($LabelingMethod == LABELING_DIFFERENT && $Value != $LastValue) {
return true;
}
if ($LabelingMethod == LABELING_DIFFERENT && $Value == $LastValue) {
return false;
}
if ($LabelingMethod == LABELING_ALL && $LabelSkip == 0) {
return true;
}
if ($LabelingMethod == LABELING_ALL && ($ID + $LabelSkip) % ($LabelSkip + 1) != 1) {
return false;
}
return true;
}
/**
* Returns the number of drawable series
* @return int
*/
public function countDrawableSeries()
{
$count = 0;
$Data = $this->DataSet->getData();
foreach ($Data["Series"] as $SerieName => $Serie) {
if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
$count++;
}
}
return $count;
}
/**
* Fix box coordinates
* @param int $Xa
* @param int $Ya
* @param int $Xb
* @param int $Yb
* @return integer[]
*/
public function fixBoxCoordinates($Xa, $Ya, $Xb, $Yb)
{
return [
(int) min($Xa, $Xb),
(int) min($Ya, $Yb),
(int) max($Xa, $Xb),
(int) max($Ya, $Yb)
];
}
/**
* Apply AALias correction to the rounded box boundaries
* @param int|float $Value
* @param int $Mode
* @return int|float
*/
public function offsetCorrection($Value, $Mode)
{
$Value = round($Value, 1);
if ($Value == 0 && $Mode != 1) {
return 0;
}
if ($Mode == 1) {
if ($Value == .5) {
return .5;
}
if ($Value == .8) {
return .6;
}
if (in_array($Value, [.4, .7])) {
return .7;
}
if (in_array($Value, [.2, .3, .6])) {
return .8;
}
if (in_array($Value, [0, 1, .1, .9])) {
return .9;
}
}
if ($Mode == 2) {
if ($Value == .1) {
return .1;
}
if ($Value == .2) {
return .2;
}
if ($Value == .3) {
return .3;
}
if ($Value == .4) {
return .4;
}
if ($Value == .5) {
return .5;
}
if ($Value == .7) {
return .7;
}
if (in_array($Value, [.6, .8])) {
return .8;
}
if (in_array($Value, [1, .9])) {
return .9;
}
}
if ($Mode == 3) {
if (in_array($Value, [1, .1])) {
return .1;
}
if ($Value == .2) {
return .2;
}
if ($Value == .3) {
return .3;
}
if (in_array($Value, [.4, .8])) {
return .4;
}
if ($Value == .5) {
return .9;
}
if ($Value == .6) {
return .6;
}
if ($Value == .7) {
return .7;
}
if ($Value == .9) {
return .5;
}
}
if ($Mode == 4) {
if ($Value == 1) {
return -1;
}
if (in_array($Value, [.1, .4, .7, .8, .9])) {
return .1;
}
if ($Value == .2) {
return .2;
}
if ($Value == .3) {
return .3;
}
if ($Value == .5) {
return -.1;
}
if ($Value == .6) {
return .8;
}
}
}
/**
* Get the legend box size
* @param array $Format
* @return array
*/
public function getLegendSize(array $Format = [])
{
$FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName;
$FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
$Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5;
$Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL;
$BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5;
$BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5;
$IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth;
$IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight;
$XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5;
$Data = $this->DataSet->getData();
foreach ($Data["Series"] as $SerieName => $Serie) {
if ($Serie["isDrawable"] == true
&& $SerieName != $Data["Abscissa"]
&& isset($Serie["Picture"])
) {
list($PicWidth, $PicHeight) = $this->getPicInfo($Serie["Picture"]);
if ($IconAreaWidth < $PicWidth) {
$IconAreaWidth = $PicWidth;
}
if ($IconAreaHeight < $PicHeight) {
$IconAreaHeight = $PicHeight;
}
}
}
$YStep = max($this->FontSize, $IconAreaHeight) + 5;
$XStep = $IconAreaWidth + 5;
$XStep = $XSpacing;
$X = 100;
$Y = 100;
$Boundaries = [];
$Boundaries["L"] = $X;
$Boundaries["T"] = $Y;
$Boundaries["R"] = 0;
$Boundaries["B"] = 0;
$vY = $Y;
$vX = $X;
foreach ($Data["Series"] as $SerieName => $Serie) {
if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
if ($Mode == LEGEND_VERTICAL) {
$BoxArray = $this->getTextBox(
$vX + $IconAreaWidth + 4,
$vY + $IconAreaHeight / 2,
$FontName,
$FontSize,
0,
$Serie["Description"]
);
if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) {
$Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2;
}
if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
$Boundaries["R"] = $BoxArray[1]["X"] + 2;
}
if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) {
$Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2;
}
$Lines = preg_split("/\n/", $Serie["Description"]);
$vY = $vY + max($this->FontSize * count($Lines), $IconAreaHeight) + 5;
} elseif ($Mode == LEGEND_HORIZONTAL) {
$Lines = preg_split("/\n/", $Serie["Description"]);
$Width = [];
foreach ($Lines as $Key => $Value) {
$BoxArray = $this->getTextBox(
$vX + $IconAreaWidth + 6,
$Y + $IconAreaHeight / 2 + (($this->FontSize + 3) * $Key),
$FontName,
$FontSize,
0,
$Value
);
if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) {
$Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2;
}
if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
$Boundaries["R"] = $BoxArray[1]["X"] + 2;
}
if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) {
$Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2;
}
$Width[] = $BoxArray[1]["X"];
}
$vX = max($Width) + $XStep;
}
}
}
$vY = $vY - $YStep;
$vX = $vX - $XStep;
$TopOffset = $Y - $Boundaries["T"];
if ($Boundaries["B"] - ($vY + $IconAreaHeight) < $TopOffset) {
$Boundaries["B"] = $vY + $IconAreaHeight + $TopOffset;
}
$Width = ($Boundaries["R"] + $Margin) - ($Boundaries["L"] - $Margin);
$Height = ($Boundaries["B"] + $Margin) - ($Boundaries["T"] - $Margin);
return ["Width" => $Width, "Height" => $Height];
}
/**
* Return the abscissa margin
* @param array $Data
* @return int
*/
public function getAbscissaMargin(array $Data)
{
foreach ($Data["Axis"] as $Values) {
if ($Values["Identity"] == AXIS_X) {
return $Values["Margin"];
}
}
return 0;
}
/**
* Returns a random color
* @param int $Alpha
* @return array
*/
public function getRandomColor($Alpha = 100)
{
return [
"R" => rand(0, 255),
"G" => rand(0, 255),
"B" => rand(0, 255),
"Alpha" => $Alpha
];
}
/**
* Validate a palette
* @param mixed $Colors
* @param int|float $Surrounding
* @return array
*/
public function validatePalette($Colors, $Surrounding = null)
{
$Result = [];
if (!is_array($Colors)) {
return $this->getRandomColor();
}
foreach ($Colors as $Key => $Values) {
if (isset($Values["R"])) {
$Result[$Key]["R"] = $Values["R"];
} else {
$Result[$Key]["R"] = rand(0, 255);
}
if (isset($Values["G"])) {
$Result[$Key]["G"] = $Values["G"];
} else {
$Result[$Key]["G"] = rand(0, 255);
}
if (isset($Values["B"])) {
$Result[$Key]["B"] = $Values["B"];
} else {
$Result[$Key]["B"] = rand(0, 255);
}
if (isset($Values["Alpha"])) {
$Result[$Key]["Alpha"] = $Values["Alpha"];
} else {
$Result[$Key]["Alpha"] = 100;
}
if (null !== $Surrounding) {
$Result[$Key]["BorderR"] = $Result[$Key]["R"] + $Surrounding;
$Result[$Key]["BorderG"] = $Result[$Key]["G"] + $Surrounding;
$Result[$Key]["BorderB"] = $Result[$Key]["B"] + $Surrounding;
} else {
if (isset($Values["BorderR"])) {
$Result[$Key]["BorderR"] = $Values["BorderR"];
} else {
$Result[$Key]["BorderR"] = $Result[$Key]["R"];
}
if (isset($Values["BorderG"])) {
$Result[$Key]["BorderG"] = $Values["BorderG"];
} else {
$Result[$Key]["BorderG"] = $Result[$Key]["G"];
}
if (isset($Values["BorderB"])) {
$Result[$Key]["BorderB"] = $Values["BorderB"];
} else {
$Result[$Key]["BorderB"] = $Result[$Key]["B"];
}
if (isset($Values["BorderAlpha"])) {
$Result[$Key]["BorderAlpha"] = $Values["BorderAlpha"];
} else {
$Result[$Key]["BorderAlpha"] = $Result[$Key]["Alpha"];
}
}
}
return $Result;
}
/**
* @param mixed $Values
* @param array $Option
* @param boolean $ReturnOnly0Height
* @return int|float|array
*/
public function scaleComputeY($Values, array $Option = [], $ReturnOnly0Height = false)
{
$AxisID = isset($Option["AxisID"]) ? $Option["AxisID"] : 0;
$SerieName = isset($Option["SerieName"]) ? $Option["SerieName"] : null;
$Data = $this->DataSet->getData();
if (!isset($Data["Axis"][$AxisID])) {
return -1;
}
if ($SerieName != null) {
$AxisID = $Data["Series"][$SerieName]["Axis"];
}
if (!is_array($Values)) {
$tmp = $Values;
$Values = [];
$Values[0] = $tmp;
}
$Result = [];
if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
$Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Data["Axis"][$AxisID]["Margin"] * 2;
$ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"];
$Step = $Height / $ScaleHeight;
if ($ReturnOnly0Height) {
foreach ($Values as $Key => $Value) {
if ($Value == VOID) {
$Result[] = VOID;
} else {
$Result[] = $Step * $Value;
}
}
} else {
foreach ($Values as $Key => $Value) {
if ($Value == VOID) {
$Result[] = VOID;
} else {
$Result[] = $this->GraphAreaY2
- $Data["Axis"][$AxisID]["Margin"]
- ($Step * ($Value - $Data["Axis"][$AxisID]["ScaleMin"]))
;
}
}
}
} else {
$Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Data["Axis"][$AxisID]["Margin"] * 2;
$ScaleWidth = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"];
$Step = $Width / $ScaleWidth;
if ($ReturnOnly0Height) {
foreach ($Values as $Key => $Value) {
if ($Value == VOID) {
$Result[] = VOID;
} else {
$Result[] = $Step * $Value;
}
}
} else {
foreach ($Values as $Key => $Value) {
if ($Value == VOID) {
$Result[] = VOID;
} else {
$Result[] = $this->GraphAreaX1
+ $Data["Axis"][$AxisID]["Margin"]
+ ($Step * ($Value - $Data["Axis"][$AxisID]["ScaleMin"]))
;
}
}
}
}
return count($Result) == 1 ? reset($Result) : $Result;
}
/**
* Format the axis values
* @param mixed $Value
* @param int $Mode
* @param array $Format
* @param string $Unit
* @return string
*/
public function scaleFormat($Value, $Mode = null, $Format = null, $Unit = null)
{
if ($Value == VOID) {
return "";
}
if ($Mode == AXIS_FORMAT_TRAFFIC) {
if ($Value == 0) {
return "0B";
}
$Units = ["B", "KB", "MB", "GB", "TB", "PB"];
$Sign = "";
if ($Value < 0) {
$Value = abs($Value);
$Sign = "-";
}
$Value = number_format($Value / pow(1024, ($Scale = floor(log($Value, 1024)))), 2, ",", ".");
return $Sign . $Value . " " . $Units[$Scale];
}
if ($Mode == AXIS_FORMAT_CUSTOM) {
if (is_callable($Format)) {
return call_user_func($Format, $Value);
}
}
if ($Mode == AXIS_FORMAT_DATE) {
$Pattern = "d/m/Y";
if ($Format !== null) {
$Pattern = $Format;
}
return gmdate($Pattern, $Value);
}
if ($Mode == AXIS_FORMAT_TIME) {
$Pattern = "H:i:s";
if ($Format !== null) {
$Pattern = $Format;
}
return gmdate($Pattern, $Value);
}
if ($Mode == AXIS_FORMAT_CURRENCY) {
return $Format . number_format($Value, 2);
}
if ($Mode == AXIS_FORMAT_METRIC) {
if (abs($Value) > 1000000000) {
return round($Value / 1000000000, $Format) . "g" . $Unit;
}
if (abs($Value) > 1000000) {
return round($Value / 1000000, $Format) . "m" . $Unit;
} elseif (abs($Value) >= 1000) {
return round($Value / 1000, $Format) . "k" . $Unit;
}
}
return $Value . $Unit;
}
/**
* @return array|null
*/
public function scaleGetXSettings()
{
$Data = $this->DataSet->getData();
foreach ($Data["Axis"] as $Settings) {
if ($Settings["Identity"] == AXIS_X) {
return [$Settings["Margin"], $Settings["Rows"]];
}
}
}
/**
* Write Max value on a chart
* @param int $Type
* @param array $Format
*/
public function writeBounds($Type = BOUND_BOTH, $Format = null)
{
$MaxLabelTxt = isset($Format["MaxLabelTxt"]) ? $Format["MaxLabelTxt"] : "max=";
$MinLabelTxt = isset($Format["MinLabelTxt"]) ? $Format["MinLabelTxt"] : "min=";
$Decimals = isset($Format["Decimals"]) ? $Format["Decimals"] : 1;
$ExcludedSeries = isset($Format["ExcludedSeries"]) ? $Format["ExcludedSeries"] : "";
$DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4;
$DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
$MaxDisplayR = isset($Format["MaxDisplayR"]) ? $Format["MaxDisplayR"] : 0;
$MaxDisplayG = isset($Format["MaxDisplayG"]) ? $Format["MaxDisplayG"] : 0;
$MaxDisplayB = isset($Format["MaxDisplayB"]) ? $Format["MaxDisplayB"] : 0;
$MinDisplayR = isset($Format["MinDisplayR"]) ? $Format["MinDisplayR"] : 255;
$MinDisplayG = isset($Format["MinDisplayG"]) ? $Format["MinDisplayG"] : 255;
$MinDisplayB = isset($Format["MinDisplayB"]) ? $Format["MinDisplayB"] : 255;
$MinLabelPos = isset($Format["MinLabelPos"]) ? $Format["MinLabelPos"] : BOUND_LABEL_POS_AUTO;
$MaxLabelPos = isset($Format["MaxLabelPos"]) ? $Format["MaxLabelPos"] : BOUND_LABEL_POS_AUTO;
$DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : true;
$DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : false;
$BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5;
$BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : true;
$RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3;
$BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
$BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
$BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
$BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20;
$BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
$BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255;
$BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255;
$BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255;
$BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100;
$CaptionSettings = [
"DrawBox" => $DrawBox,
"DrawBoxBorder" => $DrawBoxBorder,
"BorderOffset" => $BorderOffset,
"BoxRounded" => $BoxRounded,
"RoundedRadius" => $RoundedRadius,
"BoxR" => $BoxR,
"BoxG" => $BoxG,
"BoxB" => $BoxB,
"BoxAlpha" => $BoxAlpha,
"BoxSurrounding" => $BoxSurrounding,
"BoxBorderR" => $BoxBorderR,
"BoxBorderG" => $BoxBorderG,
"BoxBorderB" => $BoxBorderB,
"BoxBorderAlpha" => $BoxBorderAlpha
];
list($XMargin, $XDivs) = $this->scaleGetXSettings();
$Data = $this->DataSet->getData();
foreach ($Data["Series"] as $SerieName => $Serie) {
if ($Serie["isDrawable"] == true
&& $SerieName != $Data["Abscissa"]
&& !isset($ExcludedSeries[$SerieName])
) {
$R = $Serie["Color"]["R"];
$G = $Serie["Color"]["G"];
$B = $Serie["Color"]["B"];
$MinValue = $this->DataSet->getMin($SerieName);
$MaxValue = $this->DataSet->getMax($SerieName);
$MinPos = VOID;
$MaxPos = VOID;
foreach ($Serie["Data"] as $Key => $Value) {
if ($Value == $MinValue && $MinPos == VOID) {
$MinPos = $Key;
}
if ($Value == $MaxValue) {
$MaxPos = $Key;
}
}
$AxisID = $Serie["Axis"];
$Mode = $Data["Axis"][$AxisID]["Display"];
$Format = $Data["Axis"][$AxisID]["Format"];
$Unit = $Data["Axis"][$AxisID]["Unit"];
$PosArray = $this->scaleComputeY(
$Serie["Data"],
["AxisID" => $Serie["Axis"]]
);
if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
$XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
$X = $this->GraphAreaX1 + $XMargin;
$SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0;
if ($Type == BOUND_MAX || $Type == BOUND_BOTH) {
if ($MaxLabelPos == BOUND_LABEL_POS_TOP
|| ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0)
) {
$YPos = $PosArray[$MaxPos] - $DisplayOffset + 2;
$Align = TEXT_ALIGN_BOTTOMMIDDLE;
}
if ($MaxLabelPos == BOUND_LABEL_POS_BOTTOM
|| ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0)
) {
$YPos = $PosArray[$MaxPos] + $DisplayOffset + 2;
$Align = TEXT_ALIGN_TOPMIDDLE;
}
$XPos = $X + $MaxPos * $XStep + $SerieOffset;
$Label = sprintf(
'%s%s',
$MaxLabelTxt,
$this->scaleFormat(round($MaxValue, $Decimals), $Mode, $Format, $Unit)
);
$TxtPos = $this->getTextBox($XPos, $YPos, $this->FontName, $this->FontSize, 0, $Label);
$XOffset = 0;
$YOffset = 0;
if ($TxtPos[0]["X"] < $this->GraphAreaX1) {
$XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"]) / 2);
}
if ($TxtPos[1]["X"] > $this->GraphAreaX2) {
$XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2) / 2);
}
if ($TxtPos[2]["Y"] < $this->GraphAreaY1) {
$YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"];
}
if ($TxtPos[0]["Y"] > $this->GraphAreaY2) {
$YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2);
}
$CaptionSettings["R"] = $MaxDisplayR;
$CaptionSettings["G"] = $MaxDisplayG;
$CaptionSettings["B"] = $MaxDisplayB;
$CaptionSettings["Align"] = $Align;
$this->drawText($XPos + $XOffset, $YPos + $YOffset, $Label, $CaptionSettings);
}
if ($Type == BOUND_MIN || $Type == BOUND_BOTH) {
if ($MinLabelPos == BOUND_LABEL_POS_TOP
|| ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0)
) {
$YPos = $PosArray[$MinPos] - $DisplayOffset + 2;
$Align = TEXT_ALIGN_BOTTOMMIDDLE;
}
if ($MinLabelPos == BOUND_LABEL_POS_BOTTOM
|| ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0)
) {
$YPos = $PosArray[$MinPos] + $DisplayOffset + 2;
$Align = TEXT_ALIGN_TOPMIDDLE;
}
$XPos = $X + $MinPos * $XStep + $SerieOffset;
$Label = sprintf(
'%s%s',
$MinLabelTxt,
$this->scaleFormat(round($MinValue, $Decimals), $Mode, $Format, $Unit)
);
$TxtPos = $this->getTextBox($XPos, $YPos, $this->FontName, $this->FontSize, 0, $Label);
$XOffset = 0;
$YOffset = 0;
if ($TxtPos[0]["X"] < $this->GraphAreaX1) {
$XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"]) / 2);
}
if ($TxtPos[1]["X"] > $this->GraphAreaX2) {
$XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2) / 2);
}
if ($TxtPos[2]["Y"] < $this->GraphAreaY1) {
$YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"];
}
if ($TxtPos[0]["Y"] > $this->GraphAreaY2) {
$YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2);
}
$CaptionSettings["R"] = $MinDisplayR;
$CaptionSettings["G"] = $MinDisplayG;
$CaptionSettings["B"] = $MinDisplayB;
$CaptionSettings["Align"] = $Align;
$this->drawText(
$XPos + $XOffset,
$YPos - $DisplayOffset + $YOffset,
$Label,
$CaptionSettings
);
}
} else {
$XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
$X = $this->GraphAreaY1 + $XMargin;
$SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0;
if ($Type == BOUND_MAX || $Type == BOUND_BOTH) {
if ($MaxLabelPos == BOUND_LABEL_POS_TOP
|| ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0)
) {
$YPos = $PosArray[$MaxPos] + $DisplayOffset + 2;
$Align = TEXT_ALIGN_MIDDLELEFT;
}
if ($MaxLabelPos == BOUND_LABEL_POS_BOTTOM
|| ($MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0)
) {
$YPos = $PosArray[$MaxPos] - $DisplayOffset + 2;
$Align = TEXT_ALIGN_MIDDLERIGHT;
}
$XPos = $X + $MaxPos * $XStep + $SerieOffset;
$Label = $MaxLabelTxt . $this->scaleFormat($MaxValue, $Mode, $Format, $Unit);
$TxtPos = $this->getTextBox($YPos, $XPos, $this->FontName, $this->FontSize, 0, $Label);
$XOffset = 0;
$YOffset = 0;
if ($TxtPos[0]["X"] < $this->GraphAreaX1) {
$XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"];
}
if ($TxtPos[1]["X"] > $this->GraphAreaX2) {
$XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2);
}
if ($TxtPos[2]["Y"] < $this->GraphAreaY1) {
$YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"]) / 2;
}
if ($TxtPos[0]["Y"] > $this->GraphAreaY2) {
$YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2) / 2);
}
$CaptionSettings["R"] = $MaxDisplayR;
$CaptionSettings["G"] = $MaxDisplayG;
$CaptionSettings["B"] = $MaxDisplayB;
$CaptionSettings["Align"] = $Align;
$this->drawText($YPos + $XOffset, $XPos + $YOffset, $Label, $CaptionSettings);
}
if ($Type == BOUND_MIN || $Type == BOUND_BOTH) {
if ($MinLabelPos == BOUND_LABEL_POS_TOP
|| ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0)
) {
$YPos = $PosArray[$MinPos] + $DisplayOffset + 2;
$Align = TEXT_ALIGN_MIDDLELEFT;
}
if ($MinLabelPos == BOUND_LABEL_POS_BOTTOM
|| ($MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0)
) {
$YPos = $PosArray[$MinPos] - $DisplayOffset + 2;
$Align = TEXT_ALIGN_MIDDLERIGHT;
}
$XPos = $X + $MinPos * $XStep + $SerieOffset;
$Label = $MinLabelTxt . $this->scaleFormat($MinValue, $Mode, $Format, $Unit);
$TxtPos = $this->getTextBox($YPos, $XPos, $this->FontName, $this->FontSize, 0, $Label);
$XOffset = 0;
$YOffset = 0;
if ($TxtPos[0]["X"] < $this->GraphAreaX1) {
$XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"];
}
if ($TxtPos[1]["X"] > $this->GraphAreaX2) {
$XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2);
}
if ($TxtPos[2]["Y"] < $this->GraphAreaY1) {
$YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"]) / 2;
}
if ($TxtPos[0]["Y"] > $this->GraphAreaY2) {
$YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2) / 2);
}
$CaptionSettings["R"] = $MinDisplayR;
$CaptionSettings["G"] = $MinDisplayG;
$CaptionSettings["B"] = $MinDisplayB;
$CaptionSettings["Align"] = $Align;
$this->drawText($YPos + $XOffset, $XPos + $YOffset, $Label, $CaptionSettings);
}
}
}
}
}
/**
* Write labels
* @param string $SeriesName
* @param array $Indexes
* @param array $Format
*/
public function writeLabel($SeriesName, $Indexes, array $Format = [])
{
$OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : null;
$ForceLabels = isset($Format["ForceLabels"]) ? $Format["ForceLabels"] : null;
$DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX;
$DrawVerticalLine = isset($Format["DrawVerticalLine"]) ? $Format["DrawVerticalLine"] : false;
$VerticalLineR = isset($Format["VerticalLineR"]) ? $Format["VerticalLineR"] : 0;
$VerticalLineG = isset($Format["VerticalLineG"]) ? $Format["VerticalLineG"] : 0;
$VerticalLineB = isset($Format["VerticalLineB"]) ? $Format["VerticalLineB"] : 0;
$VerticalLineAlpha = isset($Format["VerticalLineAlpha"]) ? $Format["VerticalLineAlpha"] : 40;
$VerticalLineTicks = isset($Format["VerticalLineTicks"]) ? $Format["VerticalLineTicks"] : 2;
$Data = $this->DataSet->getData();
list($XMargin, $XDivs) = $this->scaleGetXSettings();
if (!is_array($Indexes)) {
$Index = $Indexes;
$Indexes = [];
$Indexes[] = $Index;
}
if (!is_array($SeriesName)) {
$SerieName = $SeriesName;
$SeriesName = [];
$SeriesName[] = $SerieName;
}
if ($ForceLabels != null && !is_array($ForceLabels)) {
$ForceLabel = $ForceLabels;
$ForceLabels = [];
$ForceLabels[] = $ForceLabel;
}
foreach ($Indexes as $Key => $Index) {
$Series = [];
if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
if ($XDivs == 0) {
$XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
} else {
$XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
}
$X = $this->GraphAreaX1 + $XMargin + $Index * $XStep;
if ($DrawVerticalLine) {
$this->drawLine(
$X,
$this->GraphAreaY1 + $Data["YMargin"],
$X,
$this->GraphAreaY2 - $Data["YMargin"],
[
"R" => $VerticalLineR,
"G" => $VerticalLineG,
"B" => $VerticalLineB,
"Alpha" => $VerticalLineAlpha,
"Ticks" => $VerticalLineTicks
]
);
}
$MinY = $this->GraphAreaY2;
foreach ($SeriesName as $SerieName) {
if (isset($Data["Series"][$SerieName]["Data"][$Index])) {
$AxisID = $Data["Series"][$SerieName]["Axis"];
$XAxisMode = $Data["XAxisDisplay"];
$XAxisFormat = $Data["XAxisFormat"];
$XAxisUnit = $Data["XAxisUnit"];
$AxisMode = $Data["Axis"][$AxisID]["Display"];
$AxisFormat = $Data["Axis"][$AxisID]["Format"];
$AxisUnit = $Data["Axis"][$AxisID]["Unit"];
$XLabel = "";
if (isset($Data["Abscissa"])
&& isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index])
) {
$XLabel = $this->scaleFormat(
$Data["Series"][$Data["Abscissa"]]["Data"][$Index],
$XAxisMode,
$XAxisFormat,
$XAxisUnit
);
}
if ($OverrideTitle != null) {
$Description = $OverrideTitle;
} elseif (count($SeriesName) == 1) {
$Description = $Data["Series"][$SerieName]["Description"] . " - " . $XLabel;
} elseif (isset($Data["Abscissa"])
&& isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index])
) {
$Description = $XLabel;
}
$Serie = [
"R" => $Data["Series"][$SerieName]["Color"]["R"],
"G" => $Data["Series"][$SerieName]["Color"]["G"],
"B" => $Data["Series"][$SerieName]["Color"]["B"],
"Alpha" => $Data["Series"][$SerieName]["Color"]["Alpha"]
];
if (count($SeriesName) == 1
&& isset($Data["Series"][$SerieName]["XOffset"])
) {
$SerieOffset = $Data["Series"][$SerieName]["XOffset"];
} else {
$SerieOffset = 0;
}
$Value = $Data["Series"][$SerieName]["Data"][$Index];
if ($Value == VOID) {
$Value = "NaN";
}
if ($ForceLabels != null) {
$Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set";
} else {
$Caption = $this->scaleFormat($Value, $AxisMode, $AxisFormat, $AxisUnit);
}
if ($this->LastChartLayout == CHART_LAST_LAYOUT_STACKED) {
if ($Value >= 0) {
$LookFor = "+";
} else {
$LookFor = "-";
}
$Value = 0;
$Done = false;
foreach ($Data["Series"] as $Name => $SerieLookup) {
if ($SerieLookup["isDrawable"] == true
&& $Name != $Data["Abscissa"] && !$Done
) {
if (isset($Data["Series"][$Name]["Data"][$Index])
&& $Data["Series"][$Name]["Data"][$Index] != VOID
) {
if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+") {
$Value = $Value + $Data["Series"][$Name]["Data"][$Index];
}
if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-") {
$Value = $Value - $Data["Series"][$Name]["Data"][$Index];
}
if ($Name == $SerieName) {
$Done = true;
}
}
}
}
}
$X = floor($this->GraphAreaX1 + $XMargin + $Index * $XStep + $SerieOffset);
$Y = floor($this->scaleComputeY($Value, ["AxisID" => $AxisID]));
if ($Y < $MinY) {
$MinY = $Y;
}
if ($DrawPoint == LABEL_POINT_CIRCLE) {
$this->drawFilledCircle(
$X,
$Y,
3,
[
"R" => 255,
"G" => 255,
"B" => 255,
"BorderR" => 0,
"BorderG" => 0,
"BorderB" => 0
]
);
} elseif ($DrawPoint == LABEL_POINT_BOX) {
$this->drawFilledRectangle(
$X - 2,
$Y - 2,
$X + 2,
$Y + 2,
[
"R" => 255,
"G" => 255,
"B" => 255,
"BorderR" => 0,
"BorderG" => 0,
"BorderB" => 0
]
);
}
$Series[] = ["Format" => $Serie, "Caption" => $Caption];
}
}
$this->drawLabelBox($X, $MinY - 3, $Description, $Series, $Format);
} else {
if ($XDivs == 0) {
$XStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
} else {
$XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
}
$Y = $this->GraphAreaY1 + $XMargin + $Index * $XStep;
if ($DrawVerticalLine) {
$this->drawLine(
$this->GraphAreaX1 + $Data["YMargin"],
$Y,
$this->GraphAreaX2 - $Data["YMargin"],
$Y,
[
"R" => $VerticalLineR,
"G" => $VerticalLineG,
"B" => $VerticalLineB,
"Alpha" => $VerticalLineAlpha,
"Ticks" => $VerticalLineTicks
]
);
}
$MinX = $this->GraphAreaX2;
foreach ($SeriesName as $Key => $SerieName) {
if (isset($Data["Series"][$SerieName]["Data"][$Index])) {
$AxisID = $Data["Series"][$SerieName]["Axis"];
$XAxisMode = $Data["XAxisDisplay"];
$XAxisFormat = $Data["XAxisFormat"];
$XAxisUnit = $Data["XAxisUnit"];
$AxisMode = $Data["Axis"][$AxisID]["Display"];
$AxisFormat = $Data["Axis"][$AxisID]["Format"];
$AxisUnit = $Data["Axis"][$AxisID]["Unit"];
$XLabel = "";
if (isset($Data["Abscissa"])
&& isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index])
) {
$XLabel = $this->scaleFormat(
$Data["Series"][$Data["Abscissa"]]["Data"][$Index],
$XAxisMode,
$XAxisFormat,
$XAxisUnit
);
}
if ($OverrideTitle != null) {
$Description = $OverrideTitle;
} elseif (count($SeriesName) == 1) {
if (isset($Data["Abscissa"])
&& isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index])
) {
$Description = $Data["Series"][$SerieName]["Description"] . " - " . $XLabel;
}
} elseif (isset($Data["Abscissa"])
&& isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index])
) {
$Description = $XLabel;
}
$Serie = [];
if (isset($Data["Extended"]["Palette"][$Index])) {
$Serie["R"] = $Data["Extended"]["Palette"][$Index]["R"];
$Serie["G"] = $Data["Extended"]["Palette"][$Index]["G"];
$Serie["B"] = $Data["Extended"]["Palette"][$Index]["B"];
$Serie["Alpha"] = $Data["Extended"]["Palette"][$Index]["Alpha"];
} else {
$Serie["R"] = $Data["Series"][$SerieName]["Color"]["R"];
$Serie["G"] = $Data["Series"][$SerieName]["Color"]["G"];
$Serie["B"] = $Data["Series"][$SerieName]["Color"]["B"];
$Serie["Alpha"] = $Data["Series"][$SerieName]["Color"]["Alpha"];
}
if (count($SeriesName) == 1 && isset($Data["Series"][$SerieName]["XOffset"])) {
$SerieOffset = $Data["Series"][$SerieName]["XOffset"];
} else {
$SerieOffset = 0;
}
$Value = $Data["Series"][$SerieName]["Data"][$Index];
if ($ForceLabels != null) {
$Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set";
} else {
$Caption = $this->scaleFormat($Value, $AxisMode, $AxisFormat, $AxisUnit);
}
if ($Value == VOID) {
$Value = "NaN";
}
if ($this->LastChartLayout == CHART_LAST_LAYOUT_STACKED) {
if ($Value >= 0) {
$LookFor = "+";
} else {
$LookFor = "-";
}
$Value = 0;
$Done = false;
foreach ($Data["Series"] as $Name => $SerieLookup) {
if ($SerieLookup["isDrawable"] == true
&& $Name != $Data["Abscissa"]
&& !$Done
) {
if (isset($Data["Series"][$Name]["Data"][$Index])
&& $Data["Series"][$Name]["Data"][$Index] != VOID
) {
if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+") {
$Value = $Value + $Data["Series"][$Name]["Data"][$Index];
}
if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-") {
$Value = $Value - $Data["Series"][$Name]["Data"][$Index];
}
if ($Name == $SerieName) {
$Done = true;
}
}
}
}
}
$X = floor($this->scaleComputeY($Value, ["AxisID" => $AxisID]));
$Y = floor($this->GraphAreaY1 + $XMargin + $Index * $XStep + $SerieOffset);
if ($X < $MinX) {
$MinX = $X;
}
if ($DrawPoint == LABEL_POINT_CIRCLE) {
$this->drawFilledCircle(
$X,
$Y,
3,
[
"R" => 255,
"G" => 255,
"B" => 255,
"BorderR" => 0,
"BorderG" => 0,
"BorderB" => 0
]
);
} elseif ($DrawPoint == LABEL_POINT_BOX) {
$this->drawFilledRectangle(
$X - 2,
$Y - 2,
$X + 2,
$Y + 2,
[
"R" => 255,
"G" => 255,
"B" => 255,
"BorderR" => 0,
"BorderG" => 0,
"BorderB" => 0
]
);
}
$Series[] = ["Format" => $Serie, "Caption" => $Caption];
}
}
$this->drawLabelBox($MinX, $Y - 3, $Description, $Series, $Format);
}
}
}
/**
* @param GdImage|resource $image
* @param array $points
* @param int $numPoints
* @param int $color
* @return void
*/
protected function imageFilledPolygonWrapper(
$image,
array $points,
$numPoints,
$color
) {
if (version_compare(PHP_VERSION, '8.1.0') === -1) {
imagefilledpolygon($image, $points, $numPoints, $color);
} else {
imagefilledpolygon($image, $points, $color);
}
}
}