jpgraph_pie3d.php
上传用户:gzy2002
上传日期:2010-02-11
资源大小:1785k
文件大小:24k
源码类别:

电子政务应用

开发平台:

Java

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