Changeset 265 for trunk/client/modules/Elezioni/grafici/jpgraph_pie3d.php
- Timestamp:
- Apr 13, 2019, 8:05:15 PM (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/client/modules/Elezioni/grafici/jpgraph_pie3d.php
r2 r265 1 1 <?php 2 2 /*======================================================================= 3 // File:JPGRAPH_PIE3D.PHP4 // Description: 3D Pie plot extension for JpGraph5 // Created:2001-03-246 // Ver: $Id: jpgraph_pie3d.php 956 2007-11-17 13:19:20Z ljp $7 //8 // Copyright (c) Aditus Consulting. All rights reserved.9 //========================================================================10 */3 // File: JPGRAPH_PIE3D.PHP 4 // Description: 3D Pie plot extension for JpGraph 5 // Created: 2001-03-24 6 // Ver: $Id: jpgraph_pie3d.php 1329 2009-06-20 19:23:30Z ljp $ 7 // 8 // Copyright (c) Asial Corporation. All rights reserved. 9 //======================================================================== 10 */ 11 11 12 12 //=================================================== 13 13 // CLASS PiePlot3D 14 // Description: Plots a 3D pie with a specified projection 14 // Description: Plots a 3D pie with a specified projection 15 15 // angle between 20 and 70 degrees. 16 16 //=================================================== 17 17 class PiePlot3D extends PiePlot { 18 18 private $labelhintcolor="red",$showlabelhint=true; 19 private $angle=50; 19 private $angle=50; 20 20 private $edgecolor="", $edgeweight=1; 21 21 private $iThickness=false; 22 23 //---------------24 // CONSTRUCTOR25 function PiePlot3d($data) {26 27 28 29 30 31 32 33 } 34 35 //---------------36 // PUBLIC METHODS 37 22 23 //--------------- 24 // CONSTRUCTOR 25 function __construct($data) { 26 $this->radius = 0.5; 27 $this->data = $data; 28 $this->title = new Text(""); 29 $this->title->SetFont(FF_FONT1,FS_BOLD); 30 $this->value = new DisplayValue(); 31 $this->value->Show(); 32 $this->value->SetFormat('%.0f%%'); 33 } 34 35 //--------------- 36 // PUBLIC METHODS 37 38 38 // Set label arrays 39 39 function SetLegends($aLegend) { 40 40 $this->legends = array_reverse(array_slice($aLegend,0,count($this->data))); 41 41 } 42 42 43 43 function SetSliceColors($aColors) { 44 44 $this->setslicecolors = $aColors; 45 45 } 46 46 47 47 function Legend($aGraph) { 48 49 48 parent::Legend($aGraph); 49 $aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol); 50 50 } 51 51 52 52 function SetCSIMTargets($aTargets,$aAlts='',$aWinTargets='') { 53 54 55 53 $this->csimtargets = $aTargets; 54 $this->csimwintargets = $aWinTargets; 55 $this->csimalts = $aAlts; 56 56 } 57 57 … … 59 59 // will be used to separate pie slices. 60 60 function SetEdge($aColor='black',$aWeight=1) { 61 $this->edgecolor = $aColor; 62 $this->edgeweight = $aWeight; 63 } 64 65 // Dummy function to make Pie3D behave in a similair way to 2D 66 function ShowBorder($exterior=true,$interior=true) { 67 JpGraphError::RaiseL(14001); 68 //('Pie3D::ShowBorder() . Deprecated function. Use Pie3D::SetEdge() to control the edges around slices.'); 61 $this->edgecolor = $aColor; 62 $this->edgeweight = $aWeight; 69 63 } 70 64 … … 72 66 // Must be between 20 and 70 degrees 73 67 function SetAngle($a) { 74 if( $a<5 || $a>90 ) 75 JpGraphError::RaiseL(14002); 76 //("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees."); 77 else 78 $this->angle = $a; 68 if( $a<5 || $a>90 ) { 69 JpGraphError::RaiseL(14002); 70 //("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees."); 71 } 72 else { 73 $this->angle = $a; 74 } 79 75 } 80 76 81 77 function Add3DSliceToCSIM($i,$xc,$yc,$height,$width,$thick,$sa,$ea) { //Slice number, ellipse centre (x,y), height, width, start angle, end angle 82 78 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 $this->csimareas .= " target=\"".$this->csimwintargets[$i]."\" "; 129 130 131 if( !empty($this->csimalts[$i]) ) { 132 133 134 135 136 79 $sa *= M_PI/180; 80 $ea *= M_PI/180; 81 82 //add coordinates of the centre to the map 83 $coords = "$xc, $yc"; 84 85 //add coordinates of the first point on the arc to the map 86 $xp = floor($width*cos($sa)/2+$xc); 87 $yp = floor($yc-$height*sin($sa)/2); 88 $coords.= ", $xp, $yp"; 89 90 //If on the front half, add the thickness offset 91 if ($sa >= M_PI && $sa <= 2*M_PI*1.01) { 92 $yp = floor($yp+$thick); 93 $coords.= ", $xp, $yp"; 94 } 95 96 //add coordinates every 0.2 radians 97 $a=$sa+0.2; 98 while ($a<$ea) { 99 $xp = floor($width*cos($a)/2+$xc); 100 if ($a >= M_PI && $a <= 2*M_PI*1.01) { 101 $yp = floor($yc-($height*sin($a)/2)+$thick); 102 } else { 103 $yp = floor($yc-$height*sin($a)/2); 104 } 105 $coords.= ", $xp, $yp"; 106 $a += 0.2; 107 } 108 109 //Add the last point on the arc 110 $xp = floor($width*cos($ea)/2+$xc); 111 $yp = floor($yc-$height*sin($ea)/2); 112 113 114 if ($ea >= M_PI && $ea <= 2*M_PI*1.01) { 115 $coords.= ", $xp, ".floor($yp+$thick); 116 } 117 $coords.= ", $xp, $yp"; 118 $alt=''; 119 120 if( !empty($this->csimtargets[$i]) ) { 121 $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtargets[$i]."\""; 122 123 if( !empty($this->csimwintargets[$i]) ) { 124 $this->csimareas .= " target=\"".$this->csimwintargets[$i]."\" "; 125 } 126 127 if( !empty($this->csimalts[$i]) ) { 128 $tmp=sprintf($this->csimalts[$i],$this->data[$i]); 129 $this->csimareas .= "alt=\"$tmp\" title=\"$tmp\" "; 130 } 131 $this->csimareas .= " />\n"; 132 } 137 133 138 134 } 139 135 140 136 function SetLabels($aLabels,$aLblPosAdj="auto") { 141 142 143 } 144 145 137 $this->labels = $aLabels; 138 $this->ilabelposadj=$aLblPosAdj; 139 } 140 141 146 142 // Distance from the pie to the labels 147 143 function SetLabelMargin($m) { 148 149 } 150 144 $this->value->SetMargin($m); 145 } 146 151 147 // Show a thin line from the pie to the label for a specific slice 152 148 function ShowLabelHint($f=true) { 153 154 } 155 149 $this->showlabelhint=$f; 150 } 151 156 152 // Set color of hint line to label for each slice 157 153 function SetLabelHintColor($c) { 158 154 $this->labelhintcolor=$c; 159 155 } 160 156 161 157 function SetHeight($aHeight) { 162 $this->iThickness = $aHeight;163 } 164 165 166 // Normalize Angle between 0-360158 $this->iThickness = $aHeight; 159 } 160 161 162 // Normalize Angle between 0-360 167 163 function NormAngle($a) { 168 169 // 170 171 172 173 174 175 176 177 178 179 180 181 } 182 183 184 185 // Draw one 3D pie slice at position ($xc,$yc) with height $z164 // Normalize anle to 0 to 2M_PI 165 // 166 if( $a > 0 ) { 167 while($a > 360) $a -= 360; 168 } 169 else { 170 while($a < 0) $a += 360; 171 } 172 if( $a < 0 ) 173 $a = 360 + $a; 174 175 if( $a == 360 ) $a=0; 176 return $a; 177 } 178 179 180 181 // Draw one 3D pie slice at position ($xc,$yc) with height $z 186 182 function Pie3DSlice($img,$xc,$yc,$w,$h,$sa,$ea,$z,$fillcolor,$shadow=0.65) { 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 $rsa = $sa/180*M_PI;// to Rad202 $rea = $ea/180*M_PI;// to Rad203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 $tsa = sin($a); 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 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 387 388 389 390 391 392 183 184 // Due to the way the 3D Pie algorithm works we are 185 // guaranteed that any slice we get into this method 186 // belongs to either the left or right side of the 187 // pie ellipse. Hence, no slice will cross 90 or 270 188 // point. 189 if( ($sa < 90 && $ea > 90) || ( ($sa > 90 && $sa < 270) && $ea > 270) ) { 190 JpGraphError::RaiseL(14003);//('Internal assertion failed. Pie3D::Pie3DSlice'); 191 exit(1); 192 } 193 194 $p[] = array(); 195 196 // Setup pre-calculated values 197 $rsa = $sa/180*M_PI; // to Rad 198 $rea = $ea/180*M_PI; // to Rad 199 $sinsa = sin($rsa); 200 $cossa = cos($rsa); 201 $sinea = sin($rea); 202 $cosea = cos($rea); 203 204 // p[] is the points for the overall slice and 205 // pt[] is the points for the top pie 206 207 // Angular step when approximating the arc with a polygon train. 208 $step = 0.05; 209 210 if( $sa >= 270 ) { 211 if( $ea > 360 || ($ea > 0 && $ea <= 90) ) { 212 if( $ea > 0 && $ea <= 90 ) { 213 // Adjust angle to simplify conditions in loops 214 $rea += 2*M_PI; 215 } 216 217 $p = array($xc,$yc,$xc,$yc+$z, 218 $xc+$w*$cossa,$z+$yc-$h*$sinsa); 219 $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa); 220 221 for( $a=$rsa; $a < 2*M_PI; $a += $step ) { 222 $tca = cos($a); 223 $tsa = sin($a); 224 $p[] = $xc+$w*$tca; 225 $p[] = $z+$yc-$h*$tsa; 226 $pt[] = $xc+$w*$tca; 227 $pt[] = $yc-$h*$tsa; 228 } 229 230 $pt[] = $xc+$w; 231 $pt[] = $yc; 232 233 $p[] = $xc+$w; 234 $p[] = $z+$yc; 235 $p[] = $xc+$w; 236 $p[] = $yc; 237 $p[] = $xc; 238 $p[] = $yc; 239 240 for( $a=2*M_PI+$step; $a < $rea; $a += $step ) { 241 $pt[] = $xc + $w*cos($a); 242 $pt[] = $yc - $h*sin($a); 243 } 244 245 $pt[] = $xc+$w*$cosea; 246 $pt[] = $yc-$h*$sinea; 247 $pt[] = $xc; 248 $pt[] = $yc; 249 250 } 251 else { 252 $p = array($xc,$yc,$xc,$yc+$z, 253 $xc+$w*$cossa,$z+$yc-$h*$sinsa); 254 $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa); 255 256 $rea = $rea == 0.0 ? 2*M_PI : $rea; 257 for( $a=$rsa; $a < $rea; $a += $step ) { 258 $tca = cos($a); 259 $tsa = sin($a); 260 $p[] = $xc+$w*$tca; 261 $p[] = $z+$yc-$h*$tsa; 262 $pt[] = $xc+$w*$tca; 263 $pt[] = $yc-$h*$tsa; 264 } 265 266 $pt[] = $xc+$w*$cosea; 267 $pt[] = $yc-$h*$sinea; 268 $pt[] = $xc; 269 $pt[] = $yc; 270 271 $p[] = $xc+$w*$cosea; 272 $p[] = $z+$yc-$h*$sinea; 273 $p[] = $xc+$w*$cosea; 274 $p[] = $yc-$h*$sinea; 275 $p[] = $xc; 276 $p[] = $yc; 277 } 278 } 279 elseif( $sa >= 180 ) { 280 $p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea); 281 $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea); 282 283 for( $a=$rea; $a>$rsa; $a -= $step ) { 284 $tca = cos($a); 285 $tsa = sin($a); 286 $p[] = $xc+$w*$tca; 287 $p[] = $z+$yc-$h*$tsa; 288 $pt[] = $xc+$w*$tca; 289 $pt[] = $yc-$h*$tsa; 290 } 291 292 $pt[] = $xc+$w*$cossa; 293 $pt[] = $yc-$h*$sinsa; 294 $pt[] = $xc; 295 $pt[] = $yc; 296 297 $p[] = $xc+$w*$cossa; 298 $p[] = $z+$yc-$h*$sinsa; 299 $p[] = $xc+$w*$cossa; 300 $p[] = $yc-$h*$sinsa; 301 $p[] = $xc; 302 $p[] = $yc; 303 304 } 305 elseif( $sa >= 90 ) { 306 if( $ea > 180 ) { 307 $p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea); 308 $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea); 309 310 for( $a=$rea; $a > M_PI; $a -= $step ) { 311 $tca = cos($a); 312 $tsa = sin($a); 313 $p[] = $xc+$w*$tca; 314 $p[] = $z + $yc - $h*$tsa; 315 $pt[] = $xc+$w*$tca; 316 $pt[] = $yc-$h*$tsa; 317 } 318 319 $p[] = $xc-$w; 320 $p[] = $z+$yc; 321 $p[] = $xc-$w; 322 $p[] = $yc; 323 $p[] = $xc; 324 $p[] = $yc; 325 326 $pt[] = $xc-$w; 327 $pt[] = $z+$yc; 328 $pt[] = $xc-$w; 329 $pt[] = $yc; 330 331 for( $a=M_PI-$step; $a > $rsa; $a -= $step ) { 332 $pt[] = $xc + $w*cos($a); 333 $pt[] = $yc - $h*sin($a); 334 } 335 336 $pt[] = $xc+$w*$cossa; 337 $pt[] = $yc-$h*$sinsa; 338 $pt[] = $xc; 339 $pt[] = $yc; 340 341 } 342 else { // $sa >= 90 && $ea <= 180 343 $p = array($xc,$yc,$xc,$yc+$z, 344 $xc+$w*$cosea,$z+$yc-$h*$sinea, 345 $xc+$w*$cosea,$yc-$h*$sinea, 346 $xc,$yc); 347 348 $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea); 349 350 for( $a=$rea; $a>$rsa; $a -= $step ) { 351 $pt[] = $xc + $w*cos($a); 352 $pt[] = $yc - $h*sin($a); 353 } 354 355 $pt[] = $xc+$w*$cossa; 356 $pt[] = $yc-$h*$sinsa; 357 $pt[] = $xc; 358 $pt[] = $yc; 359 360 } 361 } 362 else { // sa > 0 && ea < 90 363 364 $p = array($xc,$yc,$xc,$yc+$z, 365 $xc+$w*$cossa,$z+$yc-$h*$sinsa, 366 $xc+$w*$cossa,$yc-$h*$sinsa, 367 $xc,$yc); 368 369 $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa); 370 371 for( $a=$rsa; $a < $rea; $a += $step ) { 372 $pt[] = $xc + $w*cos($a); 373 $pt[] = $yc - $h*sin($a); 374 } 375 376 $pt[] = $xc+$w*$cosea; 377 $pt[] = $yc-$h*$sinea; 378 $pt[] = $xc; 379 $pt[] = $yc; 380 } 381 382 $img->PushColor($fillcolor.":".$shadow); 383 $img->FilledPolygon($p); 384 $img->PopColor(); 385 386 $img->PushColor($fillcolor); 387 $img->FilledPolygon($pt); 388 $img->PopColor(); 393 389 } 394 390 395 391 function SetStartAngle($aStart) { 396 397 398 399 400 } 401 402 // Draw a 3D Pie392 if( $aStart < 0 || $aStart > 360 ) { 393 JpGraphError::RaiseL(14004);//('Slice start angle must be between 0 and 360 degrees.'); 394 } 395 $this->startangle = $aStart; 396 } 397 398 // Draw a 3D Pie 403 399 function Pie3D($aaoption,$img,$data,$colors,$xc,$yc,$d,$angle,$z, 404 $shadow=0.65,$startangle=0,$edgecolor="",$edgeweight=1) { 405 406 //--------------------------------------------------------------------------- 407 // As usual the algorithm get more complicated than I originally 408 // envisioned. I believe that this is as simple as it is possible 409 // to do it with the features I want. It's a good exercise to start 410 // thinking on how to do this to convince your self that all this 411 // is really needed for the general case. 412 // 413 // The algorithm two draw 3D pies without "real 3D" is done in 414 // two steps. 415 // First imagine the pie cut in half through a thought line between 416 // 12'a clock and 6'a clock. It now easy to imagine that we can plot 417 // the individual slices for each half by starting with the topmost 418 // pie slice and continue down to 6'a clock. 419 // 420 // In the algortithm this is done in three principal steps 421 // Step 1. Do the knife cut to ensure by splitting slices that extends 422 // over the cut line. This is done by splitting the original slices into 423 // upto 3 subslices. 424 // Step 2. Find the top slice for each half 425 // Step 3. Draw the slices from top to bottom 426 // 427 // The thing that slightly complicates this scheme with all the 428 // angle comparisons below is that we can have an arbitrary start 429 // angle so we must take into account the different equivalence classes. 430 // For the same reason we must walk through the angle array in a 431 // modulo fashion. 432 // 433 // Limitations of algorithm: 434 // * A small exploded slice which crosses the 270 degree point 435 // will get slightly nagged close to the center due to the fact that 436 // we print the slices in Z-order and that the slice left part 437 // get printed first and might get slightly nagged by a larger 438 // slice on the right side just before the right part of the small 439 // slice. Not a major problem though. 440 //--------------------------------------------------------------------------- 441 442 443 // Determine the height of the ellippse which gives an 444 // indication of the inclination angle 445 $h = ($angle/90.0)*$d; 446 $sum = 0; 447 for($i=0; $i<count($data); ++$i ) { 448 $sum += $data[$i]; 449 } 450 451 // Special optimization 452 if( $sum==0 ) return; 453 454 if( $this->labeltype == 2 ) { 455 $this->adjusted_data = $this->AdjPercentage($data); 456 } 457 458 // Setup the start 459 $accsum = 0; 460 $a = $startangle; 461 $a = $this->NormAngle($a); 462 463 // 464 // Step 1 . Split all slices that crosses 90 or 270 465 // 466 $idx=0; 467 $adjexplode=array(); 468 $numcolors = count($colors); 469 for($i=0; $i<count($data); ++$i, ++$idx ) { 470 $da = $data[$i]/$sum * 360; 471 472 if( empty($this->explode_radius[$i]) ) 473 $this->explode_radius[$i]=0; 474 475 $expscale=1; 476 if( $aaoption == 1 ) 477 $expscale=2; 478 479 $la = $a + $da/2; 480 $explode = array( $xc + $this->explode_radius[$i]*cos($la*M_PI/180)*$expscale, 481 $yc - $this->explode_radius[$i]*sin($la*M_PI/180) * ($h/$d) *$expscale ); 482 $adjexplode[$idx] = $explode; 483 $labeldata[$i] = array($la,$explode[0],$explode[1]); 484 $originalangles[$i] = array($a,$a+$da); 485 486 $ne = $this->NormAngle($a+$da); 487 if( $da <= 180 ) { 488 // If the slice size is <= 90 it can at maximum cut across 489 // one boundary (either 90 or 270) where it needs to be split 490 $split=-1; // no split 491 if( ($da<=90 && ($a <= 90 && $ne > 90)) || 492 (($da <= 180 && $da >90) && (($a < 90 || $a >= 270) && $ne > 90)) ) { 493 $split = 90; 494 } 495 elseif( ($da<=90 && ($a <= 270 && $ne > 270)) || 496 (($da<=180 && $da>90) && ($a >= 90 && $a < 270 && ($a+$da) > 270 )) ) { 497 $split = 270; 498 } 499 if( $split > 0 ) { // split in two 500 $angles[$idx] = array($a,$split); 501 $adjcolors[$idx] = $colors[$i % $numcolors]; 502 $adjexplode[$idx] = $explode; 503 $angles[++$idx] = array($split,$ne); 504 $adjcolors[$idx] = $colors[$i % $numcolors]; 505 $adjexplode[$idx] = $explode; 506 } 507 else { // no split 508 $angles[$idx] = array($a,$ne); 509 $adjcolors[$idx] = $colors[$i % $numcolors]; 510 $adjexplode[$idx] = $explode; 511 } 512 } 513 else { 514 // da>180 515 // Slice may, depending on position, cross one or two 516 // bonudaries 517 518 if( $a < 90 ) 519 $split = 90; 520 elseif( $a <= 270 ) 521 $split = 270; 522 else 523 $split = 90; 524 525 $angles[$idx] = array($a,$split); 526 $adjcolors[$idx] = $colors[$i % $numcolors]; 527 $adjexplode[$idx] = $explode; 528 //if( $a+$da > 360-$split ) { 529 // For slices larger than 270 degrees we might cross 530 // another boundary as well. This means that we must 531 // split the slice further. The comparison gets a little 532 // bit complicated since we must take into accound that 533 // a pie might have a startangle >0 and hence a slice might 534 // wrap around the 0 angle. 535 // Three cases: 536 // a) Slice starts before 90 and hence gets a split=90, but 537 // we must also check if we need to split at 270 538 // b) Slice starts after 90 but before 270 and slices 539 // crosses 90 (after a wrap around of 0) 540 // c) If start is > 270 (hence the firstr split is at 90) 541 // and the slice is so large that it goes all the way 542 // around 270. 543 if( ($a < 90 && ($a+$da > 270)) || 544 ($a > 90 && $a<=270 && ($a+$da>360+90) ) || 545 ($a > 270 && $this->NormAngle($a+$da)>270) ) { 546 $angles[++$idx] = array($split,360-$split); 547 $adjcolors[$idx] = $colors[$i % $numcolors]; 548 $adjexplode[$idx] = $explode; 549 $angles[++$idx] = array(360-$split,$ne); 550 $adjcolors[$idx] = $colors[$i % $numcolors]; 551 $adjexplode[$idx] = $explode; 552 } 553 else { 554 // Just a simple split to the previous decided 555 // angle. 556 $angles[++$idx] = array($split,$ne); 557 $adjcolors[$idx] = $colors[$i % $numcolors]; 558 $adjexplode[$idx] = $explode; 559 } 560 } 561 $a += $da; 562 $a = $this->NormAngle($a); 563 } 564 565 // Total number of slices 566 $n = count($angles); 567 568 for($i=0; $i<$n; ++$i) { 569 list($dbgs,$dbge) = $angles[$i]; 570 } 571 572 // 573 // Step 2. Find start index (first pie that starts in upper left quadrant) 574 // 575 $minval = $angles[0][0]; 576 $min = 0; 577 for( $i=0; $i<$n; ++$i ) { 578 if( $angles[$i][0] < $minval ) { 579 $minval = $angles[$i][0]; 580 $min = $i; 581 } 582 } 583 $j = $min; 584 $cnt = 0; 585 while( $angles[$j][1] <= 90 ) { 586 $j++; 587 if( $j>=$n) { 588 $j=0; 589 } 590 if( $cnt > $n ) { 591 JpGraphError::RaiseL(14005); 592 //("Pie3D Internal error (#1). Trying to wrap twice when looking for start index"); 593 } 594 ++$cnt; 595 } 596 $start = $j; 597 598 // 599 // Step 3. Print slices in z-order 600 // 601 $cnt = 0; 602 603 // First stroke all the slices between 90 and 270 (left half circle) 604 // counterclockwise 605 606 while( $angles[$j][0] < 270 && $aaoption !== 2 ) { 607 608 list($x,$y) = $adjexplode[$j]; 609 610 $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1], 611 $z,$adjcolors[$j],$shadow); 612 613 $last = array($x,$y,$j); 614 615 $j++; 616 if( $j >= $n ) $j=0; 617 if( $cnt > $n ) { 618 JpGraphError::RaiseL(14006); 619 //("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking."); 620 } 621 ++$cnt; 622 } 623 624 $slice_left = $n-$cnt; 625 $j=$start-1; 626 if($j<0) $j=$n-1; 627 $cnt = 0; 628 629 // The stroke all slices from 90 to -90 (right half circle) 630 // clockwise 631 while( $cnt < $slice_left && $aaoption !== 2 ) { 632 633 list($x,$y) = $adjexplode[$j]; 634 635 $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1], 636 $z,$adjcolors[$j],$shadow); 637 $j--; 638 if( $cnt > $n ) { 639 JpGraphError::RaiseL(14006); 640 //("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking."); 641 } 642 if($j<0) $j=$n-1; 643 $cnt++; 644 } 645 646 // Now do a special thing. Stroke the last slice on the left 647 // halfcircle one more time. This is needed in the case where 648 // the slice close to 270 have been exploded. In that case the 649 // part of the slice close to the center of the pie might be 650 // slightly nagged. 651 if( $aaoption !== 2 ) 652 $this->Pie3DSlice($img,$last[0],$last[1],$d,$h,$angles[$last[2]][0], 653 $angles[$last[2]][1],$z,$adjcolors[$last[2]],$shadow); 654 655 656 if( $aaoption !== 1 ) { 657 // Now print possible labels and add csim 658 $this->value->ApplyFont($img); 659 $margin = $img->GetFontHeight()/2 + $this->value->margin ; 660 for($i=0; $i < count($data); ++$i ) { 661 $la = $labeldata[$i][0]; 662 $x = $labeldata[$i][1] + cos($la*M_PI/180)*($d+$margin)*$this->ilabelposadj; 663 $y = $labeldata[$i][2] - sin($la*M_PI/180)*($h+$margin)*$this->ilabelposadj; 664 if( $this->ilabelposadj >= 1.0 ) { 665 if( $la > 180 && $la < 360 ) $y += $z; 666 } 667 if( $this->labeltype == 0 ) { 668 if( $sum > 0 ) 669 $l = 100*$data[$i]/$sum; 670 else 671 $l = 0; 672 } 673 elseif( $this->labeltype == 1 ) { 674 $l = $data[$i]; 675 } 676 else { 677 $l = $this->adjusted_data[$i]; 678 } 679 if( isset($this->labels[$i]) && is_string($this->labels[$i]) ) 680 $l=sprintf($this->labels[$i],$l); 681 682 $this->StrokeLabels($l,$img,$labeldata[$i][0]*M_PI/180,$x,$y,$z); 683 684 $this->Add3DSliceToCSIM($i,$labeldata[$i][1],$labeldata[$i][2],$h*2,$d*2,$z, 685 $originalangles[$i][0],$originalangles[$i][1]); 686 } 687 } 688 689 // 690 // Finally add potential lines in pie 691 // 692 693 if( $edgecolor=="" || $aaoption !== 0 ) return; 694 695 $accsum = 0; 696 $a = $startangle; 697 $a = $this->NormAngle($a); 698 699 $a *= M_PI/180.0; 700 701 $idx=0; 702 $img->PushColor($edgecolor); 703 $img->SetLineWeight($edgeweight); 704 705 $fulledge = true; 706 for($i=0; $i < count($data) && $fulledge; ++$i ) { 707 if( empty($this->explode_radius[$i]) ) 708 $this->explode_radius[$i]=0; 709 if( $this->explode_radius[$i] > 0 ) { 710 $fulledge = false; 711 } 712 } 713 714 715 for($i=0; $i < count($data); ++$i, ++$idx ) { 716 717 $da = $data[$i]/$sum * 2*M_PI; 718 $this->StrokeFullSliceFrame($img,$xc,$yc,$a,$a+$da,$d,$h,$z,$edgecolor, 719 $this->explode_radius[$i],$fulledge); 720 $a += $da; 721 } 722 $img->PopColor(); 400 $shadow=0.65,$startangle=0,$edgecolor="",$edgeweight=1) { 401 402 //--------------------------------------------------------------------------- 403 // As usual the algorithm get more complicated than I originally 404 // envisioned. I believe that this is as simple as it is possible 405 // to do it with the features I want. It's a good exercise to start 406 // thinking on how to do this to convince your self that all this 407 // is really needed for the general case. 408 // 409 // The algorithm two draw 3D pies without "real 3D" is done in 410 // two steps. 411 // First imagine the pie cut in half through a thought line between 412 // 12'a clock and 6'a clock. It now easy to imagine that we can plot 413 // the individual slices for each half by starting with the topmost 414 // pie slice and continue down to 6'a clock. 415 // 416 // In the algortithm this is done in three principal steps 417 // Step 1. Do the knife cut to ensure by splitting slices that extends 418 // over the cut line. This is done by splitting the original slices into 419 // upto 3 subslices. 420 // Step 2. Find the top slice for each half 421 // Step 3. Draw the slices from top to bottom 422 // 423 // The thing that slightly complicates this scheme with all the 424 // angle comparisons below is that we can have an arbitrary start 425 // angle so we must take into account the different equivalence classes. 426 // For the same reason we must walk through the angle array in a 427 // modulo fashion. 428 // 429 // Limitations of algorithm: 430 // * A small exploded slice which crosses the 270 degree point 431 // will get slightly nagged close to the center due to the fact that 432 // we print the slices in Z-order and that the slice left part 433 // get printed first and might get slightly nagged by a larger 434 // slice on the right side just before the right part of the small 435 // slice. Not a major problem though. 436 //--------------------------------------------------------------------------- 437 438 439 // Determine the height of the ellippse which gives an 440 // indication of the inclination angle 441 $h = ($angle/90.0)*$d; 442 $sum = 0; 443 for($i=0; $i<count($data); ++$i ) { 444 $sum += $data[$i]; 445 } 446 447 // Special optimization 448 if( $sum==0 ) return; 449 450 if( $this->labeltype == 2 ) { 451 $this->adjusted_data = $this->AdjPercentage($data); 452 } 453 454 // Setup the start 455 $accsum = 0; 456 $a = $startangle; 457 $a = $this->NormAngle($a); 458 459 // 460 // Step 1 . Split all slices that crosses 90 or 270 461 // 462 $idx=0; 463 $adjexplode=array(); 464 $numcolors = count($colors); 465 for($i=0; $i<count($data); ++$i, ++$idx ) { 466 $da = $data[$i]/$sum * 360; 467 468 if( empty($this->explode_radius[$i]) ) { 469 $this->explode_radius[$i]=0; 470 } 471 472 $expscale=1; 473 if( $aaoption == 1 ) { 474 $expscale=2; 475 } 476 477 $la = $a + $da/2; 478 $explode = array( $xc + $this->explode_radius[$i]*cos($la*M_PI/180)*$expscale, 479 $yc - $this->explode_radius[$i]*sin($la*M_PI/180) * ($h/$d) *$expscale ); 480 $adjexplode[$idx] = $explode; 481 $labeldata[$i] = array($la,$explode[0],$explode[1]); 482 $originalangles[$i] = array($a,$a+$da); 483 484 $ne = $this->NormAngle($a+$da); 485 if( $da <= 180 ) { 486 // If the slice size is <= 90 it can at maximum cut across 487 // one boundary (either 90 or 270) where it needs to be split 488 $split=-1; // no split 489 if( ($da<=90 && ($a <= 90 && $ne > 90)) || 490 (($da <= 180 && $da >90) && (($a < 90 || $a >= 270) && $ne > 90)) ) { 491 $split = 90; 492 } 493 elseif( ($da<=90 && ($a <= 270 && $ne > 270)) || 494 (($da<=180 && $da>90) && ($a >= 90 && $a < 270 && ($a+$da) > 270 )) ) { 495 $split = 270; 496 } 497 if( $split > 0 ) { // split in two 498 $angles[$idx] = array($a,$split); 499 $adjcolors[$idx] = $colors[$i % $numcolors]; 500 $adjexplode[$idx] = $explode; 501 $angles[++$idx] = array($split,$ne); 502 $adjcolors[$idx] = $colors[$i % $numcolors]; 503 $adjexplode[$idx] = $explode; 504 } 505 else { // no split 506 $angles[$idx] = array($a,$ne); 507 $adjcolors[$idx] = $colors[$i % $numcolors]; 508 $adjexplode[$idx] = $explode; 509 } 510 } 511 else { 512 // da>180 513 // Slice may, depending on position, cross one or two 514 // bonudaries 515 516 if( $a < 90 ) $split = 90; 517 elseif( $a <= 270 ) $split = 270; 518 else $split = 90; 519 520 $angles[$idx] = array($a,$split); 521 $adjcolors[$idx] = $colors[$i % $numcolors]; 522 $adjexplode[$idx] = $explode; 523 //if( $a+$da > 360-$split ) { 524 // For slices larger than 270 degrees we might cross 525 // another boundary as well. This means that we must 526 // split the slice further. The comparison gets a little 527 // bit complicated since we must take into accound that 528 // a pie might have a startangle >0 and hence a slice might 529 // wrap around the 0 angle. 530 // Three cases: 531 // a) Slice starts before 90 and hence gets a split=90, but 532 // we must also check if we need to split at 270 533 // b) Slice starts after 90 but before 270 and slices 534 // crosses 90 (after a wrap around of 0) 535 // c) If start is > 270 (hence the firstr split is at 90) 536 // and the slice is so large that it goes all the way 537 // around 270. 538 if( ($a < 90 && ($a+$da > 270)) || ($a > 90 && $a<=270 && ($a+$da>360+90) ) || ($a > 270 && $this->NormAngle($a+$da)>270) ) { 539 $angles[++$idx] = array($split,360-$split); 540 $adjcolors[$idx] = $colors[$i % $numcolors]; 541 $adjexplode[$idx] = $explode; 542 $angles[++$idx] = array(360-$split,$ne); 543 $adjcolors[$idx] = $colors[$i % $numcolors]; 544 $adjexplode[$idx] = $explode; 545 } 546 else { 547 // Just a simple split to the previous decided 548 // angle. 549 $angles[++$idx] = array($split,$ne); 550 $adjcolors[$idx] = $colors[$i % $numcolors]; 551 $adjexplode[$idx] = $explode; 552 } 553 } 554 $a += $da; 555 $a = $this->NormAngle($a); 556 } 557 558 // Total number of slices 559 $n = count($angles); 560 561 for($i=0; $i<$n; ++$i) { 562 list($dbgs,$dbge) = $angles[$i]; 563 } 564 565 // 566 // Step 2. Find start index (first pie that starts in upper left quadrant) 567 // 568 $minval = $angles[0][0]; 569 $min = 0; 570 for( $i=0; $i<$n; ++$i ) { 571 if( $angles[$i][0] < $minval ) { 572 $minval = $angles[$i][0]; 573 $min = $i; 574 } 575 } 576 $j = $min; 577 $cnt = 0; 578 while( $angles[$j][1] <= 90 ) { 579 $j++; 580 if( $j>=$n) { 581 $j=0; 582 } 583 if( $cnt > $n ) { 584 JpGraphError::RaiseL(14005); 585 //("Pie3D Internal error (#1). Trying to wrap twice when looking for start index"); 586 } 587 ++$cnt; 588 } 589 $start = $j; 590 591 // 592 // Step 3. Print slices in z-order 593 // 594 $cnt = 0; 595 596 // First stroke all the slices between 90 and 270 (left half circle) 597 // counterclockwise 598 599 while( $angles[$j][0] < 270 && $aaoption !== 2 ) { 600 601 list($x,$y) = $adjexplode[$j]; 602 603 $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1], 604 $z,$adjcolors[$j],$shadow); 605 606 $last = array($x,$y,$j); 607 608 $j++; 609 if( $j >= $n ) $j=0; 610 if( $cnt > $n ) { 611 JpGraphError::RaiseL(14006); 612 //("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking."); 613 } 614 ++$cnt; 615 } 616 617 $slice_left = $n-$cnt; 618 $j=$start-1; 619 if($j<0) $j=$n-1; 620 $cnt = 0; 621 622 // The stroke all slices from 90 to -90 (right half circle) 623 // clockwise 624 while( $cnt < $slice_left && $aaoption !== 2 ) { 625 626 list($x,$y) = $adjexplode[$j]; 627 628 $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1], 629 $z,$adjcolors[$j],$shadow); 630 $j--; 631 if( $cnt > $n ) { 632 JpGraphError::RaiseL(14006); 633 //("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking."); 634 } 635 if($j<0) $j=$n-1; 636 $cnt++; 637 } 638 639 // Now do a special thing. Stroke the last slice on the left 640 // halfcircle one more time. This is needed in the case where 641 // the slice close to 270 have been exploded. In that case the 642 // part of the slice close to the center of the pie might be 643 // slightly nagged. 644 if( $aaoption !== 2 ) 645 $this->Pie3DSlice($img,$last[0],$last[1],$d,$h,$angles[$last[2]][0], 646 $angles[$last[2]][1],$z,$adjcolors[$last[2]],$shadow); 647 648 649 if( $aaoption !== 1 ) { 650 // Now print possible labels and add csim 651 $this->value->ApplyFont($img); 652 $margin = $img->GetFontHeight()/2 + $this->value->margin ; 653 for($i=0; $i < count($data); ++$i ) { 654 $la = $labeldata[$i][0]; 655 $x = $labeldata[$i][1] + cos($la*M_PI/180)*($d+$margin)*$this->ilabelposadj; 656 $y = $labeldata[$i][2] - sin($la*M_PI/180)*($h+$margin)*$this->ilabelposadj; 657 if( $this->ilabelposadj >= 1.0 ) { 658 if( $la > 180 && $la < 360 ) $y += $z; 659 } 660 if( $this->labeltype == 0 ) { 661 if( $sum > 0 ) $l = 100*$data[$i]/$sum; 662 else $l = 0; 663 } 664 elseif( $this->labeltype == 1 ) { 665 $l = $data[$i]; 666 } 667 else { 668 $l = $this->adjusted_data[$i]; 669 } 670 if( isset($this->labels[$i]) && is_string($this->labels[$i]) ) { 671 $l=sprintf($this->labels[$i],$l); 672 } 673 674 $this->StrokeLabels($l,$img,$labeldata[$i][0]*M_PI/180,$x,$y,$z); 675 676 $this->Add3DSliceToCSIM($i,$labeldata[$i][1],$labeldata[$i][2],$h*2,$d*2,$z, 677 $originalangles[$i][0],$originalangles[$i][1]); 678 } 679 } 680 681 // 682 // Finally add potential lines in pie 683 // 684 685 if( $edgecolor=="" || $aaoption !== 0 ) return; 686 687 $accsum = 0; 688 $a = $startangle; 689 $a = $this->NormAngle($a); 690 691 $a *= M_PI/180.0; 692 693 $idx=0; 694 $img->PushColor($edgecolor); 695 $img->SetLineWeight($edgeweight); 696 697 $fulledge = true; 698 for($i=0; $i < count($data) && $fulledge; ++$i ) { 699 if( empty($this->explode_radius[$i]) ) { 700 $this->explode_radius[$i]=0; 701 } 702 if( $this->explode_radius[$i] > 0 ) { 703 $fulledge = false; 704 } 705 } 706 707 708 for($i=0; $i < count($data); ++$i, ++$idx ) { 709 710 $da = $data[$i]/$sum * 2*M_PI; 711 $this->StrokeFullSliceFrame($img,$xc,$yc,$a,$a+$da,$d,$h,$z,$edgecolor, 712 $this->explode_radius[$i],$fulledge); 713 $a += $da; 714 } 715 $img->PopColor(); 723 716 } 724 717 725 718 function StrokeFullSliceFrame($img,$xc,$yc,$sa,$ea,$w,$h,$z,$edgecolor,$exploderadius,$fulledge) { 726 $step = 0.02; 727 728 if( $exploderadius > 0 ) { 729 $la = ($sa+$ea)/2; 730 $xc += $exploderadius*cos($la); 731 $yc -= $exploderadius*sin($la) * ($h/$w) ; 732 733 } 734 735 $p = array($xc,$yc,$xc+$w*cos($sa),$yc-$h*sin($sa)); 736 737 for($a=$sa; $a < $ea; $a += $step ) { 738 $p[] = $xc + $w*cos($a); 739 $p[] = $yc - $h*sin($a); 740 } 741 742 $p[] = $xc+$w*cos($ea); 743 $p[] = $yc-$h*sin($ea); 744 $p[] = $xc; 745 $p[] = $yc; 746 747 $img->SetColor($edgecolor); 748 $img->Polygon($p); 749 750 // Unfortunately we can't really draw the full edge around the whole of 751 // of the slice if any of the slices are exploded. The reason is that 752 // this algorithm is to simply. There are cases where the edges will 753 // "overwrite" other slices when they have been exploded. 754 // Doing the full, proper 3D hidden lines stiff is actually quite 755 // tricky. So for exploded pies we only draw the top edge. Not perfect 756 // but the "real" solution is much more complicated. 757 if( $fulledge && !( $sa > 0 && $sa < M_PI && $ea < M_PI) ) { 758 759 if($sa < M_PI && $ea > M_PI) 760 $sa = M_PI; 761 762 if($sa < 2*M_PI && (($ea >= 2*M_PI) || ($ea > 0 && $ea < $sa ) ) ) 763 $ea = 2*M_PI; 764 765 if( $sa >= M_PI && $ea <= 2*M_PI ) { 766 $p = array($xc + $w*cos($sa),$yc - $h*sin($sa), 767 $xc + $w*cos($sa),$z + $yc - $h*sin($sa)); 768 769 for($a=$sa+$step; $a < $ea; $a += $step ) { 770 $p[] = $xc + $w*cos($a); 771 $p[] = $z + $yc - $h*sin($a); 772 } 773 $p[] = $xc + $w*cos($ea); 774 $p[] = $z + $yc - $h*sin($ea); 775 $p[] = $xc + $w*cos($ea); 776 $p[] = $yc - $h*sin($ea); 777 $img->SetColor($edgecolor); 778 $img->Polygon($p); 779 } 780 } 719 $step = 0.02; 720 721 if( $exploderadius > 0 ) { 722 $la = ($sa+$ea)/2; 723 $xc += $exploderadius*cos($la); 724 $yc -= $exploderadius*sin($la) * ($h/$w) ; 725 726 } 727 728 $p = array($xc,$yc,$xc+$w*cos($sa),$yc-$h*sin($sa)); 729 730 for($a=$sa; $a < $ea; $a += $step ) { 731 $p[] = $xc + $w*cos($a); 732 $p[] = $yc - $h*sin($a); 733 } 734 735 $p[] = $xc+$w*cos($ea); 736 $p[] = $yc-$h*sin($ea); 737 $p[] = $xc; 738 $p[] = $yc; 739 740 $img->SetColor($edgecolor); 741 $img->Polygon($p); 742 743 // Unfortunately we can't really draw the full edge around the whole of 744 // of the slice if any of the slices are exploded. The reason is that 745 // this algorithm is to simply. There are cases where the edges will 746 // "overwrite" other slices when they have been exploded. 747 // Doing the full, proper 3D hidden lines stiff is actually quite 748 // tricky. So for exploded pies we only draw the top edge. Not perfect 749 // but the "real" solution is much more complicated. 750 if( $fulledge && !( $sa > 0 && $sa < M_PI && $ea < M_PI) ) { 751 752 if($sa < M_PI && $ea > M_PI) { 753 $sa = M_PI; 754 } 755 756 if($sa < 2*M_PI && (($ea >= 2*M_PI) || ($ea > 0 && $ea < $sa ) ) ) { 757 $ea = 2*M_PI; 758 } 759 760 if( $sa >= M_PI && $ea <= 2*M_PI ) { 761 $p = array($xc + $w*cos($sa),$yc - $h*sin($sa), 762 $xc + $w*cos($sa),$z + $yc - $h*sin($sa)); 763 764 for($a=$sa+$step; $a < $ea; $a += $step ) { 765 $p[] = $xc + $w*cos($a); 766 $p[] = $z + $yc - $h*sin($a); 767 } 768 $p[] = $xc + $w*cos($ea); 769 $p[] = $z + $yc - $h*sin($ea); 770 $p[] = $xc + $w*cos($ea); 771 $p[] = $yc - $h*sin($ea); 772 $img->SetColor($edgecolor); 773 $img->Polygon($p); 774 } 775 } 781 776 } 782 777 783 778 function Stroke($img,$aaoption=0) { 784 $n = count($this->data); 785 786 // If user hasn't set the colors use the theme array 787 if( $this->setslicecolors==null ) { 788 $colors = array_keys($img->rgb->rgb_table); 789 sort($colors); 790 $idx_a=$this->themearr[$this->theme]; 791 $ca = array(); 792 $m = count($idx_a); 793 for($i=0; $i < $m; ++$i) 794 $ca[$i] = $colors[$idx_a[$i]]; 795 $ca = array_reverse(array_slice($ca,0,$n)); 796 } 797 else { 798 $ca = $this->setslicecolors; 799 } 800 801 802 if( $this->posx <= 1 && $this->posx > 0 ) 803 $xc = round($this->posx*$img->width); 804 else 805 $xc = $this->posx ; 806 807 if( $this->posy <= 1 && $this->posy > 0 ) 808 $yc = round($this->posy*$img->height); 809 else 810 $yc = $this->posy ; 811 812 if( $this->radius <= 1 ) { 813 $width = floor($this->radius*min($img->width,$img->height)); 814 // Make sure that the pie doesn't overflow the image border 815 // The 0.9 factor is simply an extra margin to leave some space 816 // between the pie an the border of the image. 817 $width = min($width,min($xc*0.9,($yc*90/$this->angle-$width/4)*0.9)); 818 } 819 else { 820 $width = $this->radius * ($aaoption === 1 ? 2 : 1 ) ; 821 } 822 823 // Add a sanity check for width 824 if( $width < 1 ) { 825 JpGraphError::RaiseL(14007);//("Width for 3D Pie is 0. Specify a size > 0"); 826 } 827 828 // Establish a thickness. By default the thickness is a fifth of the 829 // pie slice width (=pie radius) but since the perspective depends 830 // on the inclination angle we use some heuristics to make the edge 831 // slightly thicker the less the angle. 832 833 // Has user specified an absolute thickness? In that case use 834 // that instead 835 836 if( $this->iThickness ) { 837 $thick = $this->iThickness; 838 $thick *= ($aaoption === 1 ? 2 : 1 ); 839 } 840 else 841 $thick = $width/12; 842 $a = $this->angle; 843 if( $a <= 30 ) $thick *= 1.6; 844 elseif( $a <= 40 ) $thick *= 1.4; 845 elseif( $a <= 50 ) $thick *= 1.2; 846 elseif( $a <= 60 ) $thick *= 1.0; 847 elseif( $a <= 70 ) $thick *= 0.8; 848 elseif( $a <= 80 ) $thick *= 0.7; 849 else $thick *= 0.6; 850 851 $thick = floor($thick); 852 853 if( $this->explode_all ) 854 for($i=0; $i < $n; ++$i) 855 $this->explode_radius[$i]=$this->explode_r; 856 857 $this->Pie3D($aaoption,$img,$this->data, $ca, $xc, $yc, $width, $this->angle, 858 $thick, 0.65, $this->startangle, $this->edgecolor, $this->edgeweight); 859 860 // Adjust title position 861 if( $aaoption != 1 ) { 862 $this->title->SetPos($xc,$yc-$this->title->GetFontHeight($img)-$width/2-$this->title->margin, "center","bottom"); 863 $this->title->Stroke($img); 864 } 865 } 866 867 //--------------- 868 // PRIVATE METHODS 779 $n = count($this->data); 780 781 // If user hasn't set the colors use the theme array 782 if( $this->setslicecolors==null ) { 783 $colors = array_keys($img->rgb->rgb_table); 784 sort($colors); 785 $idx_a=$this->themearr[$this->theme]; 786 $ca = array(); 787 $m = count($idx_a); 788 for($i=0; $i < $m; ++$i) { 789 $ca[$i] = $colors[$idx_a[$i]]; 790 } 791 $ca = array_reverse(array_slice($ca,0,$n)); 792 } 793 else { 794 $ca = $this->setslicecolors; 795 } 796 797 798 if( $this->posx <= 1 && $this->posx > 0 ) { 799 $xc = round($this->posx*$img->width); 800 } 801 else { 802 $xc = $this->posx ; 803 } 804 805 if( $this->posy <= 1 && $this->posy > 0 ) { 806 $yc = round($this->posy*$img->height); 807 } 808 else { 809 $yc = $this->posy ; 810 } 811 812 if( $this->radius <= 1 ) { 813 $width = floor($this->radius*min($img->width,$img->height)); 814 // Make sure that the pie doesn't overflow the image border 815 // The 0.9 factor is simply an extra margin to leave some space 816 // between the pie an the border of the image. 817 $width = min($width,min($xc*0.9,($yc*90/$this->angle-$width/4)*0.9)); 818 } 819 else { 820 $width = $this->radius * ($aaoption === 1 ? 2 : 1 ) ; 821 } 822 823 // Add a sanity check for width 824 if( $width < 1 ) { 825 JpGraphError::RaiseL(14007);//("Width for 3D Pie is 0. Specify a size > 0"); 826 } 827 828 // Establish a thickness. By default the thickness is a fifth of the 829 // pie slice width (=pie radius) but since the perspective depends 830 // on the inclination angle we use some heuristics to make the edge 831 // slightly thicker the less the angle. 832 833 // Has user specified an absolute thickness? In that case use 834 // that instead 835 836 if( $this->iThickness ) { 837 $thick = $this->iThickness; 838 $thick *= ($aaoption === 1 ? 2 : 1 ); 839 } 840 else { 841 $thick = $width/12; 842 } 843 $a = $this->angle; 844 845 if( $a <= 30 ) $thick *= 1.6; 846 elseif( $a <= 40 ) $thick *= 1.4; 847 elseif( $a <= 50 ) $thick *= 1.2; 848 elseif( $a <= 60 ) $thick *= 1.0; 849 elseif( $a <= 70 ) $thick *= 0.8; 850 elseif( $a <= 80 ) $thick *= 0.7; 851 else $thick *= 0.6; 852 853 $thick = floor($thick); 854 855 if( $this->explode_all ) { 856 for($i=0; $i < $n; ++$i) 857 $this->explode_radius[$i]=$this->explode_r; 858 } 859 860 $this->Pie3D($aaoption,$img,$this->data, $ca, $xc, $yc, $width, $this->angle, 861 $thick, 0.65, $this->startangle, $this->edgecolor, $this->edgeweight); 862 863 // Adjust title position 864 if( $aaoption != 1 ) { 865 $this->title->SetPos($xc,$yc-$this->title->GetFontHeight($img)-$width/2-$this->title->margin, "center","bottom"); 866 $this->title->Stroke($img); 867 } 868 } 869 870 //--------------- 871 // PRIVATE METHODS 869 872 870 873 // Position the labels of each slice 871 874 function StrokeLabels($label,$img,$a,$xp,$yp,$z) { 872 $this->value->halign="left"; 873 $this->value->valign="top"; 874 875 // Position the axis title. 876 // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text 877 // that intersects with the extension of the corresponding axis. The code looks a little 878 // bit messy but this is really the only way of having a reasonable position of the 879 // axis titles. 880 $this->value->ApplyFont($img); 881 $h=$img->GetTextHeight($label); 882 // For numeric values the format of the display value 883 // must be taken into account 884 if( is_numeric($label) ) { 885 if( $label >= 0 ) 886 $w=$img->GetTextWidth(sprintf($this->value->format,$label)); 887 else 888 $w=$img->GetTextWidth(sprintf($this->value->negformat,$label)); 889 } 890 else 891 $w=$img->GetTextWidth($label); 892 while( $a > 2*M_PI ) $a -= 2*M_PI; 893 if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0; 894 if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI; 895 if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1; 896 if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI); 897 898 if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI; 899 if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI); 900 if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1; 901 if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI); 902 if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0; 903 904 $x = round($xp-$dx*$w); 905 $y = round($yp-$dy*$h); 906 907 908 // Mark anchor point for debugging 909 /* 910 $img->SetColor('red'); 911 $img->Line($xp-10,$yp,$xp+10,$yp); 912 $img->Line($xp,$yp-10,$xp,$yp+10); 913 */ 914 $oldmargin = $this->value->margin; 915 $this->value->margin=0; 916 $this->value->Stroke($img,$label,$x,$y); 917 $this->value->margin=$oldmargin; 918 919 } 875 $this->value->halign="left"; 876 $this->value->valign="top"; 877 878 // Position the axis title. 879 // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text 880 // that intersects with the extension of the corresponding axis. The code looks a little 881 // bit messy but this is really the only way of having a reasonable position of the 882 // axis titles. 883 $this->value->ApplyFont($img); 884 $h=$img->GetTextHeight($label); 885 // For numeric values the format of the display value 886 // must be taken into account 887 if( is_numeric($label) ) { 888 if( $label >= 0 ) { 889 $w=$img->GetTextWidth(sprintf($this->value->format,$label)); 890 } 891 else { 892 $w=$img->GetTextWidth(sprintf($this->value->negformat,$label)); 893 } 894 } 895 else { 896 $w=$img->GetTextWidth($label); 897 } 898 899 while( $a > 2*M_PI ) { 900 $a -= 2*M_PI; 901 } 902 903 if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0; 904 if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI; 905 if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1; 906 if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI); 907 908 if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI; 909 if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI); 910 if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1; 911 if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI); 912 if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0; 913 914 $x = round($xp-$dx*$w); 915 $y = round($yp-$dy*$h); 916 917 // Mark anchor point for debugging 918 /* 919 $img->SetColor('red'); 920 $img->Line($xp-10,$yp,$xp+10,$yp); 921 $img->Line($xp,$yp-10,$xp,$yp+10); 922 */ 923 924 $oldmargin = $this->value->margin; 925 $this->value->margin=0; 926 $this->value->Stroke($img,$label,$x,$y); 927 $this->value->margin=$oldmargin; 928 929 } 920 930 } // Class 921 931
Note:
See TracChangeset
for help on using the changeset viewer.