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