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