source: trunk/xgraph/jpgraph/jpgraph_pie.php@ 42

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