PageRenderTime 65ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/graphs/jpgraph/jpgraph_pie3d.php

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