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->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]; } }