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