Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

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