Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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