source: trunk/client/modules/Elezioni/grafici/jpgraph_pie.php@ 73

Last change on this file since 73 was 2, checked in by root, 15 years ago

importo il progetto

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