Files
Chamilo/vendor/szymach/c-pchart/src/Chart/Pie.php
2025-08-14 22:41:49 +02:00

2321 lines
86 KiB
PHP

<?php
namespace CpChart\Chart;
use CpChart\Data;
use CpChart\Image;
/**
* Pie - class to draw pie charts
*
* Version : 2.1.4
* Made by : Jean-Damien POGOLOTTI
* Last Update : 19/01/2014
*
* This file can be distributed under the license you can find at :
*
* http://www.pchart.net/license
*
* You can find the whole class documentation on the pChart web site.
*/
class Pie
{
/**
* @var Image
*/
public $pChartObject;
/**
* @var Data
*/
public $pDataObject;
/**
* @var array
*/
public $LabelPos = [];
/**
* @param Image $pChartObject
* @param Data $pDataObject
*/
public function __construct(Image $pChartObject, Data $pDataObject)
{
$this->pChartObject = $pChartObject;
$this->pDataObject = $pDataObject;
}
/**
* Draw a pie chart
* @param int $X
* @param int $Y
* @param array $Format
* @return int
*/
public function draw2DPie($X, $Y, array $Format = [])
{
$Radius = isset($Format["Radius"]) ? $Format["Radius"] : 60;
$Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0;
$DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0;
$DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0;
$SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : true;
$Border = isset($Format["Border"]) ? $Format["Border"] : false;
$BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255;
$BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255;
$BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255;
$Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : false;
$DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : false;
$LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : false;
$LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL;
$LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0;
$LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0;
$LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0;
$LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100;
$WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : null;
$ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE;
$ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15;
$ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : "";
$ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255;
$ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255;
$ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255;
$ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100;
$RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
$Data = $this->pDataObject->getData();
$Palette = $this->pDataObject->getPalette();
/* Do we have an abscissa serie defined? */
if ($Data["Abscissa"] == "") {
return PIE_NO_ABSCISSA;
}
/* Try to find the data serie */
$DataSerie = null;
foreach (array_keys($Data["Series"]) as $SerieName) {
if ($SerieName != $Data["Abscissa"]) {
$DataSerie = $SerieName;
}
}
/* Do we have data to compute? */
if (!$DataSerie) {
return PIE_NO_DATASERIE;
}
/* Remove unused data */
list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]);
/* Compute the pie sum */
$SerieSum = $this->pDataObject->getSum($DataSerie);
/* Do we have data to draw? */
if ($SerieSum == 0) {
return PIE_SUMISNULL;
}
/* Dump the real number of data to draw */
$Values = [];
foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
if ($Value != 0) {
$Values[] = $Value;
}
}
/* Compute the wasted angular space between series */
if (count($Values) == 1) {
$WastedAngular = 0;
} else {
$WastedAngular = count($Values) * $DataGapAngle;
}
/* Compute the scale */
$ScaleFactor = (360 - $WastedAngular) / $SerieSum;
$RestoreShadow = $this->pChartObject->Shadow;
if ($this->pChartObject->Shadow) {
$this->pChartObject->Shadow = false;
$ShadowFormat = $Format;
$ShadowFormat["Shadow"] = true;
$this->draw2DPie(
$X + $this->pChartObject->ShadowX,
$Y + $this->pChartObject->ShadowY,
$ShadowFormat
);
}
/* Draw the polygon pie elements */
$Step = 360 / (2 * PI * $Radius);
$Offset = 0;
$ID = 0;
foreach ($Values as $Key => $Value) {
if ($Shadow) {
$Settings = [
"R" => $this->pChartObject->ShadowR,
"G" => $this->pChartObject->ShadowG,
"B" => $this->pChartObject->ShadowB,
"Alpha" => $this->pChartObject->Shadowa
];
} else {
if (!isset($Palette[$ID]["R"])) {
$Color = $this->pChartObject->getRandomColor();
$Palette[$ID] = $Color;
$this->pDataObject->savePalette($ID, $Color);
}
$Settings = [
"R" => $Palette[$ID]["R"],
"G" => $Palette[$ID]["G"],
"B" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
}
if (!$SecondPass && !$Shadow) {
if (!$Border) {
$Settings["Surrounding"] = 10;
} else {
$Settings["BorderR"] = $BorderR;
$Settings["BorderG"] = $BorderG;
$Settings["BorderB"] = $BorderB;
}
}
$EndAngle = $Offset + ($Value * $ScaleFactor);
if ($EndAngle > 360) {
$EndAngle = 360;
}
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
if ($DataGapAngle == 0) {
$X0 = $X;
$Y0 = $Y;
} else {
$X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X;
$Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius + $Y;
}
$Plots = [$X0, $Y0];
for ($i = $Offset; $i <= $EndAngle; $i = $i + $Step) {
$Xc = cos(($i - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($i - 90) * PI / 180) * $Radius + $Y;
if ($SecondPass && ($i < 90)) {
$Yc++;
}
if ($SecondPass && ($i > 180 && $i < 270)) {
$Xc++;
}
if ($SecondPass && ($i >= 270)) {
$Xc++;
$Yc++;
}
$Plots[] = $Xc;
$Plots[] = $Yc;
}
$this->pChartObject->drawPolygon($Plots, $Settings);
if ($RecordImageMap && !$Shadow) {
$this->pChartObject->addToImageMap(
"POLY",
$this->arraySerialize($Plots),
$this->pChartObject->toHTMLColor(
$Palette[$ID]["R"],
$Palette[$ID]["G"],
$Palette[$ID]["B"]
),
$Data["Series"][$Data["Abscissa"]]["Data"][$Key],
$Value
);
}
if ($DrawLabels && !$Shadow && !$SecondPass) {
if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
$Settings = [
"FillR" => $Palette[$ID]["R"],
"FillG" => $Palette[$ID]["G"],
"FillB" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
} else {
$Settings = [
"FillR" => $LabelR,
"FillG" => $LabelG,
"FillB" => $LabelB,
"Alpha" => $LabelAlpha
];
}
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
$Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($Angle - 90) * PI / 180) * $Radius + $Y;
$Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key];
if ($LabelStacked) {
$this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, true, $X, $Y, $Radius);
} else {
$this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, false);
}
}
$Offset = $i + $DataGapAngle;
$ID++;
}
/* Second pass to smooth the angles */
if ($SecondPass) {
$Step = 360 / (2 * PI * $Radius);
$Offset = 0;
$ID = 0;
foreach ($Values as $Key => $Value) {
$FirstPoint = true;
if ($Shadow) {
$Settings = [
"R" => $this->pChartObject->ShadowR,
"G" => $this->pChartObject->ShadowG,
"B" => $this->pChartObject->ShadowB,
"Alpha" => $this->pChartObject->Shadowa
];
} else {
if ($Border) {
$Settings = ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB];
} else {
$Settings = [
"R" => $Palette[$ID]["R"],
"G" => $Palette[$ID]["G"],
"B" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
}
}
$EndAngle = $Offset + ($Value * $ScaleFactor);
if ($EndAngle > 360) {
$EndAngle = 360;
}
if ($DataGapAngle == 0) {
$X0 = $X;
$Y0 = $Y;
} else {
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
$X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X;
$Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius + $Y;
}
$Plots[] = $X0;
$Plots[] = $Y0;
for ($i = $Offset; $i <= $EndAngle; $i = $i + $Step) {
$Xc = cos(($i - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($i - 90) * PI / 180) * $Radius + $Y;
if ($FirstPoint) {
$this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings);
$FirstPoint = false;
}
$this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings);
}
$this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings);
if ($DrawLabels && !$Shadow) {
if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
$Settings = [
"FillR" => $Palette[$ID]["R"],
"FillG" => $Palette[$ID]["G"],
"FillB" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
} else {
$Settings = [
"FillR" => $LabelR,
"FillG" => $LabelG,
"FillB" => $LabelB,
"Alpha" => $LabelAlpha
];
}
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
$Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($Angle - 90) * PI / 180) * $Radius + $Y;
$Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key];
if ($LabelStacked) {
$this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, true, $X, $Y, $Radius);
} else {
$this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, false);
}
}
$Offset = $i + $DataGapAngle;
$ID++;
}
}
if ($WriteValues != null && !$Shadow) {
$Step = 360 / (2 * PI * $Radius);
$Offset = 0;
$ID = count($Values) - 1;
$Settings = [
"Align" => TEXT_ALIGN_MIDDLEMIDDLE,
"R" => $ValueR,
"G" => $ValueG,
"B" => $ValueB,
"Alpha" => $ValueAlpha
];
foreach ($Values as $Key => $Value) {
$EndAngle = ($Value * $ScaleFactor) + $Offset;
if ((int) $EndAngle > 360) {
$EndAngle = 0;
}
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
if ($ValuePosition == PIE_VALUE_OUTSIDE) {
$Xc = cos(($Angle - 90) * PI / 180) * ($Radius + $ValuePadding) + $X;
$Yc = sin(($Angle - 90) * PI / 180) * ($Radius + $ValuePadding) + $Y;
} else {
$Xc = cos(($Angle - 90) * PI / 180) * ($Radius) / 2 + $X;
$Yc = sin(($Angle - 90) * PI / 180) * ($Radius) / 2 + $Y;
}
if ($WriteValues == PIE_VALUE_PERCENTAGE) {
$Display = round((100 / $SerieSum) * $Value, $Precision) . "%";
} elseif ($WriteValues == PIE_VALUE_NATURAL) {
$Display = $Value . $ValueSuffix;
}
$this->pChartObject->drawText($Xc, $Yc, $Display, $Settings);
$Offset = $EndAngle + $DataGapAngle;
$ID--;
}
}
if ($DrawLabels && $LabelStacked) {
$this->writeShiftedLabels();
}
$this->pChartObject->Shadow = $RestoreShadow;
return PIE_RENDERED;
}
/**
* Draw a 3D pie chart
* @param int $X
* @param int $Y
* @param array $Format
* @return int
*/
public function draw3DPie($X, $Y, array $Format = [])
{
/* Rendering layout */
$Radius = isset($Format["Radius"]) ? $Format["Radius"] : 80;
$Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0;
$SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : .5;
$SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 20;
$DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0;
$DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0;
$SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : true;
$Border = isset($Format["Border"]) ? $Format["Border"] : false;
$Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : false;
$DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : false;
$LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : false;
$LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL;
$LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0;
$LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0;
$LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0;
$LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100;
$WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : null; //PIE_VALUE_PERCENTAGE
$ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_INSIDE;
$ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15;
$ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : "";
$ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255;
$ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255;
$ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255;
$ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100;
$RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
/* Error correction for overlaying rounded corners */
if ($SkewFactor < .5) {
$SkewFactor = .5;
}
/* Data Processing */
$Data = $this->pDataObject->getData();
$Palette = $this->pDataObject->getPalette();
/* Do we have an abscissa serie defined? */
if ($Data["Abscissa"] == "") {
return PIE_NO_ABSCISSA;
}
/* Try to find the data serie */
$DataSerie = null;
foreach ($Data["Series"] as $SerieName => $SerieData) {
if ($SerieName != $Data["Abscissa"]) {
$DataSerie = $SerieName;
}
}
/* Do we have data to compute? */
if (!$DataSerie) {
return PIE_NO_DATASERIE;
}
/* Remove unused data */
list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]);
/* Compute the pie sum */
$SerieSum = $this->pDataObject->getSum($DataSerie);
/* Do we have data to draw? */
if ($SerieSum == 0) {
return PIE_SUMISNULL;
}
/* Dump the real number of data to draw */
$Values = [];
foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
if ($Value != 0) {
$Values[] = $Value;
}
}
/* Compute the wasted angular space between series */
if (count($Values) == 1) {
$WastedAngular = 0;
} else {
$WastedAngular = count($Values) * $DataGapAngle;
}
/* Compute the scale */
$ScaleFactor = (360 - $WastedAngular) / $SerieSum;
$RestoreShadow = $this->pChartObject->Shadow;
if ($this->pChartObject->Shadow) {
$this->pChartObject->Shadow = false;
}
/* Draw the polygon pie elements */
$Step = 360 / (2 * PI * $Radius);
$Offset = 360;
$ID = count($Values) - 1;
$Values = array_reverse($Values);
$Slice = 0;
$Slices = [];
$SliceColors = [];
$Visible = [];
$SliceAngle = [];
foreach ($Values as $Key => $Value) {
if (!isset($Palette[$ID]["R"])) {
$Color = $this->pChartObject->getRandomColor();
$Palette[$ID] = $Color;
$this->pDataObject->savePalette($ID, $Color);
}
$Settings = [
"R" => $Palette[$ID]["R"],
"G" => $Palette[$ID]["G"],
"B" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
$SliceColors[$Slice] = $Settings;
$StartAngle = $Offset;
$EndAngle = $Offset - ($Value * $ScaleFactor);
if ($EndAngle < 0) {
$EndAngle = 0;
}
if ($StartAngle > 180) {
$Visible[$Slice]["Start"] = true;
} else {
$Visible[$Slice]["Start"] = true;
}
if ($EndAngle < 180) {
$Visible[$Slice]["End"] = false;
} else {
$Visible[$Slice]["End"] = true;
}
if ($DataGapAngle == 0) {
$X0 = $X;
$Y0 = $Y;
} else {
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
$X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X;
$Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius * $SkewFactor + $Y;
}
$Slices[$Slice][] = $X0;
$Slices[$Slice][] = $Y0;
$SliceAngle[$Slice][] = 0;
for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) {
$Xc = cos(($i - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($i - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
if (($SecondPass || $RestoreShadow) && ($i < 90)) {
$Yc++;
}
if (($SecondPass || $RestoreShadow) && ($i > 90 && $i < 180)) {
$Xc++;
}
if (($SecondPass || $RestoreShadow) && ($i > 180 && $i < 270)) {
$Xc++;
}
if (($SecondPass || $RestoreShadow) && ($i >= 270)) {
$Xc++;
$Yc++;
}
$Slices[$Slice][] = $Xc;
$Slices[$Slice][] = $Yc;
$SliceAngle[$Slice][] = $i;
}
$Offset = $i - $DataGapAngle;
$ID--;
$Slice++;
}
/* Draw the bottom shadow if needed */
if ($RestoreShadow && ($this->pChartObject->ShadowX != 0 || $this->pChartObject->ShadowY != 0)) {
foreach ($Slices as $SliceID => $Plots) {
$ShadowPie = [];
for ($i = 0; $i < count($Plots); $i = $i + 2) {
$ShadowPie[] = $Plots[$i] + $this->pChartObject->ShadowX;
$ShadowPie[] = $Plots[$i + 1] + $this->pChartObject->ShadowY;
}
$Settings = [
"R" => $this->pChartObject->ShadowR,
"G" => $this->pChartObject->ShadowG,
"B" => $this->pChartObject->ShadowB,
"Alpha" => $this->pChartObject->Shadowa,
"NoBorder" => true
];
$this->pChartObject->drawPolygon($ShadowPie, $Settings);
}
$Step = 360 / (2 * PI * $Radius);
$Offset = 360;
foreach ($Values as $Key => $Value) {
$EndAngle = $Offset - ($Value * $ScaleFactor);
if ($EndAngle < 0) {
$EndAngle = 0;
}
for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) {
$Xc = cos(($i - 90) * PI / 180) * $Radius + $X + $this->pChartObject->ShadowX;
$Yc = sin(($i - 90) * PI / 180) * $Radius * $SkewFactor + $Y + $this->pChartObject->ShadowY;
$this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings);
}
$Offset = $i - $DataGapAngle;
$ID--;
}
}
/* Draw the bottom pie splice */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["NoBorder"] = true;
$this->pChartObject->drawPolygon($Plots, $Settings);
if ($SecondPass) {
$Settings = $SliceColors[$SliceID];
if ($Border) {
$Settings["R"] += 30;
$Settings["G"] += 30;
$Settings["B"] += 30;
}
/* Empty error handling */
if (isset($SliceAngle[$SliceID][1])) {
$Angle = $SliceAngle[$SliceID][1];
$Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
$this->pChartObject->drawLine($Plots[0], $Plots[1], $Xc, $Yc, $Settings);
$Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1];
$Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
$this->pChartObject->drawLine($Plots[0], $Plots[1], $Xc, $Yc, $Settings);
}
}
}
/* Draw the two vertical edges */
$Slices = array_reverse($Slices);
$SliceColors = array_reverse($SliceColors);
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["R"] += 10;
$Settings["G"] += 10;
$Settings["B"] += 10;
$Settings["NoBorder"] = true;
/* Empty error handling */
if ($Visible[$SliceID]["Start"] && isset($Plots[2])) {
$this->pChartObject->drawLine(
$Plots[2],
$Plots[3],
$Plots[2],
$Plots[3] - $SliceHeight,
["R" => $Settings["R"], "G" => $Settings["G"], "B" => $Settings["B"]]
);
$Border = [];
$Border[] = $Plots[0];
$Border[] = $Plots[1];
$Border[] = $Plots[0];
$Border[] = $Plots[1] - $SliceHeight;
$Border[] = $Plots[2];
$Border[] = $Plots[3] - $SliceHeight;
$Border[] = $Plots[2];
$Border[] = $Plots[3];
$this->pChartObject->drawPolygon($Border, $Settings);
}
}
$Slices = array_reverse($Slices);
$SliceColors = array_reverse($SliceColors);
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["R"] += 10;
$Settings["G"] += 10;
$Settings["B"] += 10;
$Settings["NoBorder"] = true;
if ($Visible[$SliceID]["End"]) {
$this->pChartObject->drawLine(
$Plots[count($Plots) - 2],
$Plots[count($Plots) - 1],
$Plots[count($Plots) - 2],
$Plots[count($Plots) - 1] - $SliceHeight,
["R" => $Settings["R"], "G" => $Settings["G"], "B" => $Settings["B"]]
);
$Border = [];
$Border[] = $Plots[0];
$Border[] = $Plots[1];
$Border[] = $Plots[0];
$Border[] = $Plots[1] - $SliceHeight;
$Border[] = $Plots[count($Plots) - 2];
$Border[] = $Plots[count($Plots) - 1] - $SliceHeight;
$Border[] = $Plots[count($Plots) - 2];
$Border[] = $Plots[count($Plots) - 1];
$this->pChartObject->drawPolygon($Border, $Settings);
}
}
/* Draw the rounded edges */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["R"] += 10;
$Settings["G"] += 10;
$Settings["B"] += 10;
$Settings["NoBorder"] = true;
for ($j = 2; $j < count($Plots) - 2; $j = $j + 2) {
$Angle = $SliceAngle[$SliceID][$j / 2];
if ($Angle < 270 && $Angle > 90) {
$Border = [];
$Border[] = $Plots[$j];
$Border[] = $Plots[$j + 1];
$Border[] = $Plots[$j + 2];
$Border[] = $Plots[$j + 3];
$Border[] = $Plots[$j + 2];
$Border[] = $Plots[$j + 3] - $SliceHeight;
$Border[] = $Plots[$j];
$Border[] = $Plots[$j + 1] - $SliceHeight;
$this->pChartObject->drawPolygon($Border, $Settings);
}
}
if ($SecondPass) {
$Settings = $SliceColors[$SliceID];
if (count($Border)) {
$Settings["R"] += 30;
$Settings["G"] += 30;
$Settings["B"] += 30;
}
/* Empty error handling */
if (isset($SliceAngle[$SliceID][1])) {
$Angle = $SliceAngle[$SliceID][1];
if ($Angle < 270 && $Angle > 90) {
$Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
$this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings);
}
}
$Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1];
if ($Angle < 270 && $Angle > 90) {
$Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
$this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings);
}
if (isset($SliceAngle[$SliceID][1])
&& $SliceAngle[$SliceID][1] > 270
&& $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1] < 270
) {
$Xc = cos((270 - 90) * PI / 180) * $Radius + $X;
$Yc = sin((270 - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
$this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings);
}
if (isset($SliceAngle[$SliceID][1])
&& $SliceAngle[$SliceID][1] > 90
&& $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1] < 90
) {
$Xc = cos((0) * PI / 180) * $Radius + $X;
$Yc = sin((0) * PI / 180) * $Radius * $SkewFactor + $Y;
$this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings);
}
}
}
/* Draw the top splice */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["R"] += 20;
$Settings["G"] += 20;
$Settings["B"] += 20;
$Top = [];
for ($j = 0; $j < count($Plots); $j = $j + 2) {
$Top[] = $Plots[$j];
$Top[] = $Plots[$j + 1] - $SliceHeight;
}
$this->pChartObject->drawPolygon($Top, $Settings);
if ($RecordImageMap && !$Shadow) {
$this->pChartObject->addToImageMap(
"POLY",
$this->arraySerialize($Top),
$this->pChartObject->toHTMLColor(
$Settings["R"],
$Settings["G"],
$Settings["B"]
),
$Data["Series"][$Data["Abscissa"]]["Data"][count($Slices) - $SliceID - 1],
$Values[$SliceID]
);
}
}
/* Second pass to smooth the angles */
if ($SecondPass) {
$Step = 360 / (2 * PI * $Radius);
$Offset = 360;
$ID = count($Values) - 1;
foreach ($Values as $Key => $Value) {
$FirstPoint = true;
if ($Shadow) {
$Settings = [
"R" => $this->pChartObject->ShadowR,
"G" => $this->pChartObject->ShadowG,
"B" => $this->pChartObject->ShadowB,
"Alpha" => $this->pChartObject->Shadowa
];
} else {
if ($Border) {
$Settings = [
"R" => $Palette[$ID]["R"] + 30,
"G" => $Palette[$ID]["G"] + 30,
"B" => $Palette[$ID]["B"] + 30,
"Alpha" => $Palette[$ID]["Alpha"]
];
} else {
$Settings = [
"R" => $Palette[$ID]["R"],
"G" => $Palette[$ID]["G"],
"B" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
}
}
$EndAngle = $Offset - ($Value * $ScaleFactor);
if ($EndAngle < 0) {
$EndAngle = 0;
}
if ($DataGapAngle == 0) {
$X0 = $X;
$Y0 = $Y - $SliceHeight;
} else {
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
$X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X;
$Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius * $SkewFactor + $Y - $SliceHeight;
}
$Plots[] = $X0;
$Plots[] = $Y0;
for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) {
$Xc = cos(($i - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($i - 90) * PI / 180) * $Radius * $SkewFactor + $Y - $SliceHeight;
if ($FirstPoint) {
$this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings);
$FirstPoint = false;
}
$this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings);
if ($i < 270 && $i > 90) {
$this->pChartObject->drawAntialiasPixel($Xc, $Yc + $SliceHeight, $Settings);
}
}
$this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings);
$Offset = $i - $DataGapAngle;
$ID--;
}
}
if ($WriteValues != null) {
$Step = 360 / (2 * PI * $Radius);
$Offset = 360;
$ID = count($Values) - 1;
$Settings = [
"Align" => TEXT_ALIGN_MIDDLEMIDDLE,
"R" => $ValueR,
"G" => $ValueG,
"B" => $ValueB,
"Alpha" => $ValueAlpha
];
foreach ($Values as $Key => $Value) {
$EndAngle = $Offset - ($Value * $ScaleFactor);
if ($EndAngle < 0) {
$EndAngle = 0;
}
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
if ($ValuePosition == PIE_VALUE_OUTSIDE) {
$Xc = cos(($Angle - 90) * PI / 180) * ($Radius + $ValuePadding) + $X;
$Yc = sin(($Angle - 90) * PI / 180)
* (($Radius * $SkewFactor) + $ValuePadding)
+ $Y - $SliceHeight
;
} else {
$Xc = cos(($Angle - 90) * PI / 180) * ($Radius) / 2 + $X;
$Yc = sin(($Angle - 90) * PI / 180) * ($Radius * $SkewFactor) / 2 + $Y - $SliceHeight;
}
if ($WriteValues == PIE_VALUE_PERCENTAGE) {
$Display = round((100 / $SerieSum) * $Value, $Precision) . "%";
} elseif ($WriteValues == PIE_VALUE_NATURAL) {
$Display = $Value . $ValueSuffix;
}
$this->pChartObject->drawText($Xc, $Yc, $Display, $Settings);
$Offset = $EndAngle - $DataGapAngle;
$ID--;
}
}
if ($DrawLabels) {
$Step = 360 / (2 * PI * $Radius);
$Offset = 360;
$ID = count($Values) - 1;
foreach ($Values as $Key => $Value) {
if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
$Settings = [
"FillR" => $Palette[$ID]["R"],
"FillG" => $Palette[$ID]["G"],
"FillB" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
} else {
$Settings = [
"FillR" => $LabelR,
"FillG" => $LabelG,
"FillB" => $LabelB,
"Alpha" => $LabelAlpha
];
}
$EndAngle = $Offset - ($Value * $ScaleFactor);
if ($EndAngle < 0) {
$EndAngle = 0;
}
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
$Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
$Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y - $SliceHeight;
if (isset($Data["Series"][$Data["Abscissa"]]["Data"][$ID])) {
$Label = $Data["Series"][$Data["Abscissa"]]["Data"][$ID];
if ($LabelStacked) {
$this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, true, $X, $Y, $Radius, true);
} else {
$this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, false);
}
}
$Offset = $EndAngle - $DataGapAngle;
$ID--;
}
}
if ($DrawLabels && $LabelStacked) {
$this->writeShiftedLabels();
}
$this->pChartObject->Shadow = $RestoreShadow;
return PIE_RENDERED;
}
/**
* Draw the legend of pie chart
* @param int $X
* @param int $Y
* @param array $Format
* @return int
*/
public function drawPieLegend($X, $Y, array $Format = [])
{
$FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName;
$FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize;
$FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->pChartObject->FontColorR;
$FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->pChartObject->FontColorG;
$FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->pChartObject->FontColorB;
$BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5;
$Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5;
$R = isset($Format["R"]) ? $Format["R"] : 200;
$G = isset($Format["G"]) ? $Format["G"] : 200;
$B = isset($Format["B"]) ? $Format["B"] : 200;
$Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
$BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255;
$BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255;
$BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255;
$Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
$Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND;
$Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL;
if ($Surrounding != null) {
$BorderR = $R + $Surrounding;
$BorderG = $G + $Surrounding;
$BorderB = $B + $Surrounding;
}
$YStep = max($this->pChartObject->FontSize, $BoxSize) + 5;
$XStep = $BoxSize + 5;
/* Data Processing */
$Data = $this->pDataObject->getData();
$Palette = $this->pDataObject->getPalette();
/* Do we have an abscissa serie defined? */
if ($Data["Abscissa"] == "") {
return PIE_NO_ABSCISSA;
}
$Boundaries = [];
$Boundaries["L"] = $X;
$Boundaries["T"] = $Y;
$Boundaries["R"] = 0;
$Boundaries["B"] = 0;
$vY = $Y;
$vX = $X;
foreach ($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) {
$BoxArray = $this->pChartObject->getTextBox(
$vX + $BoxSize + 4,
$vY + $BoxSize / 2,
$FontName,
$FontSize,
0,
$Value
);
if ($Mode == LEGEND_VERTICAL) {
if ($Boundaries["T"] > $BoxArray[2]["Y"] + $BoxSize / 2) {
$Boundaries["T"] = $BoxArray[2]["Y"] + $BoxSize / 2;
}
if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
$Boundaries["R"] = $BoxArray[1]["X"] + 2;
}
if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $BoxSize / 2) {
$Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $BoxSize / 2;
}
$vY = $vY + $YStep;
} elseif ($Mode == LEGEND_HORIZONTAL) {
if ($Boundaries["T"] > $BoxArray[2]["Y"] + $BoxSize / 2) {
$Boundaries["T"] = $BoxArray[2]["Y"] + $BoxSize / 2;
}
if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
$Boundaries["R"] = $BoxArray[1]["X"] + 2;
}
if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $BoxSize / 2) {
$Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $BoxSize / 2;
}
$vX = $Boundaries["R"] + $XStep;
}
}
$vY = $vY - $YStep;
$vX = $vX - $XStep;
$TopOffset = $Y - $Boundaries["T"];
if ($Boundaries["B"] - ($vY + $BoxSize) < $TopOffset) {
$Boundaries["B"] = $vY + $BoxSize + $TopOffset;
}
if ($Style == LEGEND_ROUND) {
$this->pChartObject->drawRoundedFilledRectangle(
$Boundaries["L"] - $Margin,
$Boundaries["T"] - $Margin,
$Boundaries["R"] + $Margin,
$Boundaries["B"] + $Margin,
$Margin,
[
"R" => $R,
"G" => $G,
"B" => $B,
"Alpha" => $Alpha,
"BorderR" => $BorderR,
"BorderG" => $BorderG,
"BorderB" => $BorderB
]
);
} elseif ($Style == LEGEND_BOX) {
$this->pChartObject->drawFilledRectangle(
$Boundaries["L"] - $Margin,
$Boundaries["T"] - $Margin,
$Boundaries["R"] + $Margin,
$Boundaries["B"] + $Margin,
[
"R" => $R,
"G" => $G,
"B" => $B,
"Alpha" => $Alpha,
"BorderR" => $BorderR,
"BorderG" => $BorderG,
"BorderB" => $BorderB
]
);
}
$RestoreShadow = $this->pChartObject->Shadow;
$this->pChartObject->Shadow = false;
foreach ($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) {
$R = $Palette[$Key]["R"];
$G = $Palette[$Key]["G"];
$B = $Palette[$Key]["B"];
$this->pChartObject->drawFilledRectangle(
$X + 1,
$Y + 1,
$X + $BoxSize + 1,
$Y + $BoxSize + 1,
["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]
);
$this->pChartObject->drawFilledRectangle(
$X,
$Y,
$X + $BoxSize,
$Y + $BoxSize,
["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20]
);
if ($Mode == LEGEND_VERTICAL) {
$this->pChartObject->drawText(
$X + $BoxSize + 4,
$Y + $BoxSize / 2,
$Value,
[
"R" => $FontR,
"G" => $FontG,
"B" => $FontB,
"Align" => TEXT_ALIGN_MIDDLELEFT,
"FontName" => $FontName,
"FontSize" => $FontSize
]
);
$Y = $Y + $YStep;
} elseif ($Mode == LEGEND_HORIZONTAL) {
$BoxArray = $this->pChartObject->drawText(
$X + $BoxSize + 4,
$Y + $BoxSize / 2,
$Value,
[
"R" => $FontR,
"G" => $FontG,
"B" => $FontB,
"Align" => TEXT_ALIGN_MIDDLELEFT,
"FontName" => $FontName,
"FontSize" => $FontSize
]
);
$X = $BoxArray[1]["X"] + 2 + $XStep;
}
}
$this->pChartObject->Shadow = $RestoreShadow;
}
/**
* Set the color of the specified slice
* @param mixed $SliceID
* @param array $Format
*/
public function setSliceColor($SliceID, array $Format = [])
{
$R = isset($Format["R"]) ? $Format["R"] : 0;
$G = isset($Format["G"]) ? $Format["G"] : 0;
$B = isset($Format["B"]) ? $Format["B"] : 0;
$Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
$this->pDataObject->Palette[$SliceID]["R"] = $R;
$this->pDataObject->Palette[$SliceID]["G"] = $G;
$this->pDataObject->Palette[$SliceID]["B"] = $B;
$this->pDataObject->Palette[$SliceID]["Alpha"] = $Alpha;
}
/**
* Internally used compute the label positions
* @param int $X
* @param int $Y
* @param string $Label
* @param int|float $Angle
* @param array $Settings
* @param boolean $Stacked
* @param int $Xc
* @param int $Yc
* @param int $Radius
* @param boolean $Reversed
*/
public function writePieLabel(
$X,
$Y,
$Label,
$Angle,
$Settings,
$Stacked,
$Xc = 0,
$Yc = 0,
$Radius = 0,
$Reversed = false
) {
$LabelOffset = 30;
$FontName = $this->pChartObject->FontName;
$FontSize = $this->pChartObject->FontSize;
if (!$Stacked) {
$Settings["Angle"] = 360 - $Angle;
$Settings["Length"] = 25;
$Settings["Size"] = 8;
$this->pChartObject->drawArrowLabel($X, $Y, " " . $Label . " ", $Settings);
} else {
$X2 = cos(deg2rad($Angle - 90)) * 20 + $X;
$Y2 = sin(deg2rad($Angle - 90)) * 20 + $Y;
$TxtPos = $this->pChartObject->getTextBox($X, $Y, $FontName, $FontSize, 0, $Label);
$Height = $TxtPos[0]["Y"] - $TxtPos[2]["Y"];
$YTop = $Y2 - $Height / 2 - 2;
$YBottom = $Y2 + $Height / 2 + 2;
if (count($this->LabelPos)) {
$Done = false;
foreach ($this->LabelPos as $Key => $Settings) {
if (!$Done) {
$yTopAboveTopBelowBottom = ($YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"]);
$yBottomAboveTopBelowBottom = ($YBottom >= $Settings["YTop"]
&& $YBottom <= $Settings["YBottom"]
);
if ($Angle <= 90
&& ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom)
) {
$this->shift(0, 180, -($Height + 2), $Reversed);
$Done = true;
}
if ($Angle > 90
&& $Angle <= 180
&& ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom)
) {
$this->shift(0, 180, -($Height + 2), $Reversed);
$Done = true;
}
if ($Angle > 180
&& $Angle <= 270
&& ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom)
) {
$this->shift(180, 360, ($Height + 2), $Reversed);
$Done = true;
}
if ($Angle > 270
&& $Angle <= 360
&& ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom)
) {
$this->shift(180, 360, ($Height + 2), $Reversed);
$Done = true;
}
}
}
}
$LabelSettings = [
"YTop" => $YTop,
"YBottom" => $YBottom,
"Label" => $Label,
"Angle" => $Angle,
"X1" => $X,
"Y1" => $Y,
"X2" => $X2,
"Y2" => $Y2
];
if ($Angle <= 180) {
$LabelSettings["X3"] = $Xc + $Radius + $LabelOffset;
}
if ($Angle > 180) {
$LabelSettings["X3"] = $Xc - $Radius - $LabelOffset;
}
$this->LabelPos[] = $LabelSettings;
}
}
/**
* Internally used to shift label positions
* @param int $StartAngle
* @param int $EndAngle
* @param int $Offset
* @param boolean $Reversed
*/
public function shift($StartAngle, $EndAngle, $Offset, $Reversed)
{
if ($Reversed) {
$Offset = -$Offset;
}
foreach ($this->LabelPos as $Key => $Settings) {
if ($Settings["Angle"] > $StartAngle && $Settings["Angle"] <= $EndAngle) {
$this->LabelPos[$Key]["YTop"] = $Settings["YTop"] + $Offset;
$this->LabelPos[$Key]["YBottom"] = $Settings["YBottom"] + $Offset;
$this->LabelPos[$Key]["Y2"] = $Settings["Y2"] + $Offset;
}
}
}
/**
* Internally used to write the re-computed labels
* @return null|int
*/
public function writeShiftedLabels()
{
if (!count($this->LabelPos)) {
return 0;
}
foreach ($this->LabelPos as $Settings) {
$X1 = $Settings["X1"];
$Y1 = $Settings["Y1"];
$X2 = $Settings["X2"];
$Y2 = $Settings["Y2"];
$X3 = $Settings["X3"];
$Angle = $Settings["Angle"];
$Label = $Settings["Label"];
$this->pChartObject->drawArrow($X2, $Y2, $X1, $Y1, ["Size" => 8]);
if ($Angle <= 180) {
$this->pChartObject->drawLine($X2, $Y2, $X3, $Y2);
$this->pChartObject->drawText($X3 + 2, $Y2, $Label, ["Align" => TEXT_ALIGN_MIDDLELEFT]);
} else {
$this->pChartObject->drawLine($X2, $Y2, $X3, $Y2);
$this->pChartObject->drawText($X3 - 2, $Y2, $Label, ["Align" => TEXT_ALIGN_MIDDLERIGHT]);
}
}
}
/**
* Draw a ring chart
* @param int $X
* @param int $Y
* @param array $Format
* @return int
*/
public function draw2DRing($X, $Y, array $Format = [])
{
$OuterRadius = isset($Format["Radius"]) ? $Format["Radius"] : 60; // For compatibility
$OuterRadius = isset($Format["OuterRadius"]) ? $Format["OuterRadius"] : $OuterRadius;
$Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0;
$InnerRadius = isset($Format["InnerRadius"]) ? $Format["InnerRadius"] : 30;
$Border = isset($Format["Border"]) ? $Format["Border"] : false;
$BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255;
$BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255;
$BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255;
$BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100;
$Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : false;
$DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : false;
$LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : false;
$LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL;
$LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0;
$LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0;
$LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0;
$LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100;
$WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : null; //PIE_VALUE_PERCENTAGE
$ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 5;
$ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE;
$ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : "";
$ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255;
$ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255;
$ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255;
$ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100;
$RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
/* Data Processing */
$Data = $this->pDataObject->getData();
$Palette = $this->pDataObject->getPalette();
/* Do we have an abscissa serie defined? */
if ($Data["Abscissa"] == "") {
return PIE_NO_ABSCISSA;
}
/* Try to find the data serie */
$DataSerie = null;
foreach ($Data["Series"] as $SerieName => $SerieData) {
if ($SerieName != $Data["Abscissa"]) {
$DataSerie = $SerieName;
}
}
/* Do we have data to compute? */
if (!$DataSerie) {
return PIE_NO_DATASERIE;
}
/* Remove unused data */
list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]);
/* Compute the pie sum */
$SerieSum = $this->pDataObject->getSum($DataSerie);
/* Do we have data to draw? */
if ($SerieSum == 0) {
return PIE_SUMISNULL;
}
/* Dump the real number of data to draw */
$Values = [];
foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
if ($Value != 0) {
$Values[] = $Value;
}
}
/* Compute the wasted angular space between series */
if (count($Values) == 1) {
$WastedAngular = 0;
} else {
$WastedAngular = 0;
} // count($Values)
/* Compute the scale */
$ScaleFactor = (360 - $WastedAngular) / $SerieSum;
$RestoreShadow = $this->pChartObject->Shadow;
if ($this->pChartObject->Shadow) {
$this->pChartObject->Shadow = false;
$ShadowFormat = $Format;
$ShadowFormat["Shadow"] = true;
$this->draw2DRing(
$X + $this->pChartObject->ShadowX,
$Y + $this->pChartObject->ShadowY,
$ShadowFormat
);
}
/* Draw the polygon pie elements */
$Step = 360 / (2 * PI * $OuterRadius);
$Offset = 0;
$ID = 0;
foreach ($Values as $Key => $Value) {
if ($Shadow) {
$Settings = [
"R" => $this->pChartObject->ShadowR,
"G" => $this->pChartObject->ShadowG,
"B" => $this->pChartObject->ShadowB,
"Alpha" => $this->pChartObject->Shadowa
];
$BorderColor = $Settings;
} else {
if (!isset($Palette[$ID]["R"])) {
$Color = $this->pChartObject->getRandomColor();
$Palette[$ID] = $Color;
$this->pDataObject->savePalette($ID, $Color);
}
$Settings = [
"R" => $Palette[$ID]["R"],
"G" => $Palette[$ID]["G"],
"B" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
if ($Border) {
$BorderColor = [
"R" => $BorderR,
"G" => $BorderG,
"B" => $BorderB,
"Alpha" => $BorderAlpha
];
} else {
$BorderColor = $Settings;
}
}
$Plots = [];
$Boundaries = [];
$AAPixels = [];
$EndAngle = $Offset + ($Value * $ScaleFactor);
if ($EndAngle > 360) {
$EndAngle = 360;
}
for ($i = $Offset; $i <= $EndAngle; $i = $i + $Step) {
$Xc = cos(($i - 90) * PI / 180) * $OuterRadius + $X;
$Yc = sin(($i - 90) * PI / 180) * $OuterRadius + $Y;
if (!isset($Boundaries[0]["X1"])) {
$Boundaries[0]["X1"] = $Xc;
$Boundaries[0]["Y1"] = $Yc;
}
$AAPixels[] = [$Xc, $Yc];
if ($i < 90) {
$Yc++;
}
if ($i > 180 && $i < 270) {
$Xc++;
}
if ($i >= 270) {
$Xc++;
$Yc++;
}
$Plots[] = $Xc;
$Plots[] = $Yc;
}
$Boundaries[1]["X1"] = $Xc;
$Boundaries[1]["Y1"] = $Yc;
$Lasti = $EndAngle;
for ($i = $EndAngle; $i >= $Offset; $i = $i - $Step) {
$Xc = cos(($i - 90) * PI / 180) * ($InnerRadius - 1) + $X;
$Yc = sin(($i - 90) * PI / 180) * ($InnerRadius - 1) + $Y;
if (!isset($Boundaries[1]["X2"])) {
$Boundaries[1]["X2"] = $Xc;
$Boundaries[1]["Y2"] = $Yc;
}
$AAPixels[] = [$Xc, $Yc];
$Xc = cos(($i - 90) * PI / 180) * $InnerRadius + $X;
$Yc = sin(($i - 90) * PI / 180) * $InnerRadius + $Y;
if ($i < 90) {
$Yc++;
}
if ($i > 180 && $i < 270) {
$Xc++;
}
if ($i >= 270) {
$Xc++;
$Yc++;
}
$Plots[] = $Xc;
$Plots[] = $Yc;
}
$Boundaries[0]["X2"] = $Xc;
$Boundaries[0]["Y2"] = $Yc;
/* Draw the polygon */
$this->pChartObject->drawPolygon($Plots, $Settings);
if ($RecordImageMap && !$Shadow) {
$this->pChartObject->addToImageMap(
"POLY",
$this->arraySerialize($Plots),
$this->pChartObject->toHTMLColor(
$Palette[$ID]["R"],
$Palette[$ID]["G"],
$Palette[$ID]["B"]
),
$Data["Series"][$Data["Abscissa"]]["Data"][$Key],
$Value
);
}
/* Smooth the edges using AA */
foreach ($AAPixels as $Pos) {
$this->pChartObject->drawAntialiasPixel($Pos[0], $Pos[1], $BorderColor);
}
$this->pChartObject->drawLine(
$Boundaries[0]["X1"],
$Boundaries[0]["Y1"],
$Boundaries[0]["X2"],
$Boundaries[0]["Y2"],
$BorderColor
);
$this->pChartObject->drawLine(
$Boundaries[1]["X1"],
$Boundaries[1]["Y1"],
$Boundaries[1]["X2"],
$Boundaries[1]["Y2"],
$BorderColor
);
if ($DrawLabels && !$Shadow) {
if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
$Settings = [
"FillR" => $Palette[$ID]["R"],
"FillG" => $Palette[$ID]["G"],
"FillB" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
} else {
$Settings = [
"FillR" => $LabelR,
"FillG" => $LabelG,
"FillB" => $LabelB,
"Alpha" => $LabelAlpha
];
}
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
$Xc = cos(($Angle - 90) * PI / 180) * $OuterRadius + $X;
$Yc = sin(($Angle - 90) * PI / 180) * $OuterRadius + $Y;
$Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key];
if ($LabelStacked) {
$this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, true, $X, $Y, $OuterRadius);
} else {
$this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, false);
}
}
$Offset = $Lasti;
$ID++;
}
if ($DrawLabels && $LabelStacked) {
$this->writeShiftedLabels();
}
if ($WriteValues && !$Shadow) {
$Step = 360 / (2 * PI * $OuterRadius);
$Offset = 0;
foreach ($Values as $Key => $Value) {
$EndAngle = $Offset + ($Value * $ScaleFactor);
if ($EndAngle > 360) {
$EndAngle = 360;
}
$Angle = $Offset + ($Value * $ScaleFactor) / 2;
if ($ValuePosition == PIE_VALUE_OUTSIDE) {
$Xc = cos(($Angle - 90) * PI / 180) * ($OuterRadius + $ValuePadding) + $X;
$Yc = sin(($Angle - 90) * PI / 180) * ($OuterRadius + $ValuePadding) + $Y;
if ($Angle >= 0 && $Angle <= 90) {
$Align = TEXT_ALIGN_BOTTOMLEFT;
}
if ($Angle > 90 && $Angle <= 180) {
$Align = TEXT_ALIGN_TOPLEFT;
}
if ($Angle > 180 && $Angle <= 270) {
$Align = TEXT_ALIGN_TOPRIGHT;
}
if ($Angle > 270) {
$Align = TEXT_ALIGN_BOTTOMRIGHT;
}
} else {
$Xc = cos(($Angle - 90) * PI / 180)
* (($OuterRadius - $InnerRadius) / 2 + $InnerRadius)
+ $X
;
$Yc = sin(($Angle - 90) * PI / 180)
* (($OuterRadius - $InnerRadius) / 2 + $InnerRadius)
+ $Y
;
$Align = TEXT_ALIGN_MIDDLEMIDDLE;
}
if ($WriteValues == PIE_VALUE_PERCENTAGE) {
$Display = round((100 / $SerieSum) * $Value, $Precision) . "%";
} elseif ($WriteValues == PIE_VALUE_NATURAL) {
$Display = $Value . $ValueSuffix;
} else {
$Display = "";
}
$this->pChartObject->drawText(
$Xc,
$Yc,
$Display,
["Align" => $Align, "R" => $ValueR, "G" => $ValueG, "B" => $ValueB]
);
$Offset = $EndAngle;
}
}
$this->pChartObject->Shadow = $RestoreShadow;
return PIE_RENDERED;
}
/**
* Draw a 3D ring chart
* @param int $X
* @param int $Y
* @param array $Format
* @return int
*/
public function draw3DRing($X, $Y, array $Format = [])
{
$OuterRadius = isset($Format["OuterRadius"]) ? $Format["OuterRadius"] : 100;
$Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0;
$InnerRadius = isset($Format["InnerRadius"]) ? $Format["InnerRadius"] : 30;
$SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : .6;
$SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 10;
$DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 10;
$DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 10;
$Border = isset($Format["Border"]) ? $Format["Border"] : false;
$Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : false;
$DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : false;
$LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : false;
$LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL;
$LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0;
$LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0;
$LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0;
$LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100;
$Cf = isset($Format["Cf"]) ? $Format["Cf"] : 20;
$WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : PIE_VALUE_NATURAL;
$ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : $SliceHeight + 15;
$ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE;
$ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : "";
$ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255;
$ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255;
$ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255;
$ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100;
$RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
/* Error correction for overlaying rounded corners */
if ($SkewFactor < .5) {
$SkewFactor = .5;
}
/* Data Processing */
$Data = $this->pDataObject->getData();
$Palette = $this->pDataObject->getPalette();
/* Do we have an abscissa serie defined? */
if ($Data["Abscissa"] == "") {
return PIE_NO_ABSCISSA;
}
/* Try to find the data serie */
$DataSerie = null;
foreach ($Data["Series"] as $SerieName => $SerieData) {
if ($SerieName != $Data["Abscissa"]) {
$DataSerie = $SerieName;
}
}
/* Do we have data to compute? */
if (!$DataSerie) {
return PIE_NO_DATASERIE;
}
/* Remove unused data */
list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]);
/* Compute the pie sum */
$SerieSum = $this->pDataObject->getSum($DataSerie);
/* Do we have data to draw? */
if ($SerieSum == 0) {
return PIE_SUMISNULL;
}
/* Dump the real number of data to draw */
$Values = [];
foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
if ($Value != 0) {
$Values[] = $Value;
}
}
/* Compute the wasted angular space between series */
if (count($Values) == 1) {
$WastedAngular = 0;
} else {
$WastedAngular = count($Values) * $DataGapAngle;
}
/* Compute the scale */
$ScaleFactor = (360 - $WastedAngular) / $SerieSum;
$RestoreShadow = $this->pChartObject->Shadow;
if ($this->pChartObject->Shadow) {
$this->pChartObject->Shadow = false;
}
/* Draw the polygon ring elements */
$Offset = 360;
$ID = count($Values) - 1;
$Values = array_reverse($Values);
$Slice = 0;
$Slices = [];
$SliceColors = [];
$Visible = [];
$SliceAngle = [];
foreach ($Values as $Key => $Value) {
if (!isset($Palette[$ID]["R"])) {
$Color = $this->pChartObject->getRandomColor();
$Palette[$ID] = $Color;
$this->pDataObject->savePalette($ID, $Color);
}
$Settings = [
"R" => $Palette[$ID]["R"],
"G" => $Palette[$ID]["G"],
"B" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
$SliceColors[$Slice] = $Settings;
$StartAngle = $Offset;
$EndAngle = $Offset - ($Value * $ScaleFactor);
if ($EndAngle < 0) {
$EndAngle = 0;
}
if ($StartAngle > 180) {
$Visible[$Slice]["Start"] = true;
} else {
$Visible[$Slice]["Start"] = true;
}
if ($EndAngle < 180) {
$Visible[$Slice]["End"] = false;
} else {
$Visible[$Slice]["End"] = true;
}
$Step = (360 / (2 * PI * $OuterRadius)) / 2;
$OutX1 = VOID;
$OutY1 = VOID;
for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) {
$Xc = cos(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 2) + $X;
$Yc = sin(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 2) * $SkewFactor + $Y;
$Slices[$Slice]["AA"][] = [$Xc, $Yc];
$Xc = cos(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 1) + $X;
$Yc = sin(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 1) * $SkewFactor + $Y;
$Slices[$Slice]["AA"][] = [$Xc, $Yc];
$Xc = cos(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) + $X;
$Yc = sin(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) * $SkewFactor + $Y;
$this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings);
if ($OutX1 == VOID) {
$OutX1 = $Xc;
$OutY1 = $Yc;
}
if ($i < 90) {
$Yc++;
}
if ($i > 90 && $i < 180) {
$Xc++;
}
if ($i > 180 && $i < 270) {
$Xc++;
}
if ($i >= 270) {
$Xc++;
$Yc++;
}
$Slices[$Slice]["BottomPoly"][] = floor($Xc);
$Slices[$Slice]["BottomPoly"][] = floor($Yc);
$Slices[$Slice]["TopPoly"][] = floor($Xc);
$Slices[$Slice]["TopPoly"][] = floor($Yc) - $SliceHeight;
$Slices[$Slice]["Angle"][] = $i;
}
$OutX2 = $Xc;
$OutY2 = $Yc;
$Slices[$Slice]["Angle"][] = VOID;
$Lasti = $i;
$Step = (360 / (2 * PI * $InnerRadius)) / 2;
$InX1 = VOID;
$InY1 = VOID;
for ($i = $EndAngle; $i <= $Offset; $i = $i + $Step) {
$Xc = cos(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius - 1) + $X;
$Yc = sin(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius - 1) * $SkewFactor + $Y;
$Slices[$Slice]["AA"][] = [$Xc, $Yc];
$Xc = cos(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius) + $X;
$Yc = sin(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius) * $SkewFactor + $Y;
$Slices[$Slice]["AA"][] = [$Xc, $Yc];
if ($InX1 == VOID) {
$InX1 = $Xc;
$InY1 = $Yc;
}
if ($i < 90) {
$Yc++;
}
if ($i > 90 && $i < 180) {
$Xc++;
}
if ($i > 180 && $i < 270) {
$Xc++;
}
if ($i >= 270) {
$Xc++;
$Yc++;
}
$Slices[$Slice]["BottomPoly"][] = floor($Xc);
$Slices[$Slice]["BottomPoly"][] = floor($Yc);
$Slices[$Slice]["TopPoly"][] = floor($Xc);
$Slices[$Slice]["TopPoly"][] = floor($Yc) - $SliceHeight;
$Slices[$Slice]["Angle"][] = $i;
}
$InX2 = $Xc;
$InY2 = $Yc;
$Slices[$Slice]["InX1"] = $InX1;
$Slices[$Slice]["InY1"] = $InY1;
$Slices[$Slice]["InX2"] = $InX2;
$Slices[$Slice]["InY2"] = $InY2;
$Slices[$Slice]["OutX1"] = $OutX1;
$Slices[$Slice]["OutY1"] = $OutY1;
$Slices[$Slice]["OutX2"] = $OutX2;
$Slices[$Slice]["OutY2"] = $OutY2;
$Offset = $Lasti - $DataGapAngle;
$ID--;
$Slice++;
}
/* Draw the bottom pie splice */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["NoBorder"] = true;
$this->pChartObject->drawPolygon($Plots["BottomPoly"], $Settings);
foreach ($Plots["AA"] as $Key => $Pos) {
$this->pChartObject->drawAntialiasPixel($Pos[0], $Pos[1], $Settings);
}
$this->pChartObject->drawLine(
$Plots["InX1"],
$Plots["InY1"],
$Plots["OutX2"],
$Plots["OutY2"],
$Settings
);
$this->pChartObject->drawLine(
$Plots["InX2"],
$Plots["InY2"],
$Plots["OutX1"],
$Plots["OutY1"],
$Settings
);
}
$Slices = array_reverse($Slices);
$SliceColors = array_reverse($SliceColors);
/* Draw the vertical edges (semi-visible) */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["NoBorder"] = true;
$Settings["R"] = $Settings["R"] + $Cf;
$Settings["G"] = $Settings["G"] + $Cf;
$Settings["B"] = $Settings["B"] + $Cf;
$StartAngle = $Plots["Angle"][0];
foreach ($Plots["Angle"] as $Key => $Angle) {
if ($Angle == VOID) {
$EndAngle = $Plots["Angle"][$Key - 1];
}
}
if ($StartAngle >= 270 || $StartAngle <= 90) {
$this->pChartObject->drawLine(
$Plots["OutX1"],
$Plots["OutY1"],
$Plots["OutX1"],
$Plots["OutY1"] - $SliceHeight,
$Settings
);
}
if ($StartAngle >= 270 || $StartAngle <= 90) {
$this->pChartObject->drawLine(
$Plots["OutX2"],
$Plots["OutY2"],
$Plots["OutX2"],
$Plots["OutY2"] - $SliceHeight,
$Settings
);
}
$this->pChartObject->drawLine(
$Plots["InX1"],
$Plots["InY1"],
$Plots["InX1"],
$Plots["InY1"] - $SliceHeight,
$Settings
);
$this->pChartObject->drawLine(
$Plots["InX2"],
$Plots["InY2"],
$Plots["InX2"],
$Plots["InY2"] - $SliceHeight,
$Settings
);
}
/* Draw the inner vertical slices */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["NoBorder"] = true;
$Settings["R"] = $Settings["R"] + $Cf;
$Settings["G"] = $Settings["G"] + $Cf;
$Settings["B"] = $Settings["B"] + $Cf;
$Outer = true;
$Inner = false;
$InnerPlotsA = [];
$InnerPlotsB = [];
foreach ($Plots["Angle"] as $ID => $Angle) {
if ($Angle == VOID) {
$Outer = false;
$Inner = true;
} elseif ($Inner && ($Angle < 90 || $Angle > 270) && isset($Plots["BottomPoly"][$ID * 2])) {
$Xo = $Plots["BottomPoly"][$ID * 2];
$Yo = $Plots["BottomPoly"][$ID * 2 + 1];
$InnerPlotsA[] = $Xo;
$InnerPlotsA[] = $Yo;
$InnerPlotsB[] = $Xo;
$InnerPlotsB[] = $Yo - $SliceHeight;
}
}
if (count($InnerPlotsA)) {
$InnerPlots = array_merge($InnerPlotsA, $this->arrayReverse($InnerPlotsB));
$this->pChartObject->drawPolygon($InnerPlots, $Settings);
}
}
/* Draw the splice top and left poly */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["NoBorder"] = true;
$Settings["R"] = $Settings["R"] + $Cf * 1.5;
$Settings["G"] = $Settings["G"] + $Cf * 1.5;
$Settings["B"] = $Settings["B"] + $Cf * 1.5;
$StartAngle = $Plots["Angle"][0];
foreach ($Plots["Angle"] as $Key => $Angle) {
if ($Angle == VOID) {
$EndAngle = $Plots["Angle"][$Key - 1];
}
}
if ($StartAngle < 180) {
$Points = [];
$Points[] = $Plots["InX2"];
$Points[] = $Plots["InY2"];
$Points[] = $Plots["InX2"];
$Points[] = $Plots["InY2"] - $SliceHeight;
$Points[] = $Plots["OutX1"];
$Points[] = $Plots["OutY1"] - $SliceHeight;
$Points[] = $Plots["OutX1"];
$Points[] = $Plots["OutY1"];
$this->pChartObject->drawPolygon($Points, $Settings);
}
if ($EndAngle > 180) {
$Points = [];
$Points[] = $Plots["InX1"];
$Points[] = $Plots["InY1"];
$Points[] = $Plots["InX1"];
$Points[] = $Plots["InY1"] - $SliceHeight;
$Points[] = $Plots["OutX2"];
$Points[] = $Plots["OutY2"] - $SliceHeight;
$Points[] = $Plots["OutX2"];
$Points[] = $Plots["OutY2"];
$this->pChartObject->drawPolygon($Points, $Settings);
}
}
/* Draw the vertical edges (visible) */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["NoBorder"] = true;
$Settings["R"] = $Settings["R"] + $Cf;
$Settings["G"] = $Settings["G"] + $Cf;
$Settings["B"] = $Settings["B"] + $Cf;
$StartAngle = $Plots["Angle"][0];
foreach ($Plots["Angle"] as $Key => $Angle) {
if ($Angle == VOID) {
$EndAngle = $Plots["Angle"][$Key - 1];
}
}
if ($StartAngle <= 270 && $StartAngle >= 90) {
$this->pChartObject->drawLine(
$Plots["OutX1"],
$Plots["OutY1"],
$Plots["OutX1"],
$Plots["OutY1"] - $SliceHeight,
$Settings
);
}
if ($EndAngle <= 270 && $EndAngle >= 90) {
$this->pChartObject->drawLine(
$Plots["OutX2"],
$Plots["OutY2"],
$Plots["OutX2"],
$Plots["OutY2"] - $SliceHeight,
$Settings
);
}
}
/* Draw the outer vertical slices */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["NoBorder"] = true;
$Settings["R"] = $Settings["R"] + $Cf;
$Settings["G"] = $Settings["G"] + $Cf;
$Settings["B"] = $Settings["B"] + $Cf;
$Outer = true;
$Inner = false;
$OuterPlotsA = [];
$OuterPlotsB = [];
$InnerPlotsA = [];
$InnerPlotsB = [];
foreach ($Plots["Angle"] as $ID => $Angle) {
if ($Angle == VOID) {
$Outer = false;
$Inner = true;
} elseif ($Outer && ($Angle > 90 && $Angle < 270) && isset($Plots["BottomPoly"][$ID * 2])) {
$Xo = $Plots["BottomPoly"][$ID * 2];
$Yo = $Plots["BottomPoly"][$ID * 2 + 1];
$OuterPlotsA[] = $Xo;
$OuterPlotsA[] = $Yo;
$OuterPlotsB[] = $Xo;
$OuterPlotsB[] = $Yo - $SliceHeight;
}
}
if (count($OuterPlotsA)) {
$OuterPlots = array_merge($OuterPlotsA, $this->arrayReverse($OuterPlotsB));
$this->pChartObject->drawPolygon($OuterPlots, $Settings);
}
}
$Slices = array_reverse($Slices);
$SliceColors = array_reverse($SliceColors);
/* Draw the top pie splice */
foreach ($Slices as $SliceID => $Plots) {
$Settings = $SliceColors[$SliceID];
$Settings["NoBorder"] = true;
$Settings["R"] = $Settings["R"] + $Cf * 2;
$Settings["G"] = $Settings["G"] + $Cf * 2;
$Settings["B"] = $Settings["B"] + $Cf * 2;
$this->pChartObject->drawPolygon($Plots["TopPoly"], $Settings);
if ($RecordImageMap) {
$this->pChartObject->addToImageMap(
"POLY",
$this->arraySerialize($Plots["TopPoly"]),
$this->pChartObject->toHTMLColor(
$Settings["R"],
$Settings["G"],
$Settings["B"]
),
$Data["Series"][$Data["Abscissa"]]["Data"][$SliceID],
$Data["Series"][$DataSerie]["Data"][count($Slices) - $SliceID - 1]
);
}
foreach ($Plots["AA"] as $Key => $Pos) {
$this->pChartObject->drawAntialiasPixel(
$Pos[0],
$Pos[1] - $SliceHeight,
$Settings
);
}
$this->pChartObject->drawLine(
$Plots["InX1"],
$Plots["InY1"] - $SliceHeight,
$Plots["OutX2"],
$Plots["OutY2"] - $SliceHeight,
$Settings
);
$this->pChartObject->drawLine(
$Plots["InX2"],
$Plots["InY2"] - $SliceHeight,
$Plots["OutX1"],
$Plots["OutY1"] - $SliceHeight,
$Settings
);
}
if ($DrawLabels) {
$Offset = 360;
foreach ($Values as $Key => $Value) {
$StartAngle = $Offset;
$EndAngle = $Offset - ($Value * $ScaleFactor);
if ($EndAngle < 0) {
$EndAngle = 0;
}
if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
$Settings = [
"FillR" => $Palette[$ID]["R"],
"FillG" => $Palette[$ID]["G"],
"FillB" => $Palette[$ID]["B"],
"Alpha" => $Palette[$ID]["Alpha"]
];
} else {
$Settings = [
"FillR" => $LabelR,
"FillG" => $LabelG,
"FillB" => $LabelB,
"Alpha" => $LabelAlpha
];
}
$Angle = ($EndAngle - $Offset) / 2 + $Offset;
$Xc = cos(($Angle - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) + $X;
$Yc = sin(($Angle - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) * $SkewFactor + $Y;
$Label = "";
if ($WriteValues == PIE_VALUE_PERCENTAGE) {
$Label = round((100 / $SerieSum) * $Value, $Precision) . "%";
} elseif ($WriteValues == PIE_VALUE_NATURAL) {
$Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key];
}
if ($LabelStacked) {
$this->writePieLabel(
$Xc,
$Yc - $SliceHeight,
$Label,
$Angle,
$Settings,
true,
$X,
$Y,
$OuterRadius
);
} else {
$this->writePieLabel($Xc, $Yc - $SliceHeight, $Label, $Angle, $Settings, false);
}
$Offset = $EndAngle - $DataGapAngle;
$ID--;
$Slice++;
}
}
if ($DrawLabels && $LabelStacked) {
$this->writeShiftedLabels();
}
$this->pChartObject->Shadow = $RestoreShadow;
return PIE_RENDERED;
}
/**
* Serialize an array
* @param array $Data
* @return string
*/
public function arraySerialize(array $Data)
{
$Result = "";
foreach ($Data as $Value) {
if ($Result == "") {
$Result = floor($Value);
} else {
$Result = $Result . "," . floor($Value);
}
}
return $Result;
}
/**
* Reverse an array
* @param array $Plots
* @return array
*/
public function arrayReverse(array $Plots)
{
$Result = [];
for ($i = count($Plots) - 1; $i >= 0; $i = $i - 2) {
$Result[] = $Plots[$i - 1];
$Result[] = $Plots[$i];
}
return $Result;
}
/**
* Remove unused series & values
* @param array $Data
* @param array $Palette
* @param string $DataSerie
* @param string $AbscissaSerie
* @return array
*/
public function clean0Values(array $Data, array $Palette, $DataSerie, $AbscissaSerie)
{
$NewPalette = [];
$NewData = [];
$NewAbscissa = [];
/* Remove unused series */
foreach (array_keys($Data["Series"]) as $SerieName) {
if ($SerieName != $DataSerie && $SerieName != $AbscissaSerie) {
unset($Data["Series"][$SerieName]);
}
}
/* Remove null values */
foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
if ($Value != 0) {
$NewData[] = $Value;
$NewAbscissa[] = $Data["Series"][$AbscissaSerie]["Data"][$Key];
if (isset($Palette[$Key])) {
$NewPalette[] = $Palette[$Key];
}
}
}
$Data["Series"][$DataSerie]["Data"] = $NewData;
$Data["Series"][$AbscissaSerie]["Data"] = $NewAbscissa;
return [$Data, $NewPalette];
}
}