Subversion Repositories Sites.obs-saisons.fr

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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