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

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