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

Last change on this file since 360 was 346, checked in by roby, 4 years ago

ulteriori modifiche per adeguamento a php7
Client: aggiornamento jpgraph

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