PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.12.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.12.1
5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / szymach / c-pchart / src / Chart / Pie.php
matomo / app / vendor / szymach / c-pchart / src / Chart Last commit date
Bubble.php 1 year ago Indicator.php 1 year ago Pie.php 1 year ago Radar.php 1 year ago Scatter.php 1 year ago Split.php 1 year ago Spring.php 1 year ago Stock.php 1 year ago Surface.php 1 year ago
Pie.php
1675 lines
1 <?php
2
3 namespace CpChart\Chart;
4
5 use CpChart\Data;
6 use CpChart\Image;
7 /**
8 * Pie - class to draw pie charts
9 *
10 * Version : 2.1.4
11 * Made by : Jean-Damien POGOLOTTI
12 * Last Update : 19/01/2014
13 *
14 * This file can be distributed under the license you can find at :
15 *
16 * http://www.pchart.net/license
17 *
18 * You can find the whole class documentation on the pChart web site.
19 */
20 class Pie
21 {
22 /**
23 * @var Image
24 */
25 public $pChartObject;
26 /**
27 * @var Data
28 */
29 public $pDataObject;
30 /**
31 * @var array
32 */
33 public $LabelPos = [];
34 /**
35 * @param Image $pChartObject
36 * @param Data $pDataObject
37 */
38 public function __construct(Image $pChartObject, Data $pDataObject)
39 {
40 $this->pChartObject = $pChartObject;
41 $this->pDataObject = $pDataObject;
42 }
43 /**
44 * Draw a pie chart
45 * @param int $X
46 * @param int $Y
47 * @param array $Format
48 * @return int
49 */
50 public function draw2DPie($X, $Y, array $Format = [])
51 {
52 $Radius = isset($Format["Radius"]) ? $Format["Radius"] : 60;
53 $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0;
54 $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0;
55 $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0;
56 $SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : \true;
57 $Border = isset($Format["Border"]) ? $Format["Border"] : \false;
58 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255;
59 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255;
60 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255;
61 $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : \false;
62 $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : \false;
63 $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : \false;
64 $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL;
65 $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0;
66 $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0;
67 $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0;
68 $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100;
69 $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : null;
70 $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE;
71 $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15;
72 $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : "";
73 $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255;
74 $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255;
75 $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255;
76 $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100;
77 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
78 $Data = $this->pDataObject->getData();
79 $Palette = $this->pDataObject->getPalette();
80 /* Do we have an abscissa serie defined? */
81 if ($Data["Abscissa"] == "") {
82 return PIE_NO_ABSCISSA;
83 }
84 /* Try to find the data serie */
85 $DataSerie = null;
86 foreach (array_keys($Data["Series"]) as $SerieName) {
87 if ($SerieName != $Data["Abscissa"]) {
88 $DataSerie = $SerieName;
89 }
90 }
91 /* Do we have data to compute? */
92 if (!$DataSerie) {
93 return PIE_NO_DATASERIE;
94 }
95 /* Remove unused data */
96 list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]);
97 /* Compute the pie sum */
98 $SerieSum = $this->pDataObject->getSum($DataSerie);
99 /* Do we have data to draw? */
100 if ($SerieSum == 0) {
101 return PIE_SUMISNULL;
102 }
103 /* Dump the real number of data to draw */
104 $Values = [];
105 foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
106 if ($Value != 0) {
107 $Values[] = $Value;
108 }
109 }
110 /* Compute the wasted angular space between series */
111 if (count($Values) == 1) {
112 $WastedAngular = 0;
113 } else {
114 $WastedAngular = count($Values) * $DataGapAngle;
115 }
116 /* Compute the scale */
117 $ScaleFactor = (360 - $WastedAngular) / $SerieSum;
118 $RestoreShadow = $this->pChartObject->Shadow;
119 if ($this->pChartObject->Shadow) {
120 $this->pChartObject->Shadow = \false;
121 $ShadowFormat = $Format;
122 $ShadowFormat["Shadow"] = \true;
123 $this->draw2DPie($X + $this->pChartObject->ShadowX, $Y + $this->pChartObject->ShadowY, $ShadowFormat);
124 }
125 /* Draw the polygon pie elements */
126 $Step = 360 / (2 * PI * $Radius);
127 $Offset = 0;
128 $ID = 0;
129 foreach ($Values as $Key => $Value) {
130 if ($Shadow) {
131 $Settings = ["R" => $this->pChartObject->ShadowR, "G" => $this->pChartObject->ShadowG, "B" => $this->pChartObject->ShadowB, "Alpha" => $this->pChartObject->Shadowa];
132 } else {
133 if (!isset($Palette[$ID]["R"])) {
134 $Color = $this->pChartObject->getRandomColor();
135 $Palette[$ID] = $Color;
136 $this->pDataObject->savePalette($ID, $Color);
137 }
138 $Settings = ["R" => $Palette[$ID]["R"], "G" => $Palette[$ID]["G"], "B" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
139 }
140 if (!$SecondPass && !$Shadow) {
141 if (!$Border) {
142 $Settings["Surrounding"] = 10;
143 } else {
144 $Settings["BorderR"] = $BorderR;
145 $Settings["BorderG"] = $BorderG;
146 $Settings["BorderB"] = $BorderB;
147 }
148 }
149 $EndAngle = $Offset + $Value * $ScaleFactor;
150 if ($EndAngle > 360) {
151 $EndAngle = 360;
152 }
153 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
154 if ($DataGapAngle == 0) {
155 $X0 = $X;
156 $Y0 = $Y;
157 } else {
158 $X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X;
159 $Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius + $Y;
160 }
161 $Plots = [$X0, $Y0];
162 for ($i = $Offset; $i <= $EndAngle; $i = $i + $Step) {
163 $Xc = cos(($i - 90) * PI / 180) * $Radius + $X;
164 $Yc = sin(($i - 90) * PI / 180) * $Radius + $Y;
165 if ($SecondPass && $i < 90) {
166 $Yc++;
167 }
168 if ($SecondPass && ($i > 180 && $i < 270)) {
169 $Xc++;
170 }
171 if ($SecondPass && $i >= 270) {
172 $Xc++;
173 $Yc++;
174 }
175 $Plots[] = $Xc;
176 $Plots[] = $Yc;
177 }
178 $this->pChartObject->drawPolygon($Plots, $Settings);
179 if ($RecordImageMap && !$Shadow) {
180 $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);
181 }
182 if ($DrawLabels && !$Shadow && !$SecondPass) {
183 if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
184 $Settings = ["FillR" => $Palette[$ID]["R"], "FillG" => $Palette[$ID]["G"], "FillB" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
185 } else {
186 $Settings = ["FillR" => $LabelR, "FillG" => $LabelG, "FillB" => $LabelB, "Alpha" => $LabelAlpha];
187 }
188 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
189 $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
190 $Yc = sin(($Angle - 90) * PI / 180) * $Radius + $Y;
191 $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key];
192 if ($LabelStacked) {
193 $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, \true, $X, $Y, $Radius);
194 } else {
195 $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, \false);
196 }
197 }
198 $Offset = $i + $DataGapAngle;
199 $ID++;
200 }
201 /* Second pass to smooth the angles */
202 if ($SecondPass) {
203 $Step = 360 / (2 * PI * $Radius);
204 $Offset = 0;
205 $ID = 0;
206 foreach ($Values as $Key => $Value) {
207 $FirstPoint = \true;
208 if ($Shadow) {
209 $Settings = ["R" => $this->pChartObject->ShadowR, "G" => $this->pChartObject->ShadowG, "B" => $this->pChartObject->ShadowB, "Alpha" => $this->pChartObject->Shadowa];
210 } else {
211 if ($Border) {
212 $Settings = ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB];
213 } else {
214 $Settings = ["R" => $Palette[$ID]["R"], "G" => $Palette[$ID]["G"], "B" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
215 }
216 }
217 $EndAngle = $Offset + $Value * $ScaleFactor;
218 if ($EndAngle > 360) {
219 $EndAngle = 360;
220 }
221 if ($DataGapAngle == 0) {
222 $X0 = $X;
223 $Y0 = $Y;
224 } else {
225 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
226 $X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X;
227 $Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius + $Y;
228 }
229 $Plots[] = $X0;
230 $Plots[] = $Y0;
231 for ($i = $Offset; $i <= $EndAngle; $i = $i + $Step) {
232 $Xc = cos(($i - 90) * PI / 180) * $Radius + $X;
233 $Yc = sin(($i - 90) * PI / 180) * $Radius + $Y;
234 if ($FirstPoint) {
235 $this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings);
236 $FirstPoint = \false;
237 }
238 $this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings);
239 }
240 $this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings);
241 if ($DrawLabels && !$Shadow) {
242 if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
243 $Settings = ["FillR" => $Palette[$ID]["R"], "FillG" => $Palette[$ID]["G"], "FillB" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
244 } else {
245 $Settings = ["FillR" => $LabelR, "FillG" => $LabelG, "FillB" => $LabelB, "Alpha" => $LabelAlpha];
246 }
247 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
248 $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
249 $Yc = sin(($Angle - 90) * PI / 180) * $Radius + $Y;
250 $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key];
251 if ($LabelStacked) {
252 $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, \true, $X, $Y, $Radius);
253 } else {
254 $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, \false);
255 }
256 }
257 $Offset = $i + $DataGapAngle;
258 $ID++;
259 }
260 }
261 if ($WriteValues != null && !$Shadow) {
262 $Step = 360 / (2 * PI * $Radius);
263 $Offset = 0;
264 $ID = count($Values) - 1;
265 $Settings = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE, "R" => $ValueR, "G" => $ValueG, "B" => $ValueB, "Alpha" => $ValueAlpha];
266 foreach ($Values as $Key => $Value) {
267 $EndAngle = $Value * $ScaleFactor + $Offset;
268 if ((int) $EndAngle > 360) {
269 $EndAngle = 0;
270 }
271 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
272 if ($ValuePosition == PIE_VALUE_OUTSIDE) {
273 $Xc = cos(($Angle - 90) * PI / 180) * ($Radius + $ValuePadding) + $X;
274 $Yc = sin(($Angle - 90) * PI / 180) * ($Radius + $ValuePadding) + $Y;
275 } else {
276 $Xc = cos(($Angle - 90) * PI / 180) * $Radius / 2 + $X;
277 $Yc = sin(($Angle - 90) * PI / 180) * $Radius / 2 + $Y;
278 }
279 if ($WriteValues == PIE_VALUE_PERCENTAGE) {
280 $Display = round(100 / $SerieSum * $Value, $Precision) . "%";
281 } elseif ($WriteValues == PIE_VALUE_NATURAL) {
282 $Display = $Value . $ValueSuffix;
283 }
284 $this->pChartObject->drawText($Xc, $Yc, $Display, $Settings);
285 $Offset = $EndAngle + $DataGapAngle;
286 $ID--;
287 }
288 }
289 if ($DrawLabels && $LabelStacked) {
290 $this->writeShiftedLabels();
291 }
292 $this->pChartObject->Shadow = $RestoreShadow;
293 return PIE_RENDERED;
294 }
295 /**
296 * Draw a 3D pie chart
297 * @param int $X
298 * @param int $Y
299 * @param array $Format
300 * @return int
301 */
302 public function draw3DPie($X, $Y, array $Format = [])
303 {
304 /* Rendering layout */
305 $Radius = isset($Format["Radius"]) ? $Format["Radius"] : 80;
306 $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0;
307 $SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : 0.5;
308 $SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 20;
309 $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 0;
310 $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 0;
311 $SecondPass = isset($Format["SecondPass"]) ? $Format["SecondPass"] : \true;
312 $Border = isset($Format["Border"]) ? $Format["Border"] : \false;
313 $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : \false;
314 $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : \false;
315 $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : \false;
316 $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL;
317 $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0;
318 $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0;
319 $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0;
320 $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100;
321 $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : null;
322 //PIE_VALUE_PERCENTAGE
323 $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_INSIDE;
324 $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 15;
325 $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : "";
326 $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255;
327 $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255;
328 $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255;
329 $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100;
330 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
331 /* Error correction for overlaying rounded corners */
332 if ($SkewFactor < 0.5) {
333 $SkewFactor = 0.5;
334 }
335 /* Data Processing */
336 $Data = $this->pDataObject->getData();
337 $Palette = $this->pDataObject->getPalette();
338 /* Do we have an abscissa serie defined? */
339 if ($Data["Abscissa"] == "") {
340 return PIE_NO_ABSCISSA;
341 }
342 /* Try to find the data serie */
343 $DataSerie = null;
344 foreach ($Data["Series"] as $SerieName => $SerieData) {
345 if ($SerieName != $Data["Abscissa"]) {
346 $DataSerie = $SerieName;
347 }
348 }
349 /* Do we have data to compute? */
350 if (!$DataSerie) {
351 return PIE_NO_DATASERIE;
352 }
353 /* Remove unused data */
354 list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]);
355 /* Compute the pie sum */
356 $SerieSum = $this->pDataObject->getSum($DataSerie);
357 /* Do we have data to draw? */
358 if ($SerieSum == 0) {
359 return PIE_SUMISNULL;
360 }
361 /* Dump the real number of data to draw */
362 $Values = [];
363 foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
364 if ($Value != 0) {
365 $Values[] = $Value;
366 }
367 }
368 /* Compute the wasted angular space between series */
369 if (count($Values) == 1) {
370 $WastedAngular = 0;
371 } else {
372 $WastedAngular = count($Values) * $DataGapAngle;
373 }
374 /* Compute the scale */
375 $ScaleFactor = (360 - $WastedAngular) / $SerieSum;
376 $RestoreShadow = $this->pChartObject->Shadow;
377 if ($this->pChartObject->Shadow) {
378 $this->pChartObject->Shadow = \false;
379 }
380 /* Draw the polygon pie elements */
381 $Step = 360 / (2 * PI * $Radius);
382 $Offset = 360;
383 $ID = count($Values) - 1;
384 $Values = array_reverse($Values);
385 $Slice = 0;
386 $Slices = [];
387 $SliceColors = [];
388 $Visible = [];
389 $SliceAngle = [];
390 foreach ($Values as $Key => $Value) {
391 if (!isset($Palette[$ID]["R"])) {
392 $Color = $this->pChartObject->getRandomColor();
393 $Palette[$ID] = $Color;
394 $this->pDataObject->savePalette($ID, $Color);
395 }
396 $Settings = ["R" => $Palette[$ID]["R"], "G" => $Palette[$ID]["G"], "B" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
397 $SliceColors[$Slice] = $Settings;
398 $StartAngle = $Offset;
399 $EndAngle = $Offset - $Value * $ScaleFactor;
400 if ($EndAngle < 0) {
401 $EndAngle = 0;
402 }
403 if ($StartAngle > 180) {
404 $Visible[$Slice]["Start"] = \true;
405 } else {
406 $Visible[$Slice]["Start"] = \true;
407 }
408 if ($EndAngle < 180) {
409 $Visible[$Slice]["End"] = \false;
410 } else {
411 $Visible[$Slice]["End"] = \true;
412 }
413 if ($DataGapAngle == 0) {
414 $X0 = $X;
415 $Y0 = $Y;
416 } else {
417 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
418 $X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X;
419 $Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius * $SkewFactor + $Y;
420 }
421 $Slices[$Slice][] = $X0;
422 $Slices[$Slice][] = $Y0;
423 $SliceAngle[$Slice][] = 0;
424 for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) {
425 $Xc = cos(($i - 90) * PI / 180) * $Radius + $X;
426 $Yc = sin(($i - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
427 if (($SecondPass || $RestoreShadow) && $i < 90) {
428 $Yc++;
429 }
430 if (($SecondPass || $RestoreShadow) && ($i > 90 && $i < 180)) {
431 $Xc++;
432 }
433 if (($SecondPass || $RestoreShadow) && ($i > 180 && $i < 270)) {
434 $Xc++;
435 }
436 if (($SecondPass || $RestoreShadow) && $i >= 270) {
437 $Xc++;
438 $Yc++;
439 }
440 $Slices[$Slice][] = $Xc;
441 $Slices[$Slice][] = $Yc;
442 $SliceAngle[$Slice][] = $i;
443 }
444 $Offset = $i - $DataGapAngle;
445 $ID--;
446 $Slice++;
447 }
448 /* Draw the bottom shadow if needed */
449 if ($RestoreShadow && ($this->pChartObject->ShadowX != 0 || $this->pChartObject->ShadowY != 0)) {
450 foreach ($Slices as $SliceID => $Plots) {
451 $ShadowPie = [];
452 for ($i = 0; $i < count($Plots); $i = $i + 2) {
453 $ShadowPie[] = $Plots[$i] + $this->pChartObject->ShadowX;
454 $ShadowPie[] = $Plots[$i + 1] + $this->pChartObject->ShadowY;
455 }
456 $Settings = ["R" => $this->pChartObject->ShadowR, "G" => $this->pChartObject->ShadowG, "B" => $this->pChartObject->ShadowB, "Alpha" => $this->pChartObject->Shadowa, "NoBorder" => \true];
457 $this->pChartObject->drawPolygon($ShadowPie, $Settings);
458 }
459 $Step = 360 / (2 * PI * $Radius);
460 $Offset = 360;
461 foreach ($Values as $Key => $Value) {
462 $EndAngle = $Offset - $Value * $ScaleFactor;
463 if ($EndAngle < 0) {
464 $EndAngle = 0;
465 }
466 for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) {
467 $Xc = cos(($i - 90) * PI / 180) * $Radius + $X + $this->pChartObject->ShadowX;
468 $Yc = sin(($i - 90) * PI / 180) * $Radius * $SkewFactor + $Y + $this->pChartObject->ShadowY;
469 $this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings);
470 }
471 $Offset = $i - $DataGapAngle;
472 $ID--;
473 }
474 }
475 /* Draw the bottom pie splice */
476 foreach ($Slices as $SliceID => $Plots) {
477 $Settings = $SliceColors[$SliceID];
478 $Settings["NoBorder"] = \true;
479 $this->pChartObject->drawPolygon($Plots, $Settings);
480 if ($SecondPass) {
481 $Settings = $SliceColors[$SliceID];
482 if ($Border) {
483 $Settings["R"] += 30;
484 $Settings["G"] += 30;
485 $Settings["B"] += 30;
486 }
487 /* Empty error handling */
488 if (isset($SliceAngle[$SliceID][1])) {
489 $Angle = $SliceAngle[$SliceID][1];
490 $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
491 $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
492 $this->pChartObject->drawLine($Plots[0], $Plots[1], $Xc, $Yc, $Settings);
493 $Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1];
494 $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
495 $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
496 $this->pChartObject->drawLine($Plots[0], $Plots[1], $Xc, $Yc, $Settings);
497 }
498 }
499 }
500 /* Draw the two vertical edges */
501 $Slices = array_reverse($Slices);
502 $SliceColors = array_reverse($SliceColors);
503 foreach ($Slices as $SliceID => $Plots) {
504 $Settings = $SliceColors[$SliceID];
505 $Settings["R"] += 10;
506 $Settings["G"] += 10;
507 $Settings["B"] += 10;
508 $Settings["NoBorder"] = \true;
509 /* Empty error handling */
510 if ($Visible[$SliceID]["Start"] && isset($Plots[2])) {
511 $this->pChartObject->drawLine($Plots[2], $Plots[3], $Plots[2], $Plots[3] - $SliceHeight, ["R" => $Settings["R"], "G" => $Settings["G"], "B" => $Settings["B"]]);
512 $Border = [];
513 $Border[] = $Plots[0];
514 $Border[] = $Plots[1];
515 $Border[] = $Plots[0];
516 $Border[] = $Plots[1] - $SliceHeight;
517 $Border[] = $Plots[2];
518 $Border[] = $Plots[3] - $SliceHeight;
519 $Border[] = $Plots[2];
520 $Border[] = $Plots[3];
521 $this->pChartObject->drawPolygon($Border, $Settings);
522 }
523 }
524 $Slices = array_reverse($Slices);
525 $SliceColors = array_reverse($SliceColors);
526 foreach ($Slices as $SliceID => $Plots) {
527 $Settings = $SliceColors[$SliceID];
528 $Settings["R"] += 10;
529 $Settings["G"] += 10;
530 $Settings["B"] += 10;
531 $Settings["NoBorder"] = \true;
532 if ($Visible[$SliceID]["End"]) {
533 $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"]]);
534 $Border = [];
535 $Border[] = $Plots[0];
536 $Border[] = $Plots[1];
537 $Border[] = $Plots[0];
538 $Border[] = $Plots[1] - $SliceHeight;
539 $Border[] = $Plots[count($Plots) - 2];
540 $Border[] = $Plots[count($Plots) - 1] - $SliceHeight;
541 $Border[] = $Plots[count($Plots) - 2];
542 $Border[] = $Plots[count($Plots) - 1];
543 $this->pChartObject->drawPolygon($Border, $Settings);
544 }
545 }
546 /* Draw the rounded edges */
547 foreach ($Slices as $SliceID => $Plots) {
548 $Settings = $SliceColors[$SliceID];
549 $Settings["R"] += 10;
550 $Settings["G"] += 10;
551 $Settings["B"] += 10;
552 $Settings["NoBorder"] = \true;
553 for ($j = 2; $j < count($Plots) - 2; $j = $j + 2) {
554 $Angle = $SliceAngle[$SliceID][$j / 2];
555 if ($Angle < 270 && $Angle > 90) {
556 $Border = [];
557 $Border[] = $Plots[$j];
558 $Border[] = $Plots[$j + 1];
559 $Border[] = $Plots[$j + 2];
560 $Border[] = $Plots[$j + 3];
561 $Border[] = $Plots[$j + 2];
562 $Border[] = $Plots[$j + 3] - $SliceHeight;
563 $Border[] = $Plots[$j];
564 $Border[] = $Plots[$j + 1] - $SliceHeight;
565 $this->pChartObject->drawPolygon($Border, $Settings);
566 }
567 }
568 if ($SecondPass) {
569 $Settings = $SliceColors[$SliceID];
570 if (count($Border)) {
571 $Settings["R"] += 30;
572 $Settings["G"] += 30;
573 $Settings["B"] += 30;
574 }
575 /* Empty error handling */
576 if (isset($SliceAngle[$SliceID][1])) {
577 $Angle = $SliceAngle[$SliceID][1];
578 if ($Angle < 270 && $Angle > 90) {
579 $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
580 $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
581 $this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings);
582 }
583 }
584 $Angle = $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1];
585 if ($Angle < 270 && $Angle > 90) {
586 $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
587 $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
588 $this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings);
589 }
590 if (isset($SliceAngle[$SliceID][1]) && $SliceAngle[$SliceID][1] > 270 && $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1] < 270) {
591 $Xc = cos((270 - 90) * PI / 180) * $Radius + $X;
592 $Yc = sin((270 - 90) * PI / 180) * $Radius * $SkewFactor + $Y;
593 $this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings);
594 }
595 if (isset($SliceAngle[$SliceID][1]) && $SliceAngle[$SliceID][1] > 90 && $SliceAngle[$SliceID][count($SliceAngle[$SliceID]) - 1] < 90) {
596 $Xc = cos(0 * PI / 180) * $Radius + $X;
597 $Yc = sin(0 * PI / 180) * $Radius * $SkewFactor + $Y;
598 $this->pChartObject->drawLine($Xc, $Yc, $Xc, $Yc - $SliceHeight, $Settings);
599 }
600 }
601 }
602 /* Draw the top splice */
603 foreach ($Slices as $SliceID => $Plots) {
604 $Settings = $SliceColors[$SliceID];
605 $Settings["R"] += 20;
606 $Settings["G"] += 20;
607 $Settings["B"] += 20;
608 $Top = [];
609 for ($j = 0; $j < count($Plots); $j = $j + 2) {
610 $Top[] = $Plots[$j];
611 $Top[] = $Plots[$j + 1] - $SliceHeight;
612 }
613 $this->pChartObject->drawPolygon($Top, $Settings);
614 if ($RecordImageMap && !$Shadow) {
615 $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]);
616 }
617 }
618 /* Second pass to smooth the angles */
619 if ($SecondPass) {
620 $Step = 360 / (2 * PI * $Radius);
621 $Offset = 360;
622 $ID = count($Values) - 1;
623 foreach ($Values as $Key => $Value) {
624 $FirstPoint = \true;
625 if ($Shadow) {
626 $Settings = ["R" => $this->pChartObject->ShadowR, "G" => $this->pChartObject->ShadowG, "B" => $this->pChartObject->ShadowB, "Alpha" => $this->pChartObject->Shadowa];
627 } else {
628 if ($Border) {
629 $Settings = ["R" => $Palette[$ID]["R"] + 30, "G" => $Palette[$ID]["G"] + 30, "B" => $Palette[$ID]["B"] + 30, "Alpha" => $Palette[$ID]["Alpha"]];
630 } else {
631 $Settings = ["R" => $Palette[$ID]["R"], "G" => $Palette[$ID]["G"], "B" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
632 }
633 }
634 $EndAngle = $Offset - $Value * $ScaleFactor;
635 if ($EndAngle < 0) {
636 $EndAngle = 0;
637 }
638 if ($DataGapAngle == 0) {
639 $X0 = $X;
640 $Y0 = $Y - $SliceHeight;
641 } else {
642 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
643 $X0 = cos(($Angle - 90) * PI / 180) * $DataGapRadius + $X;
644 $Y0 = sin(($Angle - 90) * PI / 180) * $DataGapRadius * $SkewFactor + $Y - $SliceHeight;
645 }
646 $Plots[] = $X0;
647 $Plots[] = $Y0;
648 for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) {
649 $Xc = cos(($i - 90) * PI / 180) * $Radius + $X;
650 $Yc = sin(($i - 90) * PI / 180) * $Radius * $SkewFactor + $Y - $SliceHeight;
651 if ($FirstPoint) {
652 $this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings);
653 $FirstPoint = \false;
654 }
655 $this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings);
656 if ($i < 270 && $i > 90) {
657 $this->pChartObject->drawAntialiasPixel($Xc, $Yc + $SliceHeight, $Settings);
658 }
659 }
660 $this->pChartObject->drawLine($Xc, $Yc, $X0, $Y0, $Settings);
661 $Offset = $i - $DataGapAngle;
662 $ID--;
663 }
664 }
665 if ($WriteValues != null) {
666 $Step = 360 / (2 * PI * $Radius);
667 $Offset = 360;
668 $ID = count($Values) - 1;
669 $Settings = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE, "R" => $ValueR, "G" => $ValueG, "B" => $ValueB, "Alpha" => $ValueAlpha];
670 foreach ($Values as $Key => $Value) {
671 $EndAngle = $Offset - $Value * $ScaleFactor;
672 if ($EndAngle < 0) {
673 $EndAngle = 0;
674 }
675 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
676 if ($ValuePosition == PIE_VALUE_OUTSIDE) {
677 $Xc = cos(($Angle - 90) * PI / 180) * ($Radius + $ValuePadding) + $X;
678 $Yc = sin(($Angle - 90) * PI / 180) * ($Radius * $SkewFactor + $ValuePadding) + $Y - $SliceHeight;
679 } else {
680 $Xc = cos(($Angle - 90) * PI / 180) * $Radius / 2 + $X;
681 $Yc = sin(($Angle - 90) * PI / 180) * ($Radius * $SkewFactor) / 2 + $Y - $SliceHeight;
682 }
683 if ($WriteValues == PIE_VALUE_PERCENTAGE) {
684 $Display = round(100 / $SerieSum * $Value, $Precision) . "%";
685 } elseif ($WriteValues == PIE_VALUE_NATURAL) {
686 $Display = $Value . $ValueSuffix;
687 }
688 $this->pChartObject->drawText($Xc, $Yc, $Display, $Settings);
689 $Offset = $EndAngle - $DataGapAngle;
690 $ID--;
691 }
692 }
693 if ($DrawLabels) {
694 $Step = 360 / (2 * PI * $Radius);
695 $Offset = 360;
696 $ID = count($Values) - 1;
697 foreach ($Values as $Key => $Value) {
698 if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
699 $Settings = ["FillR" => $Palette[$ID]["R"], "FillG" => $Palette[$ID]["G"], "FillB" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
700 } else {
701 $Settings = ["FillR" => $LabelR, "FillG" => $LabelG, "FillB" => $LabelB, "Alpha" => $LabelAlpha];
702 }
703 $EndAngle = $Offset - $Value * $ScaleFactor;
704 if ($EndAngle < 0) {
705 $EndAngle = 0;
706 }
707 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
708 $Xc = cos(($Angle - 90) * PI / 180) * $Radius + $X;
709 $Yc = sin(($Angle - 90) * PI / 180) * $Radius * $SkewFactor + $Y - $SliceHeight;
710 if (isset($Data["Series"][$Data["Abscissa"]]["Data"][$ID])) {
711 $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$ID];
712 if ($LabelStacked) {
713 $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, \true, $X, $Y, $Radius, \true);
714 } else {
715 $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, \false);
716 }
717 }
718 $Offset = $EndAngle - $DataGapAngle;
719 $ID--;
720 }
721 }
722 if ($DrawLabels && $LabelStacked) {
723 $this->writeShiftedLabels();
724 }
725 $this->pChartObject->Shadow = $RestoreShadow;
726 return PIE_RENDERED;
727 }
728 /**
729 * Draw the legend of pie chart
730 * @param int $X
731 * @param int $Y
732 * @param array $Format
733 * @return int
734 */
735 public function drawPieLegend($X, $Y, array $Format = [])
736 {
737 $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName;
738 $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize;
739 $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->pChartObject->FontColorR;
740 $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->pChartObject->FontColorG;
741 $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->pChartObject->FontColorB;
742 $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5;
743 $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5;
744 $R = isset($Format["R"]) ? $Format["R"] : 200;
745 $G = isset($Format["G"]) ? $Format["G"] : 200;
746 $B = isset($Format["B"]) ? $Format["B"] : 200;
747 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
748 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255;
749 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255;
750 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255;
751 $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
752 $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND;
753 $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL;
754 if ($Surrounding != null) {
755 $BorderR = $R + $Surrounding;
756 $BorderG = $G + $Surrounding;
757 $BorderB = $B + $Surrounding;
758 }
759 $YStep = max($this->pChartObject->FontSize, $BoxSize) + 5;
760 $XStep = $BoxSize + 5;
761 /* Data Processing */
762 $Data = $this->pDataObject->getData();
763 $Palette = $this->pDataObject->getPalette();
764 /* Do we have an abscissa serie defined? */
765 if ($Data["Abscissa"] == "") {
766 return PIE_NO_ABSCISSA;
767 }
768 $Boundaries = [];
769 $Boundaries["L"] = $X;
770 $Boundaries["T"] = $Y;
771 $Boundaries["R"] = 0;
772 $Boundaries["B"] = 0;
773 $vY = $Y;
774 $vX = $X;
775 foreach ($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) {
776 $BoxArray = $this->pChartObject->getTextBox($vX + $BoxSize + 4, $vY + $BoxSize / 2, $FontName, $FontSize, 0, $Value);
777 if ($Mode == LEGEND_VERTICAL) {
778 if ($Boundaries["T"] > $BoxArray[2]["Y"] + $BoxSize / 2) {
779 $Boundaries["T"] = $BoxArray[2]["Y"] + $BoxSize / 2;
780 }
781 if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
782 $Boundaries["R"] = $BoxArray[1]["X"] + 2;
783 }
784 if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $BoxSize / 2) {
785 $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $BoxSize / 2;
786 }
787 $vY = $vY + $YStep;
788 } elseif ($Mode == LEGEND_HORIZONTAL) {
789 if ($Boundaries["T"] > $BoxArray[2]["Y"] + $BoxSize / 2) {
790 $Boundaries["T"] = $BoxArray[2]["Y"] + $BoxSize / 2;
791 }
792 if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
793 $Boundaries["R"] = $BoxArray[1]["X"] + 2;
794 }
795 if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $BoxSize / 2) {
796 $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $BoxSize / 2;
797 }
798 $vX = $Boundaries["R"] + $XStep;
799 }
800 }
801 $vY = $vY - $YStep;
802 $vX = $vX - $XStep;
803 $TopOffset = $Y - $Boundaries["T"];
804 if ($Boundaries["B"] - ($vY + $BoxSize) < $TopOffset) {
805 $Boundaries["B"] = $vY + $BoxSize + $TopOffset;
806 }
807 if ($Style == LEGEND_ROUND) {
808 $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]);
809 } elseif ($Style == LEGEND_BOX) {
810 $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]);
811 }
812 $RestoreShadow = $this->pChartObject->Shadow;
813 $this->pChartObject->Shadow = \false;
814 foreach ($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $Value) {
815 $R = $Palette[$Key]["R"];
816 $G = $Palette[$Key]["G"];
817 $B = $Palette[$Key]["B"];
818 $this->pChartObject->drawFilledRectangle($X + 1, $Y + 1, $X + $BoxSize + 1, $Y + $BoxSize + 1, ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]);
819 $this->pChartObject->drawFilledRectangle($X, $Y, $X + $BoxSize, $Y + $BoxSize, ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20]);
820 if ($Mode == LEGEND_VERTICAL) {
821 $this->pChartObject->drawText($X + $BoxSize + 4, $Y + $BoxSize / 2, $Value, ["R" => $FontR, "G" => $FontG, "B" => $FontB, "Align" => TEXT_ALIGN_MIDDLELEFT, "FontName" => $FontName, "FontSize" => $FontSize]);
822 $Y = $Y + $YStep;
823 } elseif ($Mode == LEGEND_HORIZONTAL) {
824 $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]);
825 $X = $BoxArray[1]["X"] + 2 + $XStep;
826 }
827 }
828 $this->pChartObject->Shadow = $RestoreShadow;
829 }
830 /**
831 * Set the color of the specified slice
832 * @param mixed $SliceID
833 * @param array $Format
834 */
835 public function setSliceColor($SliceID, array $Format = [])
836 {
837 $R = isset($Format["R"]) ? $Format["R"] : 0;
838 $G = isset($Format["G"]) ? $Format["G"] : 0;
839 $B = isset($Format["B"]) ? $Format["B"] : 0;
840 $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
841 $this->pDataObject->Palette[$SliceID]["R"] = $R;
842 $this->pDataObject->Palette[$SliceID]["G"] = $G;
843 $this->pDataObject->Palette[$SliceID]["B"] = $B;
844 $this->pDataObject->Palette[$SliceID]["Alpha"] = $Alpha;
845 }
846 /**
847 * Internally used compute the label positions
848 * @param int $X
849 * @param int $Y
850 * @param string $Label
851 * @param int|float $Angle
852 * @param array $Settings
853 * @param boolean $Stacked
854 * @param int $Xc
855 * @param int $Yc
856 * @param int $Radius
857 * @param boolean $Reversed
858 */
859 public function writePieLabel($X, $Y, $Label, $Angle, $Settings, $Stacked, $Xc = 0, $Yc = 0, $Radius = 0, $Reversed = \false)
860 {
861 $LabelOffset = 30;
862 $FontName = $this->pChartObject->FontName;
863 $FontSize = $this->pChartObject->FontSize;
864 if (!$Stacked) {
865 $Settings["Angle"] = 360 - $Angle;
866 $Settings["Length"] = 25;
867 $Settings["Size"] = 8;
868 $this->pChartObject->drawArrowLabel($X, $Y, " " . $Label . " ", $Settings);
869 } else {
870 $X2 = cos(deg2rad($Angle - 90)) * 20 + $X;
871 $Y2 = sin(deg2rad($Angle - 90)) * 20 + $Y;
872 $TxtPos = $this->pChartObject->getTextBox($X, $Y, $FontName, $FontSize, 0, $Label);
873 $Height = $TxtPos[0]["Y"] - $TxtPos[2]["Y"];
874 $YTop = $Y2 - $Height / 2 - 2;
875 $YBottom = $Y2 + $Height / 2 + 2;
876 if (count($this->LabelPos)) {
877 $Done = \false;
878 foreach ($this->LabelPos as $Key => $Settings) {
879 if (!$Done) {
880 $yTopAboveTopBelowBottom = $YTop >= $Settings["YTop"] && $YTop <= $Settings["YBottom"];
881 $yBottomAboveTopBelowBottom = $YBottom >= $Settings["YTop"] && $YBottom <= $Settings["YBottom"];
882 if ($Angle <= 90 && ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom)) {
883 $this->shift(0, 180, -($Height + 2), $Reversed);
884 $Done = \true;
885 }
886 if ($Angle > 90 && $Angle <= 180 && ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom)) {
887 $this->shift(0, 180, -($Height + 2), $Reversed);
888 $Done = \true;
889 }
890 if ($Angle > 180 && $Angle <= 270 && ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom)) {
891 $this->shift(180, 360, $Height + 2, $Reversed);
892 $Done = \true;
893 }
894 if ($Angle > 270 && $Angle <= 360 && ($yTopAboveTopBelowBottom || $yBottomAboveTopBelowBottom)) {
895 $this->shift(180, 360, $Height + 2, $Reversed);
896 $Done = \true;
897 }
898 }
899 }
900 }
901 $LabelSettings = ["YTop" => $YTop, "YBottom" => $YBottom, "Label" => $Label, "Angle" => $Angle, "X1" => $X, "Y1" => $Y, "X2" => $X2, "Y2" => $Y2];
902 if ($Angle <= 180) {
903 $LabelSettings["X3"] = $Xc + $Radius + $LabelOffset;
904 }
905 if ($Angle > 180) {
906 $LabelSettings["X3"] = $Xc - $Radius - $LabelOffset;
907 }
908 $this->LabelPos[] = $LabelSettings;
909 }
910 }
911 /**
912 * Internally used to shift label positions
913 * @param int $StartAngle
914 * @param int $EndAngle
915 * @param int $Offset
916 * @param boolean $Reversed
917 */
918 public function shift($StartAngle, $EndAngle, $Offset, $Reversed)
919 {
920 if ($Reversed) {
921 $Offset = -$Offset;
922 }
923 foreach ($this->LabelPos as $Key => $Settings) {
924 if ($Settings["Angle"] > $StartAngle && $Settings["Angle"] <= $EndAngle) {
925 $this->LabelPos[$Key]["YTop"] = $Settings["YTop"] + $Offset;
926 $this->LabelPos[$Key]["YBottom"] = $Settings["YBottom"] + $Offset;
927 $this->LabelPos[$Key]["Y2"] = $Settings["Y2"] + $Offset;
928 }
929 }
930 }
931 /**
932 * Internally used to write the re-computed labels
933 * @return null|int
934 */
935 public function writeShiftedLabels()
936 {
937 if (!count($this->LabelPos)) {
938 return 0;
939 }
940 foreach ($this->LabelPos as $Settings) {
941 $X1 = $Settings["X1"];
942 $Y1 = $Settings["Y1"];
943 $X2 = $Settings["X2"];
944 $Y2 = $Settings["Y2"];
945 $X3 = $Settings["X3"];
946 $Angle = $Settings["Angle"];
947 $Label = $Settings["Label"];
948 $this->pChartObject->drawArrow($X2, $Y2, $X1, $Y1, ["Size" => 8]);
949 if ($Angle <= 180) {
950 $this->pChartObject->drawLine($X2, $Y2, $X3, $Y2);
951 $this->pChartObject->drawText($X3 + 2, $Y2, $Label, ["Align" => TEXT_ALIGN_MIDDLELEFT]);
952 } else {
953 $this->pChartObject->drawLine($X2, $Y2, $X3, $Y2);
954 $this->pChartObject->drawText($X3 - 2, $Y2, $Label, ["Align" => TEXT_ALIGN_MIDDLERIGHT]);
955 }
956 }
957 }
958 /**
959 * Draw a ring chart
960 * @param int $X
961 * @param int $Y
962 * @param array $Format
963 * @return int
964 */
965 public function draw2DRing($X, $Y, array $Format = [])
966 {
967 $OuterRadius = isset($Format["Radius"]) ? $Format["Radius"] : 60;
968 // For compatibility
969 $OuterRadius = isset($Format["OuterRadius"]) ? $Format["OuterRadius"] : $OuterRadius;
970 $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0;
971 $InnerRadius = isset($Format["InnerRadius"]) ? $Format["InnerRadius"] : 30;
972 $Border = isset($Format["Border"]) ? $Format["Border"] : \false;
973 $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255;
974 $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255;
975 $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255;
976 $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100;
977 $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : \false;
978 $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : \false;
979 $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : \false;
980 $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL;
981 $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0;
982 $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0;
983 $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0;
984 $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100;
985 $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : null;
986 //PIE_VALUE_PERCENTAGE
987 $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 5;
988 $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE;
989 $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : "";
990 $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255;
991 $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255;
992 $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255;
993 $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100;
994 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
995 /* Data Processing */
996 $Data = $this->pDataObject->getData();
997 $Palette = $this->pDataObject->getPalette();
998 /* Do we have an abscissa serie defined? */
999 if ($Data["Abscissa"] == "") {
1000 return PIE_NO_ABSCISSA;
1001 }
1002 /* Try to find the data serie */
1003 $DataSerie = null;
1004 foreach ($Data["Series"] as $SerieName => $SerieData) {
1005 if ($SerieName != $Data["Abscissa"]) {
1006 $DataSerie = $SerieName;
1007 }
1008 }
1009 /* Do we have data to compute? */
1010 if (!$DataSerie) {
1011 return PIE_NO_DATASERIE;
1012 }
1013 /* Remove unused data */
1014 list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]);
1015 /* Compute the pie sum */
1016 $SerieSum = $this->pDataObject->getSum($DataSerie);
1017 /* Do we have data to draw? */
1018 if ($SerieSum == 0) {
1019 return PIE_SUMISNULL;
1020 }
1021 /* Dump the real number of data to draw */
1022 $Values = [];
1023 foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
1024 if ($Value != 0) {
1025 $Values[] = $Value;
1026 }
1027 }
1028 /* Compute the wasted angular space between series */
1029 if (count($Values) == 1) {
1030 $WastedAngular = 0;
1031 } else {
1032 $WastedAngular = 0;
1033 }
1034 // count($Values)
1035 /* Compute the scale */
1036 $ScaleFactor = (360 - $WastedAngular) / $SerieSum;
1037 $RestoreShadow = $this->pChartObject->Shadow;
1038 if ($this->pChartObject->Shadow) {
1039 $this->pChartObject->Shadow = \false;
1040 $ShadowFormat = $Format;
1041 $ShadowFormat["Shadow"] = \true;
1042 $this->draw2DRing($X + $this->pChartObject->ShadowX, $Y + $this->pChartObject->ShadowY, $ShadowFormat);
1043 }
1044 /* Draw the polygon pie elements */
1045 $Step = 360 / (2 * PI * $OuterRadius);
1046 $Offset = 0;
1047 $ID = 0;
1048 foreach ($Values as $Key => $Value) {
1049 if ($Shadow) {
1050 $Settings = ["R" => $this->pChartObject->ShadowR, "G" => $this->pChartObject->ShadowG, "B" => $this->pChartObject->ShadowB, "Alpha" => $this->pChartObject->Shadowa];
1051 $BorderColor = $Settings;
1052 } else {
1053 if (!isset($Palette[$ID]["R"])) {
1054 $Color = $this->pChartObject->getRandomColor();
1055 $Palette[$ID] = $Color;
1056 $this->pDataObject->savePalette($ID, $Color);
1057 }
1058 $Settings = ["R" => $Palette[$ID]["R"], "G" => $Palette[$ID]["G"], "B" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
1059 if ($Border) {
1060 $BorderColor = ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha];
1061 } else {
1062 $BorderColor = $Settings;
1063 }
1064 }
1065 $Plots = [];
1066 $Boundaries = [];
1067 $AAPixels = [];
1068 $EndAngle = $Offset + $Value * $ScaleFactor;
1069 if ($EndAngle > 360) {
1070 $EndAngle = 360;
1071 }
1072 for ($i = $Offset; $i <= $EndAngle; $i = $i + $Step) {
1073 $Xc = cos(($i - 90) * PI / 180) * $OuterRadius + $X;
1074 $Yc = sin(($i - 90) * PI / 180) * $OuterRadius + $Y;
1075 if (!isset($Boundaries[0]["X1"])) {
1076 $Boundaries[0]["X1"] = $Xc;
1077 $Boundaries[0]["Y1"] = $Yc;
1078 }
1079 $AAPixels[] = [$Xc, $Yc];
1080 if ($i < 90) {
1081 $Yc++;
1082 }
1083 if ($i > 180 && $i < 270) {
1084 $Xc++;
1085 }
1086 if ($i >= 270) {
1087 $Xc++;
1088 $Yc++;
1089 }
1090 $Plots[] = $Xc;
1091 $Plots[] = $Yc;
1092 }
1093 $Boundaries[1]["X1"] = $Xc;
1094 $Boundaries[1]["Y1"] = $Yc;
1095 $Lasti = $EndAngle;
1096 for ($i = $EndAngle; $i >= $Offset; $i = $i - $Step) {
1097 $Xc = cos(($i - 90) * PI / 180) * ($InnerRadius - 1) + $X;
1098 $Yc = sin(($i - 90) * PI / 180) * ($InnerRadius - 1) + $Y;
1099 if (!isset($Boundaries[1]["X2"])) {
1100 $Boundaries[1]["X2"] = $Xc;
1101 $Boundaries[1]["Y2"] = $Yc;
1102 }
1103 $AAPixels[] = [$Xc, $Yc];
1104 $Xc = cos(($i - 90) * PI / 180) * $InnerRadius + $X;
1105 $Yc = sin(($i - 90) * PI / 180) * $InnerRadius + $Y;
1106 if ($i < 90) {
1107 $Yc++;
1108 }
1109 if ($i > 180 && $i < 270) {
1110 $Xc++;
1111 }
1112 if ($i >= 270) {
1113 $Xc++;
1114 $Yc++;
1115 }
1116 $Plots[] = $Xc;
1117 $Plots[] = $Yc;
1118 }
1119 $Boundaries[0]["X2"] = $Xc;
1120 $Boundaries[0]["Y2"] = $Yc;
1121 /* Draw the polygon */
1122 $this->pChartObject->drawPolygon($Plots, $Settings);
1123 if ($RecordImageMap && !$Shadow) {
1124 $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);
1125 }
1126 /* Smooth the edges using AA */
1127 foreach ($AAPixels as $Pos) {
1128 $this->pChartObject->drawAntialiasPixel($Pos[0], $Pos[1], $BorderColor);
1129 }
1130 $this->pChartObject->drawLine($Boundaries[0]["X1"], $Boundaries[0]["Y1"], $Boundaries[0]["X2"], $Boundaries[0]["Y2"], $BorderColor);
1131 $this->pChartObject->drawLine($Boundaries[1]["X1"], $Boundaries[1]["Y1"], $Boundaries[1]["X2"], $Boundaries[1]["Y2"], $BorderColor);
1132 if ($DrawLabels && !$Shadow) {
1133 if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
1134 $Settings = ["FillR" => $Palette[$ID]["R"], "FillG" => $Palette[$ID]["G"], "FillB" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
1135 } else {
1136 $Settings = ["FillR" => $LabelR, "FillG" => $LabelG, "FillB" => $LabelB, "Alpha" => $LabelAlpha];
1137 }
1138 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
1139 $Xc = cos(($Angle - 90) * PI / 180) * $OuterRadius + $X;
1140 $Yc = sin(($Angle - 90) * PI / 180) * $OuterRadius + $Y;
1141 $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key];
1142 if ($LabelStacked) {
1143 $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, \true, $X, $Y, $OuterRadius);
1144 } else {
1145 $this->writePieLabel($Xc, $Yc, $Label, $Angle, $Settings, \false);
1146 }
1147 }
1148 $Offset = $Lasti;
1149 $ID++;
1150 }
1151 if ($DrawLabels && $LabelStacked) {
1152 $this->writeShiftedLabels();
1153 }
1154 if ($WriteValues && !$Shadow) {
1155 $Step = 360 / (2 * PI * $OuterRadius);
1156 $Offset = 0;
1157 foreach ($Values as $Key => $Value) {
1158 $EndAngle = $Offset + $Value * $ScaleFactor;
1159 if ($EndAngle > 360) {
1160 $EndAngle = 360;
1161 }
1162 $Angle = $Offset + $Value * $ScaleFactor / 2;
1163 if ($ValuePosition == PIE_VALUE_OUTSIDE) {
1164 $Xc = cos(($Angle - 90) * PI / 180) * ($OuterRadius + $ValuePadding) + $X;
1165 $Yc = sin(($Angle - 90) * PI / 180) * ($OuterRadius + $ValuePadding) + $Y;
1166 if ($Angle >= 0 && $Angle <= 90) {
1167 $Align = TEXT_ALIGN_BOTTOMLEFT;
1168 }
1169 if ($Angle > 90 && $Angle <= 180) {
1170 $Align = TEXT_ALIGN_TOPLEFT;
1171 }
1172 if ($Angle > 180 && $Angle <= 270) {
1173 $Align = TEXT_ALIGN_TOPRIGHT;
1174 }
1175 if ($Angle > 270) {
1176 $Align = TEXT_ALIGN_BOTTOMRIGHT;
1177 }
1178 } else {
1179 $Xc = cos(($Angle - 90) * PI / 180) * (($OuterRadius - $InnerRadius) / 2 + $InnerRadius) + $X;
1180 $Yc = sin(($Angle - 90) * PI / 180) * (($OuterRadius - $InnerRadius) / 2 + $InnerRadius) + $Y;
1181 $Align = TEXT_ALIGN_MIDDLEMIDDLE;
1182 }
1183 if ($WriteValues == PIE_VALUE_PERCENTAGE) {
1184 $Display = round(100 / $SerieSum * $Value, $Precision) . "%";
1185 } elseif ($WriteValues == PIE_VALUE_NATURAL) {
1186 $Display = $Value . $ValueSuffix;
1187 } else {
1188 $Display = "";
1189 }
1190 $this->pChartObject->drawText($Xc, $Yc, $Display, ["Align" => $Align, "R" => $ValueR, "G" => $ValueG, "B" => $ValueB]);
1191 $Offset = $EndAngle;
1192 }
1193 }
1194 $this->pChartObject->Shadow = $RestoreShadow;
1195 return PIE_RENDERED;
1196 }
1197 /**
1198 * Draw a 3D ring chart
1199 * @param int $X
1200 * @param int $Y
1201 * @param array $Format
1202 * @return int
1203 */
1204 public function draw3DRing($X, $Y, array $Format = [])
1205 {
1206 $OuterRadius = isset($Format["OuterRadius"]) ? $Format["OuterRadius"] : 100;
1207 $Precision = isset($Format["Precision"]) ? $Format["Precision"] : 0;
1208 $InnerRadius = isset($Format["InnerRadius"]) ? $Format["InnerRadius"] : 30;
1209 $SkewFactor = isset($Format["SkewFactor"]) ? $Format["SkewFactor"] : 0.6;
1210 $SliceHeight = isset($Format["SliceHeight"]) ? $Format["SliceHeight"] : 10;
1211 $DataGapAngle = isset($Format["DataGapAngle"]) ? $Format["DataGapAngle"] : 10;
1212 $DataGapRadius = isset($Format["DataGapRadius"]) ? $Format["DataGapRadius"] : 10;
1213 $Border = isset($Format["Border"]) ? $Format["Border"] : \false;
1214 $Shadow = isset($Format["Shadow"]) ? $Format["Shadow"] : \false;
1215 $DrawLabels = isset($Format["DrawLabels"]) ? $Format["DrawLabels"] : \false;
1216 $LabelStacked = isset($Format["LabelStacked"]) ? $Format["LabelStacked"] : \false;
1217 $LabelColor = isset($Format["LabelColor"]) ? $Format["LabelColor"] : PIE_LABEL_COLOR_MANUAL;
1218 $LabelR = isset($Format["LabelR"]) ? $Format["LabelR"] : 0;
1219 $LabelG = isset($Format["LabelG"]) ? $Format["LabelG"] : 0;
1220 $LabelB = isset($Format["LabelB"]) ? $Format["LabelB"] : 0;
1221 $LabelAlpha = isset($Format["LabelAlpha"]) ? $Format["LabelAlpha"] : 100;
1222 $Cf = isset($Format["Cf"]) ? $Format["Cf"] : 20;
1223 $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : PIE_VALUE_NATURAL;
1224 $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : $SliceHeight + 15;
1225 $ValuePosition = isset($Format["ValuePosition"]) ? $Format["ValuePosition"] : PIE_VALUE_OUTSIDE;
1226 $ValueSuffix = isset($Format["ValueSuffix"]) ? $Format["ValueSuffix"] : "";
1227 $ValueR = isset($Format["ValueR"]) ? $Format["ValueR"] : 255;
1228 $ValueG = isset($Format["ValueG"]) ? $Format["ValueG"] : 255;
1229 $ValueB = isset($Format["ValueB"]) ? $Format["ValueB"] : 255;
1230 $ValueAlpha = isset($Format["ValueAlpha"]) ? $Format["ValueAlpha"] : 100;
1231 $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : \false;
1232 /* Error correction for overlaying rounded corners */
1233 if ($SkewFactor < 0.5) {
1234 $SkewFactor = 0.5;
1235 }
1236 /* Data Processing */
1237 $Data = $this->pDataObject->getData();
1238 $Palette = $this->pDataObject->getPalette();
1239 /* Do we have an abscissa serie defined? */
1240 if ($Data["Abscissa"] == "") {
1241 return PIE_NO_ABSCISSA;
1242 }
1243 /* Try to find the data serie */
1244 $DataSerie = null;
1245 foreach ($Data["Series"] as $SerieName => $SerieData) {
1246 if ($SerieName != $Data["Abscissa"]) {
1247 $DataSerie = $SerieName;
1248 }
1249 }
1250 /* Do we have data to compute? */
1251 if (!$DataSerie) {
1252 return PIE_NO_DATASERIE;
1253 }
1254 /* Remove unused data */
1255 list($Data, $Palette) = $this->clean0Values($Data, $Palette, $DataSerie, $Data["Abscissa"]);
1256 /* Compute the pie sum */
1257 $SerieSum = $this->pDataObject->getSum($DataSerie);
1258 /* Do we have data to draw? */
1259 if ($SerieSum == 0) {
1260 return PIE_SUMISNULL;
1261 }
1262 /* Dump the real number of data to draw */
1263 $Values = [];
1264 foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
1265 if ($Value != 0) {
1266 $Values[] = $Value;
1267 }
1268 }
1269 /* Compute the wasted angular space between series */
1270 if (count($Values) == 1) {
1271 $WastedAngular = 0;
1272 } else {
1273 $WastedAngular = count($Values) * $DataGapAngle;
1274 }
1275 /* Compute the scale */
1276 $ScaleFactor = (360 - $WastedAngular) / $SerieSum;
1277 $RestoreShadow = $this->pChartObject->Shadow;
1278 if ($this->pChartObject->Shadow) {
1279 $this->pChartObject->Shadow = \false;
1280 }
1281 /* Draw the polygon ring elements */
1282 $Offset = 360;
1283 $ID = count($Values) - 1;
1284 $Values = array_reverse($Values);
1285 $Slice = 0;
1286 $Slices = [];
1287 $SliceColors = [];
1288 $Visible = [];
1289 $SliceAngle = [];
1290 foreach ($Values as $Key => $Value) {
1291 if (!isset($Palette[$ID]["R"])) {
1292 $Color = $this->pChartObject->getRandomColor();
1293 $Palette[$ID] = $Color;
1294 $this->pDataObject->savePalette($ID, $Color);
1295 }
1296 $Settings = ["R" => $Palette[$ID]["R"], "G" => $Palette[$ID]["G"], "B" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
1297 $SliceColors[$Slice] = $Settings;
1298 $StartAngle = $Offset;
1299 $EndAngle = $Offset - $Value * $ScaleFactor;
1300 if ($EndAngle < 0) {
1301 $EndAngle = 0;
1302 }
1303 if ($StartAngle > 180) {
1304 $Visible[$Slice]["Start"] = \true;
1305 } else {
1306 $Visible[$Slice]["Start"] = \true;
1307 }
1308 if ($EndAngle < 180) {
1309 $Visible[$Slice]["End"] = \false;
1310 } else {
1311 $Visible[$Slice]["End"] = \true;
1312 }
1313 $Step = 360 / (2 * PI * $OuterRadius) / 2;
1314 $OutX1 = VOID;
1315 $OutY1 = VOID;
1316 for ($i = $Offset; $i >= $EndAngle; $i = $i - $Step) {
1317 $Xc = cos(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 2) + $X;
1318 $Yc = sin(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 2) * $SkewFactor + $Y;
1319 $Slices[$Slice]["AA"][] = [$Xc, $Yc];
1320 $Xc = cos(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 1) + $X;
1321 $Yc = sin(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius - 1) * $SkewFactor + $Y;
1322 $Slices[$Slice]["AA"][] = [$Xc, $Yc];
1323 $Xc = cos(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) + $X;
1324 $Yc = sin(($i - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) * $SkewFactor + $Y;
1325 $this->pChartObject->drawAntialiasPixel($Xc, $Yc, $Settings);
1326 if ($OutX1 == VOID) {
1327 $OutX1 = $Xc;
1328 $OutY1 = $Yc;
1329 }
1330 if ($i < 90) {
1331 $Yc++;
1332 }
1333 if ($i > 90 && $i < 180) {
1334 $Xc++;
1335 }
1336 if ($i > 180 && $i < 270) {
1337 $Xc++;
1338 }
1339 if ($i >= 270) {
1340 $Xc++;
1341 $Yc++;
1342 }
1343 $Slices[$Slice]["BottomPoly"][] = floor($Xc);
1344 $Slices[$Slice]["BottomPoly"][] = floor($Yc);
1345 $Slices[$Slice]["TopPoly"][] = floor($Xc);
1346 $Slices[$Slice]["TopPoly"][] = floor($Yc) - $SliceHeight;
1347 $Slices[$Slice]["Angle"][] = $i;
1348 }
1349 $OutX2 = $Xc;
1350 $OutY2 = $Yc;
1351 $Slices[$Slice]["Angle"][] = VOID;
1352 $Lasti = $i;
1353 $Step = 360 / (2 * PI * $InnerRadius) / 2;
1354 $InX1 = VOID;
1355 $InY1 = VOID;
1356 for ($i = $EndAngle; $i <= $Offset; $i = $i + $Step) {
1357 $Xc = cos(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius - 1) + $X;
1358 $Yc = sin(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius - 1) * $SkewFactor + $Y;
1359 $Slices[$Slice]["AA"][] = [$Xc, $Yc];
1360 $Xc = cos(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius) + $X;
1361 $Yc = sin(($i - 90) * PI / 180) * ($InnerRadius + $DataGapRadius) * $SkewFactor + $Y;
1362 $Slices[$Slice]["AA"][] = [$Xc, $Yc];
1363 if ($InX1 == VOID) {
1364 $InX1 = $Xc;
1365 $InY1 = $Yc;
1366 }
1367 if ($i < 90) {
1368 $Yc++;
1369 }
1370 if ($i > 90 && $i < 180) {
1371 $Xc++;
1372 }
1373 if ($i > 180 && $i < 270) {
1374 $Xc++;
1375 }
1376 if ($i >= 270) {
1377 $Xc++;
1378 $Yc++;
1379 }
1380 $Slices[$Slice]["BottomPoly"][] = floor($Xc);
1381 $Slices[$Slice]["BottomPoly"][] = floor($Yc);
1382 $Slices[$Slice]["TopPoly"][] = floor($Xc);
1383 $Slices[$Slice]["TopPoly"][] = floor($Yc) - $SliceHeight;
1384 $Slices[$Slice]["Angle"][] = $i;
1385 }
1386 $InX2 = $Xc;
1387 $InY2 = $Yc;
1388 $Slices[$Slice]["InX1"] = $InX1;
1389 $Slices[$Slice]["InY1"] = $InY1;
1390 $Slices[$Slice]["InX2"] = $InX2;
1391 $Slices[$Slice]["InY2"] = $InY2;
1392 $Slices[$Slice]["OutX1"] = $OutX1;
1393 $Slices[$Slice]["OutY1"] = $OutY1;
1394 $Slices[$Slice]["OutX2"] = $OutX2;
1395 $Slices[$Slice]["OutY2"] = $OutY2;
1396 $Offset = $Lasti - $DataGapAngle;
1397 $ID--;
1398 $Slice++;
1399 }
1400 /* Draw the bottom pie splice */
1401 foreach ($Slices as $SliceID => $Plots) {
1402 $Settings = $SliceColors[$SliceID];
1403 $Settings["NoBorder"] = \true;
1404 $this->pChartObject->drawPolygon($Plots["BottomPoly"], $Settings);
1405 foreach ($Plots["AA"] as $Key => $Pos) {
1406 $this->pChartObject->drawAntialiasPixel($Pos[0], $Pos[1], $Settings);
1407 }
1408 $this->pChartObject->drawLine($Plots["InX1"], $Plots["InY1"], $Plots["OutX2"], $Plots["OutY2"], $Settings);
1409 $this->pChartObject->drawLine($Plots["InX2"], $Plots["InY2"], $Plots["OutX1"], $Plots["OutY1"], $Settings);
1410 }
1411 $Slices = array_reverse($Slices);
1412 $SliceColors = array_reverse($SliceColors);
1413 /* Draw the vertical edges (semi-visible) */
1414 foreach ($Slices as $SliceID => $Plots) {
1415 $Settings = $SliceColors[$SliceID];
1416 $Settings["NoBorder"] = \true;
1417 $Settings["R"] = $Settings["R"] + $Cf;
1418 $Settings["G"] = $Settings["G"] + $Cf;
1419 $Settings["B"] = $Settings["B"] + $Cf;
1420 $StartAngle = $Plots["Angle"][0];
1421 foreach ($Plots["Angle"] as $Key => $Angle) {
1422 if ($Angle == VOID) {
1423 $EndAngle = $Plots["Angle"][$Key - 1];
1424 }
1425 }
1426 if ($StartAngle >= 270 || $StartAngle <= 90) {
1427 $this->pChartObject->drawLine($Plots["OutX1"], $Plots["OutY1"], $Plots["OutX1"], $Plots["OutY1"] - $SliceHeight, $Settings);
1428 }
1429 if ($StartAngle >= 270 || $StartAngle <= 90) {
1430 $this->pChartObject->drawLine($Plots["OutX2"], $Plots["OutY2"], $Plots["OutX2"], $Plots["OutY2"] - $SliceHeight, $Settings);
1431 }
1432 $this->pChartObject->drawLine($Plots["InX1"], $Plots["InY1"], $Plots["InX1"], $Plots["InY1"] - $SliceHeight, $Settings);
1433 $this->pChartObject->drawLine($Plots["InX2"], $Plots["InY2"], $Plots["InX2"], $Plots["InY2"] - $SliceHeight, $Settings);
1434 }
1435 /* Draw the inner vertical slices */
1436 foreach ($Slices as $SliceID => $Plots) {
1437 $Settings = $SliceColors[$SliceID];
1438 $Settings["NoBorder"] = \true;
1439 $Settings["R"] = $Settings["R"] + $Cf;
1440 $Settings["G"] = $Settings["G"] + $Cf;
1441 $Settings["B"] = $Settings["B"] + $Cf;
1442 $Outer = \true;
1443 $Inner = \false;
1444 $InnerPlotsA = [];
1445 $InnerPlotsB = [];
1446 foreach ($Plots["Angle"] as $ID => $Angle) {
1447 if ($Angle == VOID) {
1448 $Outer = \false;
1449 $Inner = \true;
1450 } elseif ($Inner && ($Angle < 90 || $Angle > 270) && isset($Plots["BottomPoly"][$ID * 2])) {
1451 $Xo = $Plots["BottomPoly"][$ID * 2];
1452 $Yo = $Plots["BottomPoly"][$ID * 2 + 1];
1453 $InnerPlotsA[] = $Xo;
1454 $InnerPlotsA[] = $Yo;
1455 $InnerPlotsB[] = $Xo;
1456 $InnerPlotsB[] = $Yo - $SliceHeight;
1457 }
1458 }
1459 if (count($InnerPlotsA)) {
1460 $InnerPlots = array_merge($InnerPlotsA, $this->arrayReverse($InnerPlotsB));
1461 $this->pChartObject->drawPolygon($InnerPlots, $Settings);
1462 }
1463 }
1464 /* Draw the splice top and left poly */
1465 foreach ($Slices as $SliceID => $Plots) {
1466 $Settings = $SliceColors[$SliceID];
1467 $Settings["NoBorder"] = \true;
1468 $Settings["R"] = $Settings["R"] + $Cf * 1.5;
1469 $Settings["G"] = $Settings["G"] + $Cf * 1.5;
1470 $Settings["B"] = $Settings["B"] + $Cf * 1.5;
1471 $StartAngle = $Plots["Angle"][0];
1472 foreach ($Plots["Angle"] as $Key => $Angle) {
1473 if ($Angle == VOID) {
1474 $EndAngle = $Plots["Angle"][$Key - 1];
1475 }
1476 }
1477 if ($StartAngle < 180) {
1478 $Points = [];
1479 $Points[] = $Plots["InX2"];
1480 $Points[] = $Plots["InY2"];
1481 $Points[] = $Plots["InX2"];
1482 $Points[] = $Plots["InY2"] - $SliceHeight;
1483 $Points[] = $Plots["OutX1"];
1484 $Points[] = $Plots["OutY1"] - $SliceHeight;
1485 $Points[] = $Plots["OutX1"];
1486 $Points[] = $Plots["OutY1"];
1487 $this->pChartObject->drawPolygon($Points, $Settings);
1488 }
1489 if ($EndAngle > 180) {
1490 $Points = [];
1491 $Points[] = $Plots["InX1"];
1492 $Points[] = $Plots["InY1"];
1493 $Points[] = $Plots["InX1"];
1494 $Points[] = $Plots["InY1"] - $SliceHeight;
1495 $Points[] = $Plots["OutX2"];
1496 $Points[] = $Plots["OutY2"] - $SliceHeight;
1497 $Points[] = $Plots["OutX2"];
1498 $Points[] = $Plots["OutY2"];
1499 $this->pChartObject->drawPolygon($Points, $Settings);
1500 }
1501 }
1502 /* Draw the vertical edges (visible) */
1503 foreach ($Slices as $SliceID => $Plots) {
1504 $Settings = $SliceColors[$SliceID];
1505 $Settings["NoBorder"] = \true;
1506 $Settings["R"] = $Settings["R"] + $Cf;
1507 $Settings["G"] = $Settings["G"] + $Cf;
1508 $Settings["B"] = $Settings["B"] + $Cf;
1509 $StartAngle = $Plots["Angle"][0];
1510 foreach ($Plots["Angle"] as $Key => $Angle) {
1511 if ($Angle == VOID) {
1512 $EndAngle = $Plots["Angle"][$Key - 1];
1513 }
1514 }
1515 if ($StartAngle <= 270 && $StartAngle >= 90) {
1516 $this->pChartObject->drawLine($Plots["OutX1"], $Plots["OutY1"], $Plots["OutX1"], $Plots["OutY1"] - $SliceHeight, $Settings);
1517 }
1518 if ($EndAngle <= 270 && $EndAngle >= 90) {
1519 $this->pChartObject->drawLine($Plots["OutX2"], $Plots["OutY2"], $Plots["OutX2"], $Plots["OutY2"] - $SliceHeight, $Settings);
1520 }
1521 }
1522 /* Draw the outer vertical slices */
1523 foreach ($Slices as $SliceID => $Plots) {
1524 $Settings = $SliceColors[$SliceID];
1525 $Settings["NoBorder"] = \true;
1526 $Settings["R"] = $Settings["R"] + $Cf;
1527 $Settings["G"] = $Settings["G"] + $Cf;
1528 $Settings["B"] = $Settings["B"] + $Cf;
1529 $Outer = \true;
1530 $Inner = \false;
1531 $OuterPlotsA = [];
1532 $OuterPlotsB = [];
1533 $InnerPlotsA = [];
1534 $InnerPlotsB = [];
1535 foreach ($Plots["Angle"] as $ID => $Angle) {
1536 if ($Angle == VOID) {
1537 $Outer = \false;
1538 $Inner = \true;
1539 } elseif ($Outer && ($Angle > 90 && $Angle < 270) && isset($Plots["BottomPoly"][$ID * 2])) {
1540 $Xo = $Plots["BottomPoly"][$ID * 2];
1541 $Yo = $Plots["BottomPoly"][$ID * 2 + 1];
1542 $OuterPlotsA[] = $Xo;
1543 $OuterPlotsA[] = $Yo;
1544 $OuterPlotsB[] = $Xo;
1545 $OuterPlotsB[] = $Yo - $SliceHeight;
1546 }
1547 }
1548 if (count($OuterPlotsA)) {
1549 $OuterPlots = array_merge($OuterPlotsA, $this->arrayReverse($OuterPlotsB));
1550 $this->pChartObject->drawPolygon($OuterPlots, $Settings);
1551 }
1552 }
1553 $Slices = array_reverse($Slices);
1554 $SliceColors = array_reverse($SliceColors);
1555 /* Draw the top pie splice */
1556 foreach ($Slices as $SliceID => $Plots) {
1557 $Settings = $SliceColors[$SliceID];
1558 $Settings["NoBorder"] = \true;
1559 $Settings["R"] = $Settings["R"] + $Cf * 2;
1560 $Settings["G"] = $Settings["G"] + $Cf * 2;
1561 $Settings["B"] = $Settings["B"] + $Cf * 2;
1562 $this->pChartObject->drawPolygon($Plots["TopPoly"], $Settings);
1563 if ($RecordImageMap) {
1564 $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]);
1565 }
1566 foreach ($Plots["AA"] as $Key => $Pos) {
1567 $this->pChartObject->drawAntialiasPixel($Pos[0], $Pos[1] - $SliceHeight, $Settings);
1568 }
1569 $this->pChartObject->drawLine($Plots["InX1"], $Plots["InY1"] - $SliceHeight, $Plots["OutX2"], $Plots["OutY2"] - $SliceHeight, $Settings);
1570 $this->pChartObject->drawLine($Plots["InX2"], $Plots["InY2"] - $SliceHeight, $Plots["OutX1"], $Plots["OutY1"] - $SliceHeight, $Settings);
1571 }
1572 if ($DrawLabels) {
1573 $Offset = 360;
1574 foreach ($Values as $Key => $Value) {
1575 $StartAngle = $Offset;
1576 $EndAngle = $Offset - $Value * $ScaleFactor;
1577 if ($EndAngle < 0) {
1578 $EndAngle = 0;
1579 }
1580 if ($LabelColor == PIE_LABEL_COLOR_AUTO) {
1581 $Settings = ["FillR" => $Palette[$ID]["R"], "FillG" => $Palette[$ID]["G"], "FillB" => $Palette[$ID]["B"], "Alpha" => $Palette[$ID]["Alpha"]];
1582 } else {
1583 $Settings = ["FillR" => $LabelR, "FillG" => $LabelG, "FillB" => $LabelB, "Alpha" => $LabelAlpha];
1584 }
1585 $Angle = ($EndAngle - $Offset) / 2 + $Offset;
1586 $Xc = cos(($Angle - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) + $X;
1587 $Yc = sin(($Angle - 90) * PI / 180) * ($OuterRadius + $DataGapRadius) * $SkewFactor + $Y;
1588 $Label = "";
1589 if ($WriteValues == PIE_VALUE_PERCENTAGE) {
1590 $Label = round(100 / $SerieSum * $Value, $Precision) . "%";
1591 } elseif ($WriteValues == PIE_VALUE_NATURAL) {
1592 $Label = $Data["Series"][$Data["Abscissa"]]["Data"][$Key];
1593 }
1594 if ($LabelStacked) {
1595 $this->writePieLabel($Xc, $Yc - $SliceHeight, $Label, $Angle, $Settings, \true, $X, $Y, $OuterRadius);
1596 } else {
1597 $this->writePieLabel($Xc, $Yc - $SliceHeight, $Label, $Angle, $Settings, \false);
1598 }
1599 $Offset = $EndAngle - $DataGapAngle;
1600 $ID--;
1601 $Slice++;
1602 }
1603 }
1604 if ($DrawLabels && $LabelStacked) {
1605 $this->writeShiftedLabels();
1606 }
1607 $this->pChartObject->Shadow = $RestoreShadow;
1608 return PIE_RENDERED;
1609 }
1610 /**
1611 * Serialize an array
1612 * @param array $Data
1613 * @return string
1614 */
1615 public function arraySerialize(array $Data)
1616 {
1617 $Result = "";
1618 foreach ($Data as $Value) {
1619 if ($Result == "") {
1620 $Result = floor($Value);
1621 } else {
1622 $Result = $Result . "," . floor($Value);
1623 }
1624 }
1625 return $Result;
1626 }
1627 /**
1628 * Reverse an array
1629 * @param array $Plots
1630 * @return array
1631 */
1632 public function arrayReverse(array $Plots)
1633 {
1634 $Result = [];
1635 for ($i = count($Plots) - 1; $i >= 0; $i = $i - 2) {
1636 $Result[] = $Plots[$i - 1];
1637 $Result[] = $Plots[$i];
1638 }
1639 return $Result;
1640 }
1641 /**
1642 * Remove unused series & values
1643 * @param array $Data
1644 * @param array $Palette
1645 * @param string $DataSerie
1646 * @param string $AbscissaSerie
1647 * @return array
1648 */
1649 public function clean0Values(array $Data, array $Palette, $DataSerie, $AbscissaSerie)
1650 {
1651 $NewPalette = [];
1652 $NewData = [];
1653 $NewAbscissa = [];
1654 /* Remove unused series */
1655 foreach (array_keys($Data["Series"]) as $SerieName) {
1656 if ($SerieName != $DataSerie && $SerieName != $AbscissaSerie) {
1657 unset($Data["Series"][$SerieName]);
1658 }
1659 }
1660 /* Remove null values */
1661 foreach ($Data["Series"][$DataSerie]["Data"] as $Key => $Value) {
1662 if ($Value != 0) {
1663 $NewData[] = $Value;
1664 $NewAbscissa[] = $Data["Series"][$AbscissaSerie]["Data"][$Key];
1665 if (isset($Palette[$Key])) {
1666 $NewPalette[] = $Palette[$Key];
1667 }
1668 }
1669 }
1670 $Data["Series"][$DataSerie]["Data"] = $NewData;
1671 $Data["Series"][$AbscissaSerie]["Data"] = $NewAbscissa;
1672 return [$Data, $NewPalette];
1673 }
1674 }
1675