Subversion Repositories Applications.annuaire

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
66 aurelien 1
<?php
2
/*=======================================================================
3
 // File:        JPGRAPH_PIE.PHP
4
 // Description: Pie plot extension for JpGraph
5
 // Created:     2001-02-14
6
 // Ver:         $Id: jpgraph_pie.php 1926 2010-01-11 16:33:07Z ljp $
7
 //
8
 // Copyright (c) Aditus Consulting. All rights reserved.
9
 //========================================================================
10
 */
11
 
12
 
13
// Defines for PiePlot::SetLabelType()
14
define("PIE_VALUE_ABS",1);
15
define("PIE_VALUE_PER",0);
16
define("PIE_VALUE_PERCENTAGE",0);
17
define("PIE_VALUE_ADJPERCENTAGE",2);
18
define("PIE_VALUE_ADJPER",2);
19
 
20
//===================================================
21
// CLASS PiePlot
22
// Description: Draws a pie plot
23
//===================================================
24
class PiePlot {
25
    public $posx=0.5,$posy=0.5;
26
    protected $radius=0.3;
27
    protected $explode_radius=array(),$explode_all=false,$explode_r=20;
28
    protected $labels=null, $legends=null;
29
    protected $csimtargets=null,$csimwintargets=null;  // Array of targets for CSIM
30
    protected $csimareas='';  // Generated CSIM text
31
    protected $csimalts=null;  // ALT tags for corresponding target
32
    protected $data=null;
33
    public $title;
34
    protected $startangle=0;
35
    protected $weight=1, $color="black";
36
    protected $legend_margin=6,$show_labels=true;
37
    protected $themearr = array(
38
 "earth"  => array(136,34,40,45,46,62,63,134,74,10,120,136,141,168,180,77,209,218,346,395,89,430),
39
 "pastel" => array(27,415,128,59,66,79,105,110,42,147,152,230,236,240,331,337,405,38),
40
 "water"  => array(8,370,24,40,335,56,213,237,268,14,326,387,10,388),
41
 "sand"   => array(27,168,34,170,19,50,65,72,131,209,46,393));
42
    protected $theme="earth";
43
    protected $setslicecolors=array();
44
    protected $labeltype=0; // Default to percentage
45
    protected $pie_border=true,$pie_interior_border=true;
46
    public $value;
47
    protected $ishadowcolor='',$ishadowdrop=4;
48
    protected $ilabelposadj=1;
49
    protected $legendcsimtargets = array(),$legendcsimwintargets = array();
50
    protected $legendcsimalts = array();
51
    protected $adjusted_data = array();
52
    public $guideline = null;
53
    protected $guidelinemargin=10,$iShowGuideLineForSingle = false;
54
    protected $iGuideLineCurve = false,$iGuideVFactor=1.4,$iGuideLineRFactor=0.8;
55
    protected $la = array(); // Holds the exact angle for each label
56
 
57
    //---------------
58
    // CONSTRUCTOR
59
    function __construct($data) {
60
        $this->data = array_reverse($data);
61
        $this->title = new Text("");
62
        $this->title->SetFont(FF_FONT1,FS_BOLD);
63
        $this->value = new DisplayValue();
64
        $this->value->Show();
65
        $this->value->SetFormat('%.1f%%');
66
        $this->guideline = new LineProperty();
67
    }
68
 
69
    //---------------
70
    // PUBLIC METHODS
71
    function SetCenter($x,$y=0.5) {
72
        $this->posx = $x;
73
        $this->posy = $y;
74
    }
75
 
76
    // Enable guideline and set drwaing policy
77
    function SetGuideLines($aFlg=true,$aCurved=true,$aAlways=false) {
78
        $this->guideline->Show($aFlg);
79
        $this->iShowGuideLineForSingle = $aAlways;
80
        $this->iGuideLineCurve = $aCurved;
81
    }
82
 
83
    // Adjuste the distance between labels and labels and pie
84
    function SetGuideLinesAdjust($aVFactor,$aRFactor=0.8) {
85
        $this->iGuideVFactor=$aVFactor;
86
        $this->iGuideLineRFactor=$aRFactor;
87
    }
88
 
89
    function SetColor($aColor) {
90
        $this->color = $aColor;
91
    }
92
 
93
    function SetSliceColors($aColors) {
94
        $this->setslicecolors = $aColors;
95
    }
96
 
97
    function SetShadow($aColor='darkgray',$aDropWidth=4) {
98
        $this->ishadowcolor = $aColor;
99
        $this->ishadowdrop = $aDropWidth;
100
    }
101
 
102
    function SetCSIMTargets($aTargets,$aAlts='',$aWinTargets='') {
103
        $this->csimtargets=array_reverse($aTargets);
104
        if( is_array($aWinTargets) )
105
        $this->csimwintargets=array_reverse($aWinTargets);
106
        if( is_array($aAlts) )
107
        $this->csimalts=array_reverse($aAlts);
108
    }
109
 
110
    function GetCSIMareas() {
111
        return $this->csimareas;
112
    }
113
 
114
    function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {
115
        //Slice number, ellipse centre (x,y), height, width, start angle, end angle
116
        while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
117
        while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
118
 
119
        $sa = 2*M_PI - $sa;
120
        $ea = 2*M_PI - $ea;
121
 
122
        // Special case when we have only one slice since then both start and end
123
        // angle will be == 0
124
        if( abs($sa - $ea) < 0.0001 ) {
125
            $sa=2*M_PI; $ea=0;
126
        }
127
 
128
        //add coordinates of the centre to the map
129
        $xc = floor($xc);$yc=floor($yc);
130
        $coords = "$xc, $yc";
131
 
132
        //add coordinates of the first point on the arc to the map
133
        $xp = floor(($radius*cos($ea))+$xc);
134
        $yp = floor($yc-$radius*sin($ea));
135
        $coords.= ", $xp, $yp";
136
 
137
        //add coordinates every 0.2 radians
138
        $a=$ea+0.2;
139
 
140
        // If we cross the 360-limit with a slice we need to handle
141
        // the fact that end angle is smaller than start
142
        if( $sa < $ea ) {
143
            while ($a <= 2*M_PI) {
144
                $xp = floor($radius*cos($a)+$xc);
145
                $yp = floor($yc-$radius*sin($a));
146
                $coords.= ", $xp, $yp";
147
                $a += 0.2;
148
            }
149
            $a -= 2*M_PI;
150
        }
151
 
152
 
153
        while ($a < $sa) {
154
            $xp = floor($radius*cos($a)+$xc);
155
            $yp = floor($yc-$radius*sin($a));
156
            $coords.= ", $xp, $yp";
157
            $a += 0.2;
158
        }
159
 
160
        //Add the last point on the arc
161
        $xp = floor($radius*cos($sa)+$xc);
162
        $yp = floor($yc-$radius*sin($sa));
163
        $coords.= ", $xp, $yp";
164
        if( !empty($this->csimtargets[$i]) ) {
165
            $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtargets[$i]."\"";
166
            $tmp="";
167
            if( !empty($this->csimwintargets[$i]) ) {
168
                $this->csimareas .= " target=\"".$this->csimwintargets[$i]."\" ";
169
            }
170
            if( !empty($this->csimalts[$i]) ) {
171
                $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
172
                $this->csimareas .= " title=\"$tmp\" alt=\"$tmp\" ";
173
            }
174
            $this->csimareas .= " />\n";
175
        }
176
    }
177
 
178
 
179
    function SetTheme($aTheme) {
180
        if( in_array($aTheme,array_keys($this->themearr)) )
181
        $this->theme = $aTheme;
182
        else
183
        JpGraphError::RaiseL(15001,$aTheme);//("PiePLot::SetTheme() Unknown theme: $aTheme");
184
    }
185
 
186
    function ExplodeSlice($e,$radius=20) {
187
        if( ! is_integer($e) )
188
        JpGraphError::RaiseL(15002);//('Argument to PiePlot::ExplodeSlice() must be an integer');
189
        $this->explode_radius[$e]=$radius;
190
    }
191
 
192
    function ExplodeAll($radius=20) {
193
        $this->explode_all=true;
194
        $this->explode_r = $radius;
195
    }
196
 
197
    function Explode($aExplodeArr) {
198
        if( !is_array($aExplodeArr) ) {
199
            JpGraphError::RaiseL(15003);
200
            //("Argument to PiePlot::Explode() must be an array with integer distances.");
201
        }
202
        $this->explode_radius = $aExplodeArr;
203
    }
204
 
205
    function SetStartAngle($aStart) {
206
        if( $aStart < 0 || $aStart > 360 ) {
207
            JpGraphError::RaiseL(15004);//('Slice start angle must be between 0 and 360 degrees.');
208
        }
209
        if( $aStart == 0 ) {
210
            $this->startangle = 0;
211
        }
212
        else {
213
            $this->startangle = 360-$aStart;
214
            $this->startangle *= M_PI/180;
215
        }
216
    }
217
 
218
    // Size in percentage
219
    function SetSize($aSize) {
220
        if( ($aSize>0 && $aSize<=0.5) || ($aSize>10 && $aSize<1000) )
221
        $this->radius = $aSize;
222
        else
223
        JpGraphError::RaiseL(15006);
224
        //("PiePlot::SetSize() Radius for pie must either be specified as a fraction [0, 0.5] of the size of the image or as an absolute size in pixels  in the range [10, 1000]");
225
    }
226
 
227
    // Set label arrays
228
    function SetLegends($aLegend) {
229
        $this->legends = $aLegend;
230
    }
231
 
232
    // Set text labels for slices
233
    function SetLabels($aLabels,$aLblPosAdj="auto") {
234
        $this->labels = array_reverse($aLabels);
235
        $this->ilabelposadj=$aLblPosAdj;
236
    }
237
 
238
    function SetLabelPos($aLblPosAdj) {
239
        $this->ilabelposadj=$aLblPosAdj;
240
    }
241
 
242
    // Should we display actual value or percentage?
243
    function SetLabelType($aType) {
244
        if( $aType < 0 || $aType > 2 )
245
        	JpGraphError::RaiseL(15008,$aType);
246
	        //("PiePlot::SetLabelType() Type for pie plots must be 0 or 1 (not $t).");
247
        $this->labeltype = $aType;
248
    }
249
 
250
    // Deprecated.
251
    function SetValueType($aType) {
252
        $this->SetLabelType($aType);
253
    }
254
 
255
    // Should the circle around a pie plot be displayed
256
    function ShowBorder($exterior=true,$interior=true) {
257
        $this->pie_border = $exterior;
258
        $this->pie_interior_border = $interior;
259
    }
260
 
261
    // Setup the legends
262
    function Legend($graph) {
263
        $colors = array_keys($graph->img->rgb->rgb_table);
264
        sort($colors);
265
        $ta=$this->themearr[$this->theme];
266
        $n = count($this->data);
267
 
268
        if( $this->setslicecolors==null ) {
269
            $numcolors=count($ta);
270
            if( class_exists('PiePlot3D',false) && ($this instanceof PiePlot3D) ) {
271
                $ta = array_reverse(array_slice($ta,0,$n));
272
            }
273
        }
274
        else {
275
            $this->setslicecolors = array_slice($this->setslicecolors,0,$n);
276
            $numcolors=count($this->setslicecolors);
277
            if( $graph->pieaa && !($this instanceof PiePlot3D) ) {
278
                $this->setslicecolors = array_reverse($this->setslicecolors);
279
            }
280
        }
281
 
282
        $sum=0;
283
        for($i=0; $i < $n; ++$i)
284
            $sum += $this->data[$i];
285
 
286
        // Bail out with error if the sum is 0
287
        if( $sum==0 )
288
        JpGraphError::RaiseL(15009);//("Illegal pie plot. Sum of all data is zero for Pie!");
289
 
290
        // Make sure we don't plot more values than data points
291
        // (in case the user added more legends than data points)
292
        $n = min(count($this->legends),count($this->data));
293
        if( $this->legends != "" ) {
294
            $this->legends = array_reverse(array_slice($this->legends,0,$n));
295
        }
296
        for( $i=$n-1; $i >= 0; --$i ) {
297
            $l = $this->legends[$i];
298
            // Replace possible format with actual values
299
            if( count($this->csimalts) > $i ) {
300
                $fmt = $this->csimalts[$i];
301
            }
302
            else {
303
                $fmt = "%d"; // Deafult Alt if no other has been specified
304
            }
305
            if( $this->labeltype==0 ) {
306
                $l = sprintf($l,100*$this->data[$i]/$sum);
307
                $alt = sprintf($fmt,$this->data[$i]);
308
 
309
            }
310
            elseif( $this->labeltype == 1)  {
311
                $l = sprintf($l,$this->data[$i]);
312
                $alt = sprintf($fmt,$this->data[$i]);
313
 
314
            }
315
            else {
316
                $l = sprintf($l,$this->adjusted_data[$i]);
317
                $alt = sprintf($fmt,$this->adjusted_data[$i]);
318
            }
319
 
320
            if( empty($this->csimwintargets[$i]) ) {
321
                $wintarg = '';
322
            }
323
            else {
324
                $wintarg = $this->csimwintargets[$i];
325
            }
326
 
327
            if( $this->setslicecolors==null ) {
328
                $graph->legend->Add($l,$colors[$ta[$i%$numcolors]],"",0,$this->csimtargets[$i],$alt,$wintarg);
329
            }
330
            else {
331
                $graph->legend->Add($l,$this->setslicecolors[$i%$numcolors],"",0,$this->csimtargets[$i],$alt,$wintarg);
332
            }
333
        }
334
    }
335
 
336
    // Adjust the rounded percetage value so that the sum of
337
    // of the pie slices are always 100%
338
    // Using the Hare/Niemeyer method
339
    function AdjPercentage($aData,$aPrec=0) {
340
        $mul=100;
341
        if( $aPrec > 0 && $aPrec < 3 ) {
342
            if( $aPrec == 1 )
343
            $mul=1000;
344
            else
345
            $mul=10000;
346
        }
347
 
348
        $tmp = array();
349
        $result = array();
350
        $quote_sum=0;
351
        $n = count($aData) ;
352
        for( $i=0, $sum=0; $i < $n; ++$i )
353
        $sum+=$aData[$i];
354
        foreach($aData as $index => $value) {
355
            $tmp_percentage=$value/$sum*$mul;
356
            $result[$index]=floor($tmp_percentage);
357
            $tmp[$index]=$tmp_percentage-$result[$index];
358
            $quote_sum+=$result[$index];
359
        }
360
        if( $quote_sum == $mul) {
361
            if( $mul > 100 ) {
362
                $tmp = $mul / 100;
363
                for( $i=0; $i < $n; ++$i ) {
364
                    $result[$i] /= $tmp ;
365
                }
366
            }
367
            return $result;
368
        }
369
        arsort($tmp,SORT_NUMERIC);
370
        reset($tmp);
371
        for($i=0; $i < $mul-$quote_sum; $i++)
372
        {
373
            $result[key($tmp)]++;
374
            next($tmp);
375
        }
376
        if( $mul > 100 ) {
377
            $tmp = $mul / 100;
378
            for( $i=0; $i < $n; ++$i ) {
379
                $result[$i] /= $tmp ;
380
            }
381
        }
382
        return $result;
383
    }
384
 
385
 
386
    function Stroke($img,$aaoption=0) {
387
        // aaoption is used to handle antialias
388
        // aaoption == 0 a normal pie
389
        // aaoption == 1 just the body
390
        // aaoption == 2 just the values
391
 
392
        // Explode scaling. If anti alias we scale the image
393
        // twice and we also need to scale the exploding distance
394
        $expscale = $aaoption === 1 ? 2 : 1;
395
 
396
        if( $this->labeltype == 2 ) {
397
            // Adjust the data so that it will add up to 100%
398
            $this->adjusted_data = $this->AdjPercentage($this->data);
399
        }
400
 
401
        $colors = array_keys($img->rgb->rgb_table);
402
        sort($colors);
403
        $ta=$this->themearr[$this->theme];
404
        $n = count($this->data);
405
 
406
        if( $this->setslicecolors==null ) {
407
            $numcolors=count($ta);
408
        }
409
        else {
410
            // We need to create an array of colors as long as the data
411
            // since we need to reverse it to get the colors in the right order
412
            $numcolors=count($this->setslicecolors);
413
            $i = 2*$numcolors;
414
            while( $n > $i ) {
415
                $this->setslicecolors = array_merge($this->setslicecolors,$this->setslicecolors);
416
                $i += $n;
417
            }
418
            $tt = array_slice($this->setslicecolors,0,$n % $numcolors);
419
            $this->setslicecolors = array_merge($this->setslicecolors,$tt);
420
            $this->setslicecolors = array_reverse($this->setslicecolors);
421
        }
422
 
423
        // Draw the slices
424
        $sum=0;
425
        for($i=0; $i < $n; ++$i)
426
            $sum += $this->data[$i];
427
 
428
        // Bail out with error if the sum is 0
429
        if( $sum==0 ) {
430
            JpGraphError::RaiseL(15009);//("Sum of all data is 0 for Pie.");
431
        }
432
 
433
        // Set up the pie-circle
434
        if( $this->radius <= 1 ) {
435
            $radius = floor($this->radius*min($img->width,$img->height));
436
        }
437
        else {
438
            $radius = $aaoption === 1 ? $this->radius*2 : $this->radius;
439
        }
440
 
441
        if( $this->posx <= 1 && $this->posx > 0 ) {
442
            $xc = round($this->posx*$img->width);
443
        }
444
        else {
445
            $xc = $this->posx ;
446
        }
447
 
448
        if( $this->posy <= 1 && $this->posy > 0 ) {
449
            $yc = round($this->posy*$img->height);
450
        }
451
        else {
452
            $yc = $this->posy ;
453
        }
454
 
455
        $n = count($this->data);
456
 
457
        if( $this->explode_all ) {
458
            for($i=0; $i < $n; ++$i) {
459
                $this->explode_radius[$i]=$this->explode_r;
460
            }
461
        }
462
 
463
        // If we have a shadow and not just drawing the labels
464
        if( $this->ishadowcolor != "" && $aaoption !== 2) {
465
            $accsum=0;
466
            $angle2 = $this->startangle;
467
            $img->SetColor($this->ishadowcolor);
468
            for($i=0; $sum > 0 && $i < $n; ++$i) {
469
                $j = $n-$i-1;
470
                $d = $this->data[$i];
471
                $angle1 = $angle2;
472
                $accsum += $d;
473
                $angle2 = $this->startangle+2*M_PI*$accsum/$sum;
474
                if( empty($this->explode_radius[$j]) ) {
475
                    $this->explode_radius[$j]=0;
476
                }
477
 
478
                if( $d < 0.00001 ) continue;
479
 
480
                $la = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
481
 
482
                $xcm = $xc + $this->explode_radius[$j]*cos($la)*$expscale;
483
                $ycm = $yc - $this->explode_radius[$j]*sin($la)*$expscale;
484
 
485
                $xcm += $this->ishadowdrop*$expscale;
486
                $ycm += $this->ishadowdrop*$expscale;
487
 
488
                $_sa = round($angle1*180/M_PI);
489
                $_ea = round($angle2*180/M_PI);
490
 
491
                // The CakeSlice method draws a full circle in case of start angle = end angle
492
                // for pie slices we don't want this behaviour unless we only have one
493
                // slice in the pie in case it is the wanted behaviour
494
                if( $_ea-$_sa > 0.1 || $n==1 ) {
495
                    $img->CakeSlice($xcm,$ycm,$radius-1,$radius-1,
496
                    $angle1*180/M_PI,$angle2*180/M_PI,$this->ishadowcolor);
497
                }
498
            }
499
        }
500
 
501
        //--------------------------------------------------------------------------------
502
        // This is the main loop to draw each cake slice
503
        //--------------------------------------------------------------------------------
504
 
505
        // Set up the accumulated sum, start angle for first slice and border color
506
        $accsum=0;
507
        $angle2 = $this->startangle;
508
        $img->SetColor($this->color);
509
 
510
        // Loop though all the slices if there is a pie to draw (sum>0)
511
        // There are n slices in total
512
        for($i=0; $sum>0 && $i < $n; ++$i) {
513
 
514
            // $j is the actual index used for the slice
515
            $j = $n-$i-1;
516
 
517
            // Make sure we havea  valid distance to explode the slice
518
            if( empty($this->explode_radius[$j]) ) {
519
                $this->explode_radius[$j]=0;
520
            }
521
 
522
            // The actual numeric value for the slice
523
            $d = $this->data[$i];
524
 
525
            $angle1 = $angle2;
526
 
527
            // Accumlate the sum
528
            $accsum += $d;
529
 
530
            // The new angle when we add the "size" of this slice
531
            // angle1 is then the start and angle2 the end of this slice
532
            $angle2 = $this->NormAngle($this->startangle+2*M_PI*$accsum/$sum);
533
 
534
            // We avoid some trouble by not allowing end angle to be 0, in that case
535
            // we translate to 360
536
 
537
            // la is used to hold the label angle, which is centered on the slice
538
            if( $angle2 < 0.0001 && $angle1 > 0.0001 ) {
539
                $this->la[$i] = 2*M_PI - (abs(2*M_PI-$angle1)/2.0+$angle1);
540
            }
541
            elseif( $angle1 > $angle2 ) {
542
                // The case where the slice crosses the 3 a'clock line
543
                // Remember that the slices are counted clockwise and
544
                // labels are counted counter clockwise so we need to revert with 2 PI
545
                $this->la[$i] = 2*M_PI-$this->NormAngle($angle1 + ((2*M_PI - $angle1)+$angle2)/2);
546
            }
547
            else {
548
                $this->la[$i] = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
549
            }
550
 
551
            // Too avoid rounding problems we skip the slice if it is too small
552
            if( $d < 0.00001 ) continue;
553
 
554
            // If the user has specified an array of colors for each slice then use
555
            // that a color otherwise use the theme array (ta) of colors
556
            if( $this->setslicecolors==null ) {
557
                $slicecolor=$colors[$ta[$i%$numcolors]];
558
            }
559
            else {
560
                $slicecolor=$this->setslicecolors[$i%$numcolors];
561
            }
562
 
563
//            $_sa = round($angle1*180/M_PI);
564
//            $_ea = round($angle2*180/M_PI);
565
//            $_la = round($this->la[$i]*180/M_PI);
566
//            echo "Slice#$i: ang1=$_sa , ang2=$_ea, la=$_la, color=$slicecolor<br>";
567
 
568
 
569
            // If we have enabled antialias then we don't draw any border so
570
            // make the bordedr color the same as the slice color
571
            if( $this->pie_interior_border && $aaoption===0 ) {
572
                $img->SetColor($this->color);
573
            }
574
            else {
575
                $img->SetColor($slicecolor);
576
            }
577
            $arccolor = $this->pie_border && $aaoption===0 ? $this->color : "";
578
 
579
            // Calculate the x,y coordinates for the base of this slice taking
580
            // the exploded distance into account. Here we use the mid angle as the
581
            // ray of extension and we have the mid angle handy as it is also the
582
            // label angle
583
            $xcm = $xc + $this->explode_radius[$j]*cos($this->la[$i])*$expscale;
584
            $ycm = $yc - $this->explode_radius[$j]*sin($this->la[$i])*$expscale;
585
 
586
            // If we are not just drawing the labels then draw this cake slice
587
            if( $aaoption !== 2 ) {
588
 
589
                $_sa = round($angle1*180/M_PI);
590
                $_ea = round($angle2*180/M_PI);
591
                $_la = round($this->la[$i]*180/M_PI);
592
                //echo "[$i] sa=$_sa, ea=$_ea, la[$i]=$_la, (color=$slicecolor)<br>";
593
 
594
                // The CakeSlice method draws a full circle in case of start angle = end angle
595
                // for pie slices we want this in case the slice have a value larger than 99% of the
596
                // total sum
597
                if( abs($_ea-$_sa) >= 1 || $d == $sum ) {
598
                    $img->CakeSlice($xcm,$ycm,$radius-1,$radius-1,$_sa,$_ea,$slicecolor,$arccolor);
599
                }
600
            }
601
 
602
            // If the CSIM is used then make sure we register a CSIM area for this slice as well
603
            if( $this->csimtargets && $aaoption !== 1 ) {
604
                $this->AddSliceToCSIM($i,$xcm,$ycm,$radius,$angle1,$angle2);
605
            }
606
        }
607
 
608
        // Format the titles for each slice
609
        if( $aaoption !== 2 ) {
610
            for( $i=0; $i < $n; ++$i) {
611
                if( $this->labeltype==0 ) {
612
                    if( $sum != 0 )
613
                    $l = 100.0*$this->data[$i]/$sum;
614
                    else
615
                    $l = 0.0;
616
                }
617
                elseif( $this->labeltype==1 ) {
618
                    $l = $this->data[$i]*1.0;
619
                }
620
                else {
621
                    $l = $this->adjusted_data[$i];
622
                }
623
                if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
624
                $this->labels[$i]=sprintf($this->labels[$i],$l);
625
                else
626
                $this->labels[$i]=$l;
627
            }
628
        }
629
 
630
        if( $this->value->show && $aaoption !== 1 ) {
631
            $this->StrokeAllLabels($img,$xc,$yc,$radius);
632
        }
633
 
634
        // Adjust title position
635
        if( $aaoption !== 1 ) {
636
            $this->title->SetPos($xc,
637
            $yc-$this->title->GetFontHeight($img)-$radius-$this->title->margin,
638
     "center","bottom");
639
            $this->title->Stroke($img);
640
        }
641
 
642
    }
643
 
644
    //---------------
645
    // PRIVATE METHODS
646
 
647
    function NormAngle($a) {
648
        while( $a < 0 ) $a += 2*M_PI;
649
        while( $a > 2*M_PI ) $a -= 2*M_PI;
650
        return $a;
651
    }
652
 
653
    function Quadrant($a) {
654
        $a=$this->NormAngle($a);
655
        if( $a > 0 && $a <= M_PI/2 )
656
        return 0;
657
        if( $a > M_PI/2 && $a <= M_PI )
658
        return 1;
659
        if( $a > M_PI && $a <= 1.5*M_PI )
660
        return 2;
661
        if( $a > 1.5*M_PI )
662
        return 3;
663
    }
664
 
665
    function StrokeGuideLabels($img,$xc,$yc,$radius) {
666
        $n = count($this->labels);
667
 
668
        //-----------------------------------------------------------------------
669
        // Step 1 of the algorithm is to construct a number of clusters
670
        // a cluster is defined as all slices within the same quadrant (almost)
671
        // that has an angular distance less than the treshold
672
        //-----------------------------------------------------------------------
673
        $tresh_hold=25 * M_PI/180; // 25 degrees difference to be in a cluster
674
        $incluster=false; // flag if we are currently in a cluster or not
675
        $clusters = array(); // array of clusters
676
        $cidx=-1;  // running cluster index
677
 
678
        // Go through all the labels and construct a number of clusters
679
        for($i=0; $i < $n-1; ++$i) {
680
            // Calc the angle distance between two consecutive slices
681
            $a1=$this->la[$i];
682
            $a2=$this->la[$i+1];
683
            $q1 = $this->Quadrant($a1);
684
            $q2 = $this->Quadrant($a2);
685
            $diff = abs($a1-$a2);
686
            if( $diff < $tresh_hold ) {
687
                if( $incluster ) {
688
                    $clusters[$cidx][1]++;
689
                    // Each cluster can only cover one quadrant
690
                    // Do we cross a quadrant ( and must break the cluster)
691
                    if( $q1 !=  $q2 ) {
692
                        // If we cross a quadrant boundary we normally start a
693
                        // new cluster. However we need to take the 12'a clock
694
                        // and 6'a clock positions into a special consideration.
695
                        // Case 1: WE go from q=1 to q=2 if the last slice on
696
                        // the cluster for q=1 is close to 12'a clock and the
697
                        // first slice in q=0 is small we extend the previous
698
                        // cluster
699
                        if( $q1 == 1 && $q2 == 0 && $a2 > (90-15)*M_PI/180 ) {
700
                            if( $i < $n-2 ) {
701
                                $a3 = $this->la[$i+2];
702
                                // If there isn't a cluster coming up with the next-next slice
703
                                // we extend the previous cluster to cover this slice as well
704
                                if( abs($a3-$a2) >= $tresh_hold ) {
705
                                    $clusters[$cidx][1]++;
706
                                    $i++;
707
                                }
708
                            }
709
                        }
710
                        elseif( $q1 == 3 && $q2 == 2 && $a2 > (270-15)*M_PI/180 ) {
711
                            if( $i < $n-2 ) {
712
                                $a3 = $this->la[$i+2];
713
                                // If there isn't a cluster coming up with the next-next slice
714
                                // we extend the previous cluster to cover this slice as well
715
                                if( abs($a3-$a2) >= $tresh_hold ) {
716
                                    $clusters[$cidx][1]++;
717
                                    $i++;
718
                                }
719
                            }
720
                        }
721
 
722
                        if( $q1==2 && $q2==1 && $a2 > (180-15)*M_PI/180 ) {
723
                            $clusters[$cidx][1]++;
724
                            $i++;
725
                        }
726
 
727
                        $incluster = false;
728
                    }
729
                }
730
                elseif( $q1 == $q2)  {
731
                    $incluster = true;
732
                    // Now we have a special case for quadrant 0. If we previously
733
                    // have a cluster of one in quadrant 0 we just extend that
734
                    // cluster. If we don't do this then we risk that the label
735
                    // for the cluster of one will cross the guide-line
736
                    if( $q1 == 0 && $cidx > -1 &&
737
                    $clusters[$cidx][1] == 1 &&
738
                    $this->Quadrant($this->la[$clusters[$cidx][0]]) == 0 ) {
739
                        $clusters[$cidx][1]++;
740
                    }
741
                    else {
742
                        $cidx++;
743
                        $clusters[$cidx][0] = $i;
744
                        $clusters[$cidx][1] = 1;
745
                    }
746
                }
747
                else {
748
                    // Create a "cluster" of one since we are just crossing
749
                    // a quadrant
750
                    $cidx++;
751
                    $clusters[$cidx][0] = $i;
752
                    $clusters[$cidx][1] = 1;
753
                }
754
            }
755
            else {
756
                if( $incluster ) {
757
                    // Add the last slice
758
                    $clusters[$cidx][1]++;
759
                    $incluster = false;
760
                }
761
                else { // Create a "cluster" of one
762
                    $cidx++;
763
                    $clusters[$cidx][0] = $i;
764
                    $clusters[$cidx][1] = 1;
765
                }
766
            }
767
        }
768
        // Handle the very last slice
769
        if( $incluster ) {
770
            $clusters[$cidx][1]++;
771
        }
772
        else { // Create a "cluster" of one
773
            $cidx++;
774
            $clusters[$cidx][0] = $i;
775
            $clusters[$cidx][1] = 1;
776
        }
777
 
778
        /*
779
         if( true ) {
780
         // Debug printout in labels
781
         for( $i=0; $i <= $cidx; ++$i ) {
782
         for( $j=0; $j < $clusters[$i][1]; ++$j ) {
783
         $a = $this->la[$clusters[$i][0]+$j];
784
         $aa = round($a*180/M_PI);
785
         $q = $this->Quadrant($a);
786
         $this->labels[$clusters[$i][0]+$j]="[$q:$aa] $i:$j";
787
         }
788
         }
789
         }
790
         */
791
 
792
        //-----------------------------------------------------------------------
793
        // Step 2 of the algorithm is use the clusters and draw the labels
794
        // and guidelines
795
        //-----------------------------------------------------------------------
796
 
797
        // We use the font height as the base factor for how far we need to
798
        // spread the labels in the Y-direction.
799
        $this->value->ApplyFont($img);
800
        $fh = $img->GetFontHeight();
801
        $origvstep=$fh*$this->iGuideVFactor;
802
        $this->value->SetMargin(0);
803
 
804
        // Number of clusters found
805
        $nc = count($clusters);
806
 
807
        // Walk through all the clusters
808
        for($i=0; $i < $nc; ++$i) {
809
 
810
            // Start angle and number of slices in this cluster
811
            $csize = $clusters[$i][1];
812
            $a = $this->la[$clusters[$i][0]];
813
            $q = $this->Quadrant($a);
814
 
815
            // Now set up the start and end conditions to make sure that
816
            // in each cluster we walk through the all the slices starting with the slice
817
            // closest to the equator. Since all slices are numbered clockwise from "3'a clock"
818
            // we have different conditions depending on in which quadrant the slice lies within.
819
            if( $q == 0 ) {
820
                $start = $csize-1; $idx = $start; $step = -1; $vstep = -$origvstep;
821
            }
822
            elseif( $q == 1 ) {
823
                $start = 0; $idx = $start; $step = 1; $vstep = -$origvstep;
824
            }
825
            elseif( $q == 2 ) {
826
                $start = $csize-1; $idx = $start; $step = -1; $vstep = $origvstep;
827
            }
828
            elseif( $q == 3 ) {
829
                $start = 0; $idx = $start; $step = 1; $vstep = $origvstep;
830
            }
831
 
832
            // Walk through all slices within this cluster
833
            for($j=0; $j < $csize; ++$j) {
834
                // Now adjust the position of the labels in each cluster starting
835
                // with the slice that is closest to the equator of the pie
836
                $a = $this->la[$clusters[$i][0]+$idx];
837
 
838
                // Guide line start in the center of the arc of the slice
839
                $r = $radius+$this->explode_radius[$n-1-($clusters[$i][0]+$idx)];
840
                $x = round($r*cos($a)+$xc);
841
                $y = round($yc-$r*sin($a));
842
 
843
                // The distance from the arc depends on chosen font and the "R-Factor"
844
                $r += $fh*$this->iGuideLineRFactor;
845
 
846
                // Should the labels be placed curved along the pie or in straight columns
847
                // outside the pie?
848
                if( $this->iGuideLineCurve )
849
                $xt=round($r*cos($a)+$xc);
850
 
851
                // If this is the first slice in the cluster we need some first time
852
                // proessing
853
                if( $idx == $start ) {
854
                    if( ! $this->iGuideLineCurve )
855
                    $xt=round($r*cos($a)+$xc);
856
                    $yt=round($yc-$r*sin($a));
857
 
858
                    // Some special consideration in case this cluster starts
859
                    // in quadrant 1 or 3 very close to the "equator" (< 20 degrees)
860
                    // and the previous clusters last slice is within the tolerance.
861
                    // In that case we add a font height to this labels Y-position
862
                    // so it doesn't collide with
863
                    // the slice in the previous cluster
864
                    $prevcluster = ($i + ($nc-1) ) % $nc;
865
                    $previdx=$clusters[$prevcluster][0]+$clusters[$prevcluster][1]-1;
866
                    if( $q == 1 && $a > 160*M_PI/180 ) {
867
                        // Get the angle for the previous clusters last slice
868
                        $diff = abs($a-$this->la[$previdx]);
869
                        if( $diff < $tresh_hold ) {
870
                            $yt -= $fh;
871
                        }
872
                    }
873
                    elseif( $q == 3 && $a > 340*M_PI/180 ) {
874
                        // We need to subtract 360 to compare angle distance between
875
                        // q=0 and q=3
876
                        $diff = abs($a-$this->la[$previdx]-360*M_PI/180);
877
                        if( $diff < $tresh_hold ) {
878
                            $yt += $fh;
879
                        }
880
                    }
881
 
882
                }
883
                else {
884
                    // The step is at minimum $vstep but if the slices are relatively large
885
                    // we make sure that we add at least a step that corresponds to the vertical
886
                    // distance between the centers at the arc on the slice
887
                    $prev_a = $this->la[$clusters[$i][0]+($idx-$step)];
888
                    $dy = abs($radius*(sin($a)-sin($prev_a))*1.2);
889
                    if( $vstep > 0 )
890
                    $yt += max($vstep,$dy);
891
                    else
892
                    $yt += min($vstep,-$dy);
893
                }
894
 
895
                $label = $this->labels[$clusters[$i][0]+$idx];
896
 
897
                if( $csize == 1 ) {
898
                    // A "meta" cluster with only one slice
899
                    $r = $radius+$this->explode_radius[$n-1-($clusters[$i][0]+$idx)];
900
                    $rr = $r+$img->GetFontHeight()/2;
901
                    $xt=round($rr*cos($a)+$xc);
902
                    $yt=round($yc-$rr*sin($a));
903
                    $this->StrokeLabel($label,$img,$xc,$yc,$a,$r);
904
                    if( $this->iShowGuideLineForSingle )
905
                    $this->guideline->Stroke($img,$x,$y,$xt,$yt);
906
                }
907
                else {
908
                    $this->guideline->Stroke($img,$x,$y,$xt,$yt);
909
                    if( $q==1 || $q==2 ) {
910
                        // Left side of Pie
911
                        $this->guideline->Stroke($img,$xt,$yt,$xt-$this->guidelinemargin,$yt);
912
                        $lbladj = -$this->guidelinemargin-5;
913
                        $this->value->halign = "right";
914
                        $this->value->valign = "center";
915
                    }
916
                    else {
917
                        // Right side of pie
918
                        $this->guideline->Stroke($img,$xt,$yt,$xt+$this->guidelinemargin,$yt);
919
                        $lbladj = $this->guidelinemargin+5;
920
                        $this->value->halign = "left";
921
                        $this->value->valign = "center";
922
                    }
923
                    $this->value->Stroke($img,$label,$xt+$lbladj,$yt);
924
                }
925
 
926
                // Udate idx to point to next slice in the cluster to process
927
                $idx += $step;
928
            }
929
        }
930
    }
931
 
932
    function StrokeAllLabels($img,$xc,$yc,$radius) {
933
        // First normalize all angles for labels
934
        $n = count($this->la);
935
        for($i=0; $i < $n; ++$i) {
936
            $this->la[$i] = $this->NormAngle($this->la[$i]);
937
        }
938
        if( $this->guideline->iShow ) {
939
            $this->StrokeGuideLabels($img,$xc,$yc,$radius);
940
        }
941
        else {
942
            $n = count($this->labels);
943
            for($i=0; $i < $n; ++$i) {
944
                $this->StrokeLabel($this->labels[$i],$img,$xc,$yc,
945
                $this->la[$i],
946
                $radius + $this->explode_radius[$n-1-$i]);
947
            }
948
        }
949
    }
950
 
951
    // Position the labels of each slice
952
    function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
953
 
954
        // Default value
955
        if( $this->ilabelposadj === 'auto' )
956
        $this->ilabelposadj = 0.65;
957
 
958
        // We position the values diferently depending on if they are inside
959
        // or outside the pie
960
        if( $this->ilabelposadj < 1.0 ) {
961
 
962
            $this->value->SetAlign('center','center');
963
            $this->value->margin = 0;
964
 
965
            $xt=round($this->ilabelposadj*$r*cos($a)+$xc);
966
            $yt=round($yc-$this->ilabelposadj*$r*sin($a));
967
 
968
            $this->value->Stroke($img,$label,$xt,$yt);
969
        }
970
        else {
971
 
972
            $this->value->halign = "left";
973
            $this->value->valign = "top";
974
            $this->value->margin = 0;
975
 
976
            // Position the axis title.
977
            // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
978
            // that intersects with the extension of the corresponding axis. The code looks a little
979
            // bit messy but this is really the only way of having a reasonable position of the
980
            // axis titles.
981
            $this->value->ApplyFont($img);
982
            $h=$img->GetTextHeight($label);
983
            // For numeric values the format of the display value
984
            // must be taken into account
985
            if( is_numeric($label) ) {
986
                if( $label > 0 )
987
                $w=$img->GetTextWidth(sprintf($this->value->format,$label));
988
                else
989
                $w=$img->GetTextWidth(sprintf($this->value->negformat,$label));
990
            }
991
            else
992
            $w=$img->GetTextWidth($label);
993
 
994
            if( $this->ilabelposadj > 1.0 && $this->ilabelposadj < 5.0) {
995
                $r *= $this->ilabelposadj;
996
            }
997
 
998
            $r += $img->GetFontHeight()/1.5;
999
 
1000
            $xt=round($r*cos($a)+$xc);
1001
            $yt=round($yc-$r*sin($a));
1002
 
1003
            // Normalize angle
1004
            while( $a < 0 ) $a += 2*M_PI;
1005
            while( $a > 2*M_PI ) $a -= 2*M_PI;
1006
 
1007
            if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
1008
            if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI;
1009
            if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
1010
            if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
1011
 
1012
            if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
1013
            if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
1014
            if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
1015
            if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
1016
            if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
1017
 
1018
            $this->value->Stroke($img,$label,$xt-$dx*$w,$yt-$dy*$h);
1019
        }
1020
    }
1021
} // Class
1022
 
1023
 
1024
//===================================================
1025
// CLASS PiePlotC
1026
// Description: Same as a normal pie plot but with a
1027
// filled circle in the center
1028
//===================================================
1029
class PiePlotC extends PiePlot {
1030
    private $imidsize=0.5;  // Fraction of total width
1031
    private $imidcolor='white';
1032
    public $midtitle='';
1033
    private $middlecsimtarget='',$middlecsimwintarget='',$middlecsimalt='';
1034
 
1035
    function __construct($data,$aCenterTitle='') {
1036
        parent::__construct($data);
1037
        $this->midtitle = new Text();
1038
        $this->midtitle->ParagraphAlign('center');
1039
    }
1040
 
1041
    function SetMid($aTitle,$aColor='white',$aSize=0.5) {
1042
        $this->midtitle->Set($aTitle);
1043
 
1044
        $this->imidsize = $aSize ;
1045
        $this->imidcolor = $aColor ;
1046
    }
1047
 
1048
    function SetMidTitle($aTitle) {
1049
        $this->midtitle->Set($aTitle);
1050
    }
1051
 
1052
    function SetMidSize($aSize) {
1053
        $this->imidsize = $aSize ;
1054
    }
1055
 
1056
    function SetMidColor($aColor) {
1057
        $this->imidcolor = $aColor ;
1058
    }
1059
 
1060
    function SetMidCSIM($aTarget,$aAlt='',$aWinTarget='') {
1061
        $this->middlecsimtarget = $aTarget;
1062
        $this->middlecsimwintarget = $aWinTarget;
1063
        $this->middlecsimalt = $aAlt;
1064
    }
1065
 
1066
    function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {
1067
        //Slice number, ellipse centre (x,y), radius, start angle, end angle
1068
        while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
1069
        while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
1070
 
1071
        $sa = 2*M_PI - $sa;
1072
        $ea = 2*M_PI - $ea;
1073
 
1074
        // Special case when we have only one slice since then both start and end
1075
        // angle will be == 0
1076
        if( abs($sa - $ea) < 0.0001 ) {
1077
            $sa=2*M_PI; $ea=0;
1078
        }
1079
 
1080
        // Add inner circle first point
1081
        $xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
1082
        $yp = floor($yc-($this->imidsize*$radius*sin($ea)));
1083
        $coords = "$xp, $yp";
1084
 
1085
        //add coordinates every 0.25 radians
1086
        $a=$ea+0.25;
1087
 
1088
        // If we cross the 360-limit with a slice we need to handle
1089
        // the fact that end angle is smaller than start
1090
        if( $sa < $ea ) {
1091
            while ($a <= 2*M_PI) {
1092
                $xp = floor($radius*cos($a)+$xc);
1093
                $yp = floor($yc-$radius*sin($a));
1094
                $coords.= ", $xp, $yp";
1095
                $a += 0.25;
1096
            }
1097
            $a -= 2*M_PI;
1098
        }
1099
 
1100
        while ($a < $sa) {
1101
            $xp = floor(($this->imidsize*$radius*cos($a)+$xc));
1102
            $yp = floor($yc-($this->imidsize*$radius*sin($a)));
1103
            $coords.= ", $xp, $yp";
1104
            $a += 0.25;
1105
        }
1106
 
1107
        // Make sure we end at the last point
1108
        $xp = floor(($this->imidsize*$radius*cos($sa)+$xc));
1109
        $yp = floor($yc-($this->imidsize*$radius*sin($sa)));
1110
        $coords.= ", $xp, $yp";
1111
 
1112
        // Straight line to outer circle
1113
        $xp = floor($radius*cos($sa)+$xc);
1114
        $yp = floor($yc-$radius*sin($sa));
1115
        $coords.= ", $xp, $yp";
1116
 
1117
        //add coordinates every 0.25 radians
1118
        $a=$sa - 0.25;
1119
        while ($a > $ea) {
1120
            $xp = floor($radius*cos($a)+$xc);
1121
            $yp = floor($yc-$radius*sin($a));
1122
            $coords.= ", $xp, $yp";
1123
            $a -= 0.25;
1124
        }
1125
 
1126
        //Add the last point on the arc
1127
        $xp = floor($radius*cos($ea)+$xc);
1128
        $yp = floor($yc-$radius*sin($ea));
1129
        $coords.= ", $xp, $yp";
1130
 
1131
        // Close the arc
1132
        $xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
1133
        $yp = floor($yc-($this->imidsize*$radius*sin($ea)));
1134
        $coords .= ", $xp, $yp";
1135
 
1136
        if( !empty($this->csimtargets[$i]) ) {
1137
            $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
1138
            $this->csimtargets[$i]."\"";
1139
            if( !empty($this->csimwintargets[$i]) ) {
1140
                $this->csimareas .= " target=\"".$this->csimwintargets[$i]."\" ";
1141
            }
1142
            if( !empty($this->csimalts[$i]) ) {
1143
                $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
1144
                $this->csimareas .= " title=\"$tmp\"  alt=\"$tmp\" ";
1145
            }
1146
            $this->csimareas .= " />\n";
1147
        }
1148
    }
1149
 
1150
 
1151
    function Stroke($img,$aaoption=0) {
1152
 
1153
        // Stroke the pie but don't stroke values
1154
        $tmp =  $this->value->show;
1155
        $this->value->show = false;
1156
        parent::Stroke($img,$aaoption);
1157
        $this->value->show = $tmp;
1158
 
1159
        $xc = round($this->posx*$img->width);
1160
        $yc = round($this->posy*$img->height);
1161
 
1162
        $radius = floor($this->radius * min($img->width,$img->height)) ;
1163
 
1164
 
1165
        if( $this->imidsize > 0 && $aaoption !== 2 ) {
1166
 
1167
            if( $this->ishadowcolor != "" ) {
1168
                $img->SetColor($this->ishadowcolor);
1169
                $img->FilledCircle($xc+$this->ishadowdrop,$yc+$this->ishadowdrop,
1170
                round($radius*$this->imidsize));
1171
            }
1172
 
1173
            $img->SetColor($this->imidcolor);
1174
            $img->FilledCircle($xc,$yc,round($radius*$this->imidsize));
1175
 
1176
            if(  $this->pie_border && $aaoption === 0 ) {
1177
                $img->SetColor($this->color);
1178
                $img->Circle($xc,$yc,round($radius*$this->imidsize));
1179
            }
1180
 
1181
            if( !empty($this->middlecsimtarget) )
1182
            $this->AddMiddleCSIM($xc,$yc,round($radius*$this->imidsize));
1183
 
1184
        }
1185
 
1186
        if( $this->value->show && $aaoption !== 1) {
1187
            $this->StrokeAllLabels($img,$xc,$yc,$radius);
1188
            $this->midtitle->SetPos($xc,$yc,'center','center');
1189
            $this->midtitle->Stroke($img);
1190
        }
1191
 
1192
    }
1193
 
1194
    function AddMiddleCSIM($xc,$yc,$r) {
1195
        $xc=round($xc);$yc=round($yc);$r=round($r);
1196
        $this->csimareas .= "<area shape=\"circle\" coords=\"$xc,$yc,$r\" href=\"".
1197
        $this->middlecsimtarget."\"";
1198
        if( !empty($this->middlecsimwintarget) ) {
1199
            $this->csimareas .= " target=\"".$this->middlecsimwintarget."\"";
1200
        }
1201
        if( !empty($this->middlecsimalt) ) {
1202
            $tmp = $this->middlecsimalt;
1203
            $this->csimareas .= " title=\"$tmp\" alt=\"$tmp\" ";
1204
        }
1205
        $this->csimareas .= " />\n";
1206
    }
1207
 
1208
    function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
1209
 
1210
        if( $this->ilabelposadj === 'auto' )
1211
        $this->ilabelposadj = (1-$this->imidsize)/2+$this->imidsize;
1212
 
1213
        parent::StrokeLabel($label,$img,$xc,$yc,$a,$r);
1214
 
1215
    }
1216
 
1217
}
1218
 
1219
 
1220
//===================================================
1221
// CLASS PieGraph
1222
// Description:
1223
//===================================================
1224
class PieGraph extends Graph {
1225
    private $posx, $posy, $radius;
1226
    private $legends=array();
1227
    public $plots=array();
1228
    public $pieaa = false ;
1229
    //---------------
1230
    // CONSTRUCTOR
1231
    function __construct($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
1232
        parent::__construct($width,$height,$cachedName,$timeout,$inline);
1233
        $this->posx=$width/2;
1234
        $this->posy=$height/2;
1235
        $this->SetColor(array(255,255,255));
1236
    }
1237
 
1238
    //---------------
1239
    // PUBLIC METHODS
1240
    function Add($aObj) {
1241
 
1242
        if( is_array($aObj) && count($aObj) > 0 )
1243
        $cl = $aObj[0];
1244
        else
1245
        $cl = $aObj;
1246
 
1247
        if( $cl instanceof Text )
1248
        $this->AddText($aObj);
1249
        elseif( class_exists('IconPlot',false) && ($cl instanceof IconPlot) )
1250
        $this->AddIcon($aObj);
1251
        else {
1252
            if( is_array($aObj) ) {
1253
                $n = count($aObj);
1254
                for($i=0; $i < $n; ++$i ) {
1255
                    $this->plots[] = $aObj[$i];
1256
                }
1257
            }
1258
            else {
1259
                $this->plots[] = $aObj;
1260
            }
1261
        }
1262
    }
1263
 
1264
    function SetAntiAliasing($aFlg=true) {
1265
        $this->pieaa = $aFlg;
1266
    }
1267
 
1268
    function SetColor($c) {
1269
        $this->SetMarginColor($c);
1270
    }
1271
 
1272
 
1273
    function DisplayCSIMAreas() {
1274
        $csim="";
1275
        foreach($this->plots as $p ) {
1276
            $csim .= $p->GetCSIMareas();
1277
        }
1278
 
1279
        $csim.= $this->legend->GetCSIMareas();
1280
        if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
1281
            $this->img->SetColor($this->csimcolor);
1282
            $n = count($coords[0]);
1283
            for ($i=0; $i < $n; $i++) {
1284
                if ($coords[1][$i]=="poly") {
1285
                    preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
1286
                    $this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
1287
                    $m = count($pts[0]);
1288
                    for ($j=0; $j < $m; $j++) {
1289
                        $this->img->LineTo($pts[1][$j],$pts[2][$j]);
1290
                    }
1291
                } else if ($coords[1][$i]=="rect") {
1292
                    $pts = preg_split('/,/', $coords[2][$i]);
1293
                    $this->img->SetStartPoint($pts[0],$pts[1]);
1294
                    $this->img->LineTo($pts[2],$pts[1]);
1295
                    $this->img->LineTo($pts[2],$pts[3]);
1296
                    $this->img->LineTo($pts[0],$pts[3]);
1297
                    $this->img->LineTo($pts[0],$pts[1]);
1298
 
1299
                }
1300
            }
1301
        }
1302
    }
1303
 
1304
    // Method description
1305
    function Stroke($aStrokeFileName="") {
1306
        // If the filename is the predefined value = '_csim_special_'
1307
        // we assume that the call to stroke only needs to do enough
1308
        // to correctly generate the CSIM maps.
1309
        // We use this variable to skip things we don't strictly need
1310
        // to do to generate the image map to improve performance
1311
        // a best we can. Therefor you will see a lot of tests !$_csim in the
1312
        // code below.
1313
        $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
1314
 
1315
        // If we are called the second time (perhaps the user has called GetHTMLImageMap()
1316
        // himself then the legends have alsready been populated once in order to get the
1317
        // CSIM coordinats. Since we do not want the legends to be populated a second time
1318
        // we clear the legends
1319
        $this->legend->Clear();
1320
 
1321
        // We need to know if we have stroked the plot in the
1322
        // GetCSIMareas. Otherwise the CSIM hasn't been generated
1323
        // and in the case of GetCSIM called before stroke to generate
1324
        // CSIM without storing an image to disk GetCSIM must call Stroke.
1325
        $this->iHasStroked = true;
1326
 
1327
        $n = count($this->plots);
1328
 
1329
        if( $this->pieaa ) {
1330
 
1331
            if( !$_csim ) {
1332
                if( $this->background_image != "" ) {
1333
                    $this->StrokeFrameBackground();
1334
                }
1335
                else {
1336
                    $this->StrokeFrame();
1337
                    $this->StrokeBackgroundGrad();
1338
                }
1339
            }
1340
 
1341
 
1342
            $w = $this->img->width;
1343
            $h = $this->img->height;
1344
            $oldimg = $this->img->img;
1345
 
1346
            $this->img->CreateImgCanvas(2*$w,2*$h);
1347
 
1348
            $this->img->SetColor( $this->margin_color );
1349
            $this->img->FilledRectangle(0,0,2*$w-1,2*$h-1);
1350
 
1351
            // Make all icons *2 i size since we will be scaling down the
1352
            // imahe to do the anti aliasing
1353
            $ni = count($this->iIcons);
1354
            for($i=0; $i < $ni; ++$i) {
1355
                $this->iIcons[$i]->iScale *= 2 ;
1356
                if( $this->iIcons[$i]->iX > 1 )
1357
                $this->iIcons[$i]->iX *= 2 ;
1358
                if( $this->iIcons[$i]->iY > 1 )
1359
                $this->iIcons[$i]->iY *= 2 ;
1360
            }
1361
 
1362
            $this->StrokeIcons();
1363
 
1364
            for($i=0; $i < $n; ++$i) {
1365
                if( $this->plots[$i]->posx > 1 )
1366
                $this->plots[$i]->posx *= 2 ;
1367
                if( $this->plots[$i]->posy > 1 )
1368
                $this->plots[$i]->posy *= 2 ;
1369
 
1370
                $this->plots[$i]->Stroke($this->img,1);
1371
 
1372
                if( $this->plots[$i]->posx > 1 )
1373
                $this->plots[$i]->posx /= 2 ;
1374
                if( $this->plots[$i]->posy > 1 )
1375
                $this->plots[$i]->posy /= 2 ;
1376
            }
1377
 
1378
            $indent = $this->doframe ? ($this->frame_weight + ($this->doshadow ? $this->shadow_width : 0 )) : 0 ;
1379
            $indent += $this->framebevel ? $this->framebeveldepth + 1 : 0 ;
1380
            $this->img->CopyCanvasH($oldimg,$this->img->img,$indent,$indent,$indent,$indent,
1381
            $w-2*$indent,$h-2*$indent,2*($w-$indent),2*($h-$indent));
1382
 
1383
            $this->img->img = $oldimg ;
1384
            $this->img->width = $w ;
1385
            $this->img->height = $h ;
1386
 
1387
            for($i=0; $i < $n; ++$i) {
1388
                $this->plots[$i]->Stroke($this->img,2); // Stroke labels
1389
                $this->plots[$i]->Legend($this);
1390
            }
1391
 
1392
        }
1393
        else {
1394
 
1395
            if( !$_csim ) {
1396
                if( $this->background_image != "" ) {
1397
                    $this->StrokeFrameBackground();
1398
                }
1399
                else {
1400
                    $this->StrokeFrame();
1401
                    $this->StrokeBackgroundGrad();
1402
                }
1403
            }
1404
 
1405
            $this->StrokeIcons();
1406
 
1407
            for($i=0; $i < $n; ++$i) {
1408
                $this->plots[$i]->Stroke($this->img);
1409
                $this->plots[$i]->Legend($this);
1410
            }
1411
        }
1412
 
1413
        $this->legend->Stroke($this->img);
1414
        $this->footer->Stroke($this->img);
1415
        $this->StrokeTitles();
1416
 
1417
        if( !$_csim ) {
1418
 
1419
            // Stroke texts
1420
            if( $this->texts != null ) {
1421
                $n = count($this->texts);
1422
                for($i=0; $i < $n; ++$i ) {
1423
                    $this->texts[$i]->Stroke($this->img);
1424
                }
1425
            }
1426
 
1427
            if( _JPG_DEBUG ) {
1428
                $this->DisplayCSIMAreas();
1429
            }
1430
 
1431
            // Should we do any final image transformation
1432
            if( $this->iImgTrans ) {
1433
                if( !class_exists('ImgTrans',false) ) {
1434
                    require_once('jpgraph_imgtrans.php');
1435
                    //JpGraphError::Raise('In order to use image transformation you must include the file jpgraph_imgtrans.php in your script.');
1436
                }
1437
 
1438
                $tform = new ImgTrans($this->img->img);
1439
                $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
1440
                $this->iImgTransDirection,$this->iImgTransHighQ,
1441
                $this->iImgTransMinSize,$this->iImgTransFillColor,
1442
                $this->iImgTransBorder);
1443
            }
1444
 
1445
 
1446
            // If the filename is given as the special "__handle"
1447
            // then the image handler is returned and the image is NOT
1448
            // streamed back
1449
            if( $aStrokeFileName == _IMG_HANDLER ) {
1450
                return $this->img->img;
1451
            }
1452
            else {
1453
                // Finally stream the generated picture
1454
                $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
1455
                $aStrokeFileName);
1456
            }
1457
        }
1458
    }
1459
} // Class
1460
 
1461
/* EOF */
1462
?>