[ Index ]
 

Code source de jpGraph 2.2

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/src/ -> jpgraph_pie3d.php (source)

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


Généré le : Sat Nov 24 09:27:55 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics