]> git.llucax.com Git - mecon/meconlib.git/blob - lib/MECON/Graph/external/jpgraph/src/jpgraph_pie.php
2dfc407a647e5c4d04dd357d6b654889c224bdc2
[mecon/meconlib.git] / lib / MECON / Graph / external / jpgraph / src / jpgraph_pie.php
1 <?php
2 /*=======================================================================
3 // File:        JPGRAPH_PIE.PHP
4 // Description: Pie plot extension for JpGraph
5 // Created:     2001-02-14
6 // Author:      Johan Persson (johanp@aditus.nu)
7 // Ver:         $Id: jpgraph_pie.php,v 1.49.2.8 2003/08/16 00:30:21 aditus Exp $
8 //
9 // License:     This code is released under QPL
10 // Copyright (C) 2001,2002 Johan Persson
11 //========================================================================
12 */
13
14
15 // Defines for PiePlot::SetLabelType()
16 DEFINE("PIE_VALUE_ABS",1);
17 DEFINE("PIE_VALUE_PER",0);
18 DEFINE("PIE_VALUE_PERCENTAGE",0);
19 DEFINE("PIE_VALUE_ADJPERCENTAGE",2);
20 DEFINE("PIE_VALUE_ADJPER",2);
21
22 //===================================================
23 // CLASS PiePlot
24 // Description: Draws a pie plot
25 //===================================================
26 class PiePlot {
27     var $posx=0.5,$posy=0.5;
28     var $radius=0.3;
29     var $explode_radius=array(),$explode_all=false,$explode_r=20;
30     var $labels=null, $legends=null;
31     var $csimtargets=null;  // Array of targets for CSIM
32     var $csimareas='';          // Generated CSIM text  
33     var $csimalts=null;         // ALT tags for corresponding target
34     var $data=null;
35     var $title;
36     var $startangle=0;
37     var $weight=1, $color="black";
38     var $legend_margin=6,$show_labels=true;
39     var $themearr = array(
40         "earth"         => array(136,34,40,45,46,62,63,134,74,10,120,136,141,168,180,77,209,218,346,395,89,430),
41         "pastel" => array(27,415,128,59,66,79,105,110,42,147,152,230,236,240,331,337,405,38),
42         "water"  => array(8,370,24,40,335,56,213,237,268,14,326,387,10,388),
43         "sand"   => array(27,168,34,170,19,50,65,72,131,209,46,393));
44     var $theme="earth";
45     var $setslicecolors=array();
46     var $labeltype=0; // Default to percentage
47     var $pie_border=true,$pie_interior_border=true;
48     var $value;
49     var $ishadowcolor='',$ishadowdrop=4;
50     var $ilabelposadj=1;
51     var $legendcsimtargets = array();
52     var $legendcsimalts = array();
53     var $adjusted_data = array();
54         
55 //---------------
56 // CONSTRUCTOR
57     function PiePlot($data) {
58         $this->data = array_reverse($data);
59         $this->title = new Text("");
60         $this->title->SetFont(FF_FONT1,FS_BOLD);
61         $this->value = new DisplayValue();
62         $this->value->Show();
63         $this->value->SetFormat('%.1f%%');
64     }
65
66 //---------------
67 // PUBLIC METHODS       
68     function SetCenter($x,$y=0.5) {
69         $this->posx = $x;
70         $this->posy = $y;
71     }
72
73     function SetColor($aColor) {
74         $this->color = $aColor;
75     }
76         
77     function SetSliceColors($aColors) {
78         $this->setslicecolors = $aColors;
79     }
80         
81     function SetShadow($aColor='darkgray',$aDropWidth=4) {
82         $this->ishadowcolor = $aColor;
83         $this->ishadowdrop = $aDropWidth;
84     }
85
86     function SetCSIMTargets($targets,$alts=null) {
87         $this->csimtargets=array_reverse($targets);
88         if( is_array($alts) )
89             $this->csimalts=array_reverse($alts);
90     }
91         
92     function GetCSIMareas() {
93         return $this->csimareas;
94     }
95
96     function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {  
97         //Slice number, ellipse centre (x,y), height, width, start angle, end angle
98         while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
99         while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
100
101         $sa = 2*M_PI - $sa;
102         $ea = 2*M_PI - $ea;
103
104         //add coordinates of the centre to the map
105         $coords = "$xc, $yc";
106
107         //add coordinates of the first point on the arc to the map
108         $xp = floor(($radius*cos($ea))+$xc);
109         $yp = floor($yc-$radius*sin($ea));
110         $coords.= ", $xp, $yp";
111         
112         //add coordinates every 0.2 radians
113         $a=$ea+0.2;
114         while ($a<$sa) {
115             $xp = floor($radius*cos($a)+$xc);
116             $yp = floor($yc-$radius*sin($a));
117             $coords.= ", $xp, $yp";
118             $a += 0.2;
119         }
120                 
121         //Add the last point on the arc
122         $xp = floor($radius*cos($sa)+$xc);
123         $yp = floor($yc-$radius*sin($sa));
124         $coords.= ", $xp, $yp";
125         if( !empty($this->csimtargets[$i]) ) {
126             $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
127                 $this->csimtargets[$i]."\"";
128             if( !empty($this->csimalts[$i]) ) {
129                 $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
130                 $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
131             }
132             $this->csimareas .= ">\n";
133         }
134     }
135
136         
137     function SetTheme($aTheme) {
138         if( in_array($aTheme,array_keys($this->themearr)) )
139             $this->theme = $aTheme;
140         else
141             JpGraphError::Raise("PiePLot::SetTheme() Unknown theme: $aTheme");
142     }
143         
144     function ExplodeSlice($e,$radius=20) {
145         if( ! is_integer($e) ) 
146             JpGraphError::Raise('Argument to PiePlot::ExplodeSlice() must be an integer');
147         $this->explode_radius[$e]=$radius;
148     }
149
150     function ExplodeAll($radius=20) {
151         $this->explode_all=true;
152         $this->explode_r = $radius;
153     }
154
155     function Explode($aExplodeArr) {
156         if( !is_array($aExplodeArr) ) {
157             JpGraphError::Raise("Argument to PiePlot::Explode() must be an array with integer distances.");
158         }
159         $this->explode_radius = $aExplodeArr;
160     }
161
162     function SetStartAngle($aStart) {
163         if( $aStart < 0 || $aStart > 360 ) {
164             JpGraphError::Raise('Slice start angle must be between 0 and 360 degrees.');
165         }
166         $this->startangle = 360-$aStart;
167         $this->startangle *= M_PI/180;
168     }
169         
170     function SetFont($family,$style=FS_NORMAL,$size=10) {
171                 JpGraphError::Raise('PiePlot::SetFont() is deprecated. Use PiePlot->value->SetFont() instead.');
172     }
173         
174     // Size in percentage
175     function SetSize($aSize) {
176         if( ($aSize>0 && $aSize<=0.5) || ($aSize>10 && $aSize<1000) )
177             $this->radius = $aSize;
178         else
179             JpGraphError::Raise("PiePlot::SetSize() Radius for pie must either be specified as a fraction
180                                 [0, 0.5] of the size of the image or as an absolute size in pixels 
181                                 in the range [10, 1000]");
182     }
183         
184     function SetFontColor($aColor) {
185         JpGraphError::Raise('PiePlot::SetFontColor() is deprecated. Use PiePlot->value->SetColor() instead.');
186     }
187         
188     // Set label arrays
189     function SetLegends($aLegend) {
190         $this->legends = $aLegend;
191     }
192
193     // Set text labels for slices 
194     function SetLabels($aLabels,$aLblPosAdj="auto") {
195         $this->labels = array_reverse($aLabels);
196         $this->ilabelposadj=$aLblPosAdj;
197     }
198
199     function SetLabelPos($aLblPosAdj) {
200         $this->ilabelposadj=$aLblPosAdj;
201     }
202         
203     // Should we display actual value or percentage?
204     function SetLabelType($t) {
205         if( $t < 0 || $t > 2 ) 
206             JpGraphError::Raise("PiePlot::SetLabelType() Type for pie plots must be 0 or 1 (not $t).");
207         $this->labeltype=$t;
208     }
209
210     function SetValueType($aType) {
211         $this->SetLabelType($aType);
212     }
213
214     // Should the circle around a pie plot be displayed
215     function ShowBorder($exterior=true,$interior=true) {
216         $this->pie_border = $exterior;
217         $this->pie_interior_border = $interior;
218     }
219         
220     // Setup the legends
221     function Legend(&$graph) {
222         $colors = array_keys($graph->img->rgb->rgb_table);
223         sort($colors);  
224         $ta=$this->themearr[$this->theme];      
225         $n = count($this->data);
226
227         if( $this->setslicecolors==null ) {
228             $numcolors=count($ta);
229             if( get_class($this)==='pieplot3d' ) {
230                 $ta = array_reverse(array_slice($ta,0,$n));
231             }
232         }
233         else {
234             $this->setslicecolors = array_slice($this->setslicecolors,0,$n);
235             $numcolors=$n; 
236             if( $graph->pieaa && get_class($this)==='pieplot' ) { 
237                 $this->setslicecolors = array_reverse($this->setslicecolors);
238             }
239         }
240                 
241         $sum=0;
242         for($i=0; $i < $n; ++$i)
243             $sum += $this->data[$i];
244
245         // Bail out with error if the sum is 0
246         if( $sum==0 )
247             JpGraphError::Raise("Illegal pie plot. Sum of all data is zero for Pie!");
248
249         // Make sure we don't plot more values than data points
250         // (in case the user added more legends than data points)
251         $n = min(count($this->legends),count($this->data));
252         if( $this->legends != "" )
253             $this->legends = array_reverse($this->legends);
254         for( $i=$n-1; $i >= 0; --$i ) {
255             $l = $this->legends[$i];
256             // Replace possible format with actual values
257             if( $this->labeltype==0 ) {
258                 $l = sprintf($l,100*$this->data[$i]/$sum);
259                 $alt = sprintf($this->csimalts[$i],$this->data[$i]);
260
261             }
262             elseif( $this->labeltype == 1)  {
263                 $l = sprintf($l,$this->data[$i]);
264                 $alt = sprintf($this->csimalts[$i],$this->data[$i]);
265             
266             }
267             else {
268                 $l = sprintf($l,$this->adjusted_data[$i]);
269                 $alt = sprintf($this->csimalts[$i],$this->adjusted_data[$i]);
270             }
271             
272                                 
273             if( $this->setslicecolors==null ) 
274                 $graph->legend->Add($l,$colors[$ta[$i%$numcolors]],"",0,
275                                     $this->csimtargets[$i],$alt);
276             else
277                 $graph->legend->Add($l,$this->setslicecolors[$i%$numcolors],"",0,
278                                     $this->csimtargets[$i],$alt);
279         }
280     }
281         
282     // Adjust the rounded percetage value so that the sum of
283     // of the pie slices are always 100%
284     // Using the Hare/Niemeyer method
285     function AdjPercentage($aData,$aPrec=0) {
286         $mul=100;
287         if( $aPrec > 0 && $aPrec < 3 ) {
288             if( $aPrec == 1 ) 
289                 $mul=1000;
290                 else
291                     $mul=10000;
292         }
293         
294         $tmp = array();
295         $result = array();
296         $quote_sum=0;
297         $n = count($aData) ;
298         for( $i=0, $sum=0; $i < $n; ++$i )
299             $sum+=$aData[$i];
300         foreach($aData as $index => $value) {
301             $tmp_percentage=$value/$sum*$mul;
302             $result[$index]=floor($tmp_percentage);
303             $tmp[$index]=$tmp_percentage-$result[$index];
304             $quote_sum+=$result[$index];
305         }
306         if( $quote_sum == $mul) {
307             if( $mul > 100 ) {
308                 $tmp = $mul / 100;
309                 for( $i=0; $i < $n; ++$i ) {
310                     $result[$i] /= $tmp ;
311                 }
312             }
313             return $result;
314         }
315         arsort($tmp,SORT_NUMERIC);
316         reset($tmp);
317         for($i=0; $i < $mul-$quote_sum; $i++)
318         {
319             $result[key($tmp)]++;
320             next($tmp);
321         }
322         if( $mul > 100 ) {
323             $tmp = $mul / 100;
324             for( $i=0; $i < $n; ++$i ) {
325                 $result[$i] /= $tmp ;
326             }
327         }
328         return $result;
329     }
330
331
332     function Stroke(&$img,$aaoption=0) {
333         // aaoption is used to handle antialias
334         // aaoption == 0 a normal pie
335         // aaoption == 1 just the body
336         // aaoption == 2 just the values
337
338         // Explode scaling. If anti anti alias we scale the image
339         // twice and we also need to scale the exploding distance
340         $expscale = $aaoption === 1 ? 2 : 1;
341
342         if( $this->labeltype == 2 ) {
343             // Adjust the data so that it will add up to 100%
344             $this->adjusted_data = $this->AdjPercentage($this->data);
345         }
346
347         $colors = array_keys($img->rgb->rgb_table);
348         sort($colors);  
349         $ta=$this->themearr[$this->theme];      
350         $n = count($this->data);
351         
352         if( $this->setslicecolors==null ) {
353             $numcolors=count($ta);
354         }
355         else {
356             $this->setslicecolors = array_reverse(array_slice($this->setslicecolors,0,$n));
357             $numcolors=count($this->setslicecolors); 
358         }
359
360         // Draw the slices
361         $sum=0;
362         for($i=0; $i < $n; ++$i)
363             $sum += $this->data[$i];
364         
365         // Bail out with error if the sum is 0
366         if( $sum==0 )
367             JpGraphError::Raise("Sum of all data is 0 for Pie.");
368         
369         // Set up the pie-circle
370         if( $this->radius <= 1 )
371             $radius = floor($this->radius*min($img->width,$img->height));
372         else {
373             $radius = $aaoption === 1 ? $this->radius*2 : $this->radius;
374         }
375
376         if( $this->posx <= 1 && $this->posx > 0 )
377             $xc = round($this->posx*$img->width);
378         else
379             $xc = $this->posx ;
380         
381         if( $this->posy <= 1 && $this->posy > 0 )
382             $yc = round($this->posy*$img->height);
383         else
384             $yc = $this->posy ;
385                 
386         $n = count($this->data);
387
388         if( $this->explode_all )
389             for($i=0; $i < $n; ++$i)
390                 $this->explode_radius[$i]=$this->explode_r;
391
392         if( $this->ishadowcolor != "" && $aaoption !== 2) {
393             $accsum=0;
394             $angle2 = $this->startangle;
395             $img->SetColor($this->ishadowcolor);
396             for($i=0; $sum > 0 && $i < $n; ++$i) {
397                 $j = $n-$i-1;
398                 $d = $this->data[$i];
399                 $angle1 = $angle2;
400                 $accsum += $d;
401                 $angle2 = $this->startangle+2*M_PI*$accsum/$sum;
402                 if( empty($this->explode_radius[$j]) )
403                     $this->explode_radius[$j]=0;
404
405                 $la = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
406
407                 $xcm = $xc + $this->explode_radius[$j]*cos($la)*$expscale;
408                 $ycm = $yc - $this->explode_radius[$j]*sin($la)*$expscale;
409                 
410                 $xcm += $this->ishadowdrop*$expscale;
411                 $ycm += $this->ishadowdrop*$expscale;
412
413                 $img->CakeSlice($xcm,$ycm,$radius,$radius,
414                                 $angle1*180/M_PI,$angle2*180/M_PI,$this->ishadowcolor);
415                 
416             }
417         }
418
419         $accsum=0;
420         $angle2 = $this->startangle;
421         $img->SetColor($this->color);
422         for($i=0; $sum>0 && $i < $n; ++$i) {
423             $j = $n-$i-1;
424             if( empty($this->explode_radius[$j]) )
425                 $this->explode_radius[$j]=0;
426             $d = $this->data[$i];
427             $angle1 = $angle2;
428             $accsum += $d;
429             $angle2 = $this->startangle+2*M_PI*$accsum/$sum;
430             $this->la[$i] = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
431
432             if( $d == 0 ) continue;
433
434             if( $this->setslicecolors==null )
435                 $slicecolor=$colors[$ta[$i%$numcolors]];
436             else
437                 $slicecolor=$this->setslicecolors[$i%$numcolors];
438
439             if( $this->pie_interior_border && $aaoption===0 )
440                 $img->SetColor($this->color);
441             else
442                 $img->SetColor($slicecolor);
443
444             $arccolor = $this->pie_border && $aaoption===0 ? $this->color : "";
445
446             $xcm = $xc + $this->explode_radius[$j]*cos($this->la[$i])*$expscale;
447             $ycm = $yc - $this->explode_radius[$j]*sin($this->la[$i])*$expscale;
448
449             if( $aaoption !== 2 ) {
450                 $img->CakeSlice($xcm,$ycm,$radius-1,$radius-1,
451                                 $angle1*180/M_PI,$angle2*180/M_PI,$slicecolor,$arccolor);
452             }
453
454             if( $this->csimtargets && $aaoption !== 1 ) 
455                 $this->AddSliceToCSIM($i,$xcm,$ycm,$radius,$angle1,$angle2);
456         }
457
458         // Format the titles for each slice
459         for( $i=0; $i < $n; ++$i) {
460             if( $this->labeltype==0 ) {
461                 if( $sum != 0 )
462                     $l = 100.0*$this->data[$i]/$sum;
463                 else
464                     $l = 0.0;
465             }
466             elseif( $this->labeltype==1 ) {
467                 $l = $this->data[$i]*1.0;
468             }
469             else {
470                 $l = $this->adjusted_data[$i];
471             }
472             if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
473                 $this->labels[$i]=sprintf($this->labels[$i],$l);
474             else
475                 $this->labels[$i]=$l;
476         }
477
478         if( $this->value->show && $aaoption !== 1 ) {
479             $this->StrokeAllLabels($img,$xc,$yc,$radius);
480         }
481
482         // Adjust title position
483         if( $aaoption !== 1 ) {
484             $this->title->Pos($xc,
485                           $yc-$this->title->GetFontHeight($img)-$radius-$this->title->margin,
486                           "center","bottom");
487             $this->title->Stroke($img);
488         }
489
490     }
491
492 //---------------
493 // PRIVATE METHODS      
494
495     function StrokeAllLabels($img,$xc,$yc,$radius) {
496         $n = count($this->labels);
497         for($i=0; $i < $n; ++$i) {
498             $this->StrokeLabel($this->labels[$i],$img,$xc,$yc,
499                                $this->la[$i],
500                                $radius + $this->explode_radius[$n-$i-1]); 
501         }
502     }
503
504     // Position the labels of each slice
505     function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
506
507         // Default value
508         if( $this->ilabelposadj === 'auto' )
509             $this->ilabelposadj = 0.65;
510
511         // We position the values diferently depending on if they are inside
512         // or outside the pie
513         if( $this->ilabelposadj < 1.0 ) {
514
515             $this->value->SetAlign('center','center');
516             $this->value->margin = 0;
517             
518             $xt=round($this->ilabelposadj*$r*cos($a)+$xc);
519             $yt=round($yc-$this->ilabelposadj*$r*sin($a));
520             
521             $this->value->Stroke($img,$label,$xt,$yt);
522         }
523         else {
524
525             $this->value->halign = "left";
526             $this->value->valign = "top";
527             $this->value->margin = 0;
528             
529             
530             // Position the axis title. 
531             // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
532             // that intersects with the extension of the corresponding axis. The code looks a little
533             // bit messy but this is really the only way of having a reasonable position of the
534             // axis titles.
535             $img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
536             $h=$img->GetTextHeight($label);
537             // For numeric values the format of the display value
538             // must be taken into account
539             if( is_numeric($label) ) {
540                 if( $label > 0 )
541                     $w=$img->GetTextWidth(sprintf($this->value->format,$label));
542                 else
543                     $w=$img->GetTextWidth(sprintf($this->value->negformat,$label));
544             }
545             else
546                 $w=$img->GetTextWidth($label);
547
548             if( $this->ilabelposadj > 1.0 && $this->ilabelposadj < 5.0) {
549                 $r *= $this->ilabelposadj;
550             }
551
552             $r += $img->GetFontHeight()/1.5;
553             $xt=round($r*cos($a)+$xc);
554             $yt=round($yc-$r*sin($a));
555
556             while( $a < 0 ) $a += 2*M_PI;
557             while( $a > 2*M_PI ) $a -= 2*M_PI;
558
559             if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
560             if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI; 
561             if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
562             if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
563             
564             if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
565             if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
566             if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
567             if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
568             if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
569
570             $this->value->Stroke($img,$label,$xt-$dx*$w,$yt-$dy*$h);
571         }
572     }   
573 } // Class
574
575
576 //===================================================
577 // CLASS PiePlotC
578 // Description: Same as a normal pie plot but with a 
579 // filled circle in the center
580 //===================================================
581 class PiePlotC extends PiePlot {
582     var $imidsize=0.5;          // Fraction of total width
583     var $imidcolor='white';
584     var $midtitle='';
585     var $middlecsimtarget="",$middlecsimalt="";
586
587     function PiePlotC($data,$aCenterTitle='') {
588         parent::PiePlot($data);
589         $this->midtitle = new Text();
590         $this->midtitle->ParagraphAlign('center');
591     }
592
593     function SetMid($aTitle,$aColor='white',$aSize=0.5) {
594         $this->midtitle->Set($aTitle);
595         $this->imidsize = $aSize ; 
596         $this->imidcolor = $aColor ; 
597     }
598
599     function SetMidTitle($aTitle) {
600         $this->midtitle->Set($aTitle);
601     }
602
603     function SetMidSize($aSize) {
604         $this->imidsize = $aSize ; 
605     }
606
607     function SetMidColor($aColor) {
608         $this->imidcolor = $aColor ; 
609     }
610
611     function SetMidCSIM($aTarget,$aAlt) {
612         $this->middlecsimtarget = $aTarget;
613         $this->middlecsimalt = $aAlt;
614     }
615
616     function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {  
617         //Slice number, ellipse centre (x,y), radius, start angle, end angle
618         while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
619         while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
620
621         $sa = 2*M_PI - $sa;
622         $ea = 2*M_PI - $ea;
623
624         // Add inner circle first point
625         $xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
626         $yp = floor($yc-($this->imidsize*$radius*sin($ea)));
627         $coords = "$xp, $yp";
628         
629         //add coordinates every 0.25 radians
630         $a=$ea+0.25;
631         while ($a < $sa) {
632             $xp = floor(($this->imidsize*$radius*cos($a)+$xc));
633             $yp = floor($yc-($this->imidsize*$radius*sin($a)));
634             $coords.= ", $xp, $yp";
635             $a += 0.25;
636         }
637
638         // Make sure we end at the last point
639         $xp = floor(($this->imidsize*$radius*cos($sa)+$xc));
640         $yp = floor($yc-($this->imidsize*$radius*sin($sa)));
641         $coords.= ", $xp, $yp";
642
643         // Straight line to outer circle
644         $xp = floor($radius*cos($sa)+$xc);
645         $yp = floor($yc-$radius*sin($sa));
646         $coords.= ", $xp, $yp"; 
647
648         //add coordinates every 0.25 radians
649         $a=$sa - 0.25;
650         while ($a > $ea) {
651             $xp = floor($radius*cos($a)+$xc);
652             $yp = floor($yc-$radius*sin($a));
653             $coords.= ", $xp, $yp";
654             $a -= 0.25;
655         }
656                 
657         //Add the last point on the arc
658         $xp = floor($radius*cos($ea)+$xc);
659         $yp = floor($yc-$radius*sin($ea));
660         $coords.= ", $xp, $yp";
661
662         // Close the arc
663         $xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
664         $yp = floor($yc-($this->imidsize*$radius*sin($ea)));
665         $coords .= ", $xp, $yp";
666
667         if( !empty($this->csimtargets[$i]) ) {
668             $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
669                 $this->csimtargets[$i]."\"";
670             if( !empty($this->csimalts[$i]) ) {
671                 $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
672                 $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
673             }
674             $this->csimareas .= ">\n";
675         }
676     }
677
678
679     function Stroke($img,$aaoption=0) {
680
681         // Stroke the pie but don't stroke values
682         $tmp =  $this->value->show;
683         $this->value->show = false;
684         parent::Stroke($img,$aaoption);
685         $this->value->show = $tmp;
686
687         $xc = round($this->posx*$img->width);
688         $yc = round($this->posy*$img->height);
689
690         $radius = floor($this->radius * min($img->width,$img->height)) ;
691
692
693         if( $this->imidsize > 0 && $aaoption !== 2 ) {
694
695             if( $this->ishadowcolor != "" ) {
696                 $img->SetColor($this->ishadowcolor);
697                 $img->FilledCircle($xc+$this->ishadowdrop,$yc+$this->ishadowdrop,
698                                    round($radius*$this->imidsize));
699             }
700
701             $img->SetColor($this->imidcolor);
702             $img->FilledCircle($xc,$yc,round($radius*$this->imidsize));
703
704             if(  $this->pie_border && $aaoption === 0 ) {
705                 $img->SetColor($this->color);
706                 $img->Circle($xc,$yc,round($radius*$this->imidsize));
707             }
708
709             if( !empty($this->middlecsimtarget) )
710                 $this->AddMiddleCSIM($xc,$yc,round($radius*$this->imidsize));
711
712         }
713
714         if( $this->value->show && $aaoption !== 1) {
715             $this->StrokeAllLabels($img,$xc,$yc,$radius);
716             $this->midtitle->Pos($xc,$yc,'center','center');
717             $this->midtitle->Stroke($img);
718         }
719
720     }
721
722     function AddMiddleCSIM($xc,$yc,$r) {
723         $this->csimareas .= "<area shape=\"circle\" coords=\"$xc,$yc,$r\" href=\"".
724             $this->middlecsimtarget."\"";
725         if( !empty($this->middlecsimalt) ) {
726             $tmp = $this->middlecsimalt;
727             $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
728         }
729         $this->csimareas .= ">\n";
730     }
731
732     function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
733
734         if( $this->ilabelposadj === 'auto' )
735             $this->ilabelposadj = (1-$this->imidsize)/2+$this->imidsize;
736
737         parent::StrokeLabel($label,$img,$xc,$yc,$a,$r);
738
739     }
740
741 }
742
743
744 //===================================================
745 // CLASS PieGraph
746 // Description: 
747 //===================================================
748 class PieGraph extends Graph {
749     var $posx, $posy, $radius;          
750     var $legends=array();       
751     var $plots=array();
752     var $pieaa = false ;
753 //---------------
754 // CONSTRUCTOR
755     function PieGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
756         $this->Graph($width,$height,$cachedName,$timeout,$inline);
757         $this->posx=$width/2;
758         $this->posy=$height/2;
759         $this->SetColor(array(255,255,255));            
760     }
761
762 //---------------
763 // PUBLIC METHODS       
764     function Add($aObj) {
765
766         if( is_array($aObj) && count($aObj) > 0 )
767             $cl = get_class($aObj[0]);
768         else
769             $cl = get_class($aObj);
770
771         if( $cl == 'text' ) 
772             $this->AddText($aObj);
773         else
774             $this->plots[] = $aObj;
775     }
776
777     function SetAntiAliasing($aFlg=true) {
778         $this->pieaa = $aFlg;
779     }
780         
781     function SetColor($c) {
782         $this->SetMarginColor($c);
783     }
784
785
786     function DisplayCSIMAreas() {
787             $csim="";
788             foreach($this->plots as $p ) {
789                 $csim .= $p->GetCSIMareas();
790             }
791             //$csim.= $this->legend->GetCSIMareas();
792             if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
793                 $this->img->SetColor($this->csimcolor);
794                 for ($i=0; $i<count($coords[0]); $i++) {
795                     if ($coords[1][$i]=="poly") {
796                         preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
797                         $this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
798                         for ($j=0; $j<count($pts[0]); $j++) {
799                             $this->img->LineTo($pts[1][$j],$pts[2][$j]);
800                         }
801                     } else if ($coords[1][$i]=="rect") {
802                         $pts = preg_split('/,/', $coords[2][$i]);
803                         $this->img->SetStartPoint($pts[0],$pts[1]);
804                         $this->img->LineTo($pts[2],$pts[1]);
805                         $this->img->LineTo($pts[2],$pts[3]);
806                         $this->img->LineTo($pts[0],$pts[3]);
807                         $this->img->LineTo($pts[0],$pts[1]);
808                                                 
809                     }
810                 }
811             }
812     }
813
814     // Method description
815     function Stroke($aStrokeFileName="") {
816         // If the filename is the predefined value = '_csim_special_'
817         // we assume that the call to stroke only needs to do enough
818         // to correctly generate the CSIM maps.
819         // We use this variable to skip things we don't strictly need
820         // to do to generate the image map to improve performance
821         // a best we can. Therefor you will see a lot of tests !$_csim in the
822         // code below.
823         $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
824
825         // We need to know if we have stroked the plot in the
826         // GetCSIMareas. Otherwise the CSIM hasn't been generated
827         // and in the case of GetCSIM called before stroke to generate
828         // CSIM without storing an image to disk GetCSIM must call Stroke.
829         $this->iHasStroked = true;
830
831                 
832                 
833         $n = count($this->plots);
834
835         if( $this->pieaa ) {
836
837             if( !$_csim ) {
838                 if( $this->background_image != "" ) {
839                     $this->StrokeFrameBackground();             
840                 }
841                 else {
842                     $this->StrokeFrame();               
843                 }
844             }
845
846
847             $w = $this->img->width;
848             $h = $this->img->height;
849             $oldimg = $this->img->img;
850
851             $this->img->CreateImgCanvas(2*$w,2*$h);
852             
853             $this->img->SetColor( $this->margin_color );
854             $this->img->FilledRectangle(0,0,2*$w-1,2*$h-1);
855
856             for($i=0; $i < $n; ++$i) {
857                 if( $this->plots[$i]->posx > 1 ) 
858                     $this->plots[$i]->posx *= 2 ;
859                 if( $this->plots[$i]->posy > 1 ) 
860                     $this->plots[$i]->posy *= 2 ;
861
862                 $this->plots[$i]->Stroke($this->img,1);
863
864                 if( $this->plots[$i]->posx > 1 ) 
865                     $this->plots[$i]->posx /= 2 ;
866                 if( $this->plots[$i]->posy > 1 ) 
867                     $this->plots[$i]->posy /= 2 ;
868             }
869
870             $indent = $this->doframe ? ($this->frame_weight + ($this->doshadow ? $this->shadow_width : 0 )) : 0 ;
871             $indent += $this->framebevel ? $this->framebeveldepth + 1 : 0 ;
872             $this->img->CopyCanvasH($oldimg,$this->img->img,$indent,$indent,$indent,$indent,
873                                     $w-2*$indent,$h-2*$indent,2*($w-$indent),2*($h-$indent));
874
875             $this->img->img = $oldimg ;
876             $this->img->width = $w ;
877             $this->img->height = $h ;
878
879             for($i=0; $i < $n; ++$i) {
880                 $this->plots[$i]->Stroke($this->img,2);
881                 $this->plots[$i]->Legend($this);
882             }
883
884         }
885         else {
886
887             if( !$_csim ) {
888                 if( $this->background_image != "" ) {
889                     $this->StrokeFrameBackground();             
890                 }
891                 else {
892                     $this->StrokeFrame();               
893                 }
894             }
895             
896             for($i=0; $i < $n; ++$i) {
897                 $this->plots[$i]->Stroke($this->img);
898                 $this->plots[$i]->Legend($this);
899             }
900         }
901
902
903         $this->legend->Stroke($this->img);
904         $this->footer->Stroke($this->img);
905
906         if( !$_csim ) { 
907             $this->StrokeTitles();
908
909             // Stroke texts
910             if( $this->texts != null ) {
911                 $n = count($this->texts);
912                 for($i=0; $i < $n; ++$i ) {
913                     $this->texts[$i]->Stroke($this->img);
914                 }
915             }
916
917             if( JPG_DEBUG ) {
918                 $this->DisplayCSIMAreas();
919             }
920             // If the filename is given as the special "__handle"
921             // then the image handler is returned and the image is NOT
922             // streamed back
923             if( $aStrokeFileName == _IMG_HANDLER ) {
924                 return $this->img->img;
925             }
926             else {
927                 // Finally stream the generated picture                                 
928                 $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
929                                            $aStrokeFileName);           
930             }
931         }
932     }
933 } // Class
934
935 /* EOF */
936 ?>