📄 jpgraph_pie3d.php
字号:
<?php/*=======================================================================// File: JPGRAPH_PIE3D.PHP// Description: 3D Pie plot extension for JpGraph// Created: 2001-03-24// Author: Johan Persson (johanp@aditus.nu)// Ver: $Id: jpgraph_pie3d.php 571 2006-03-04 10:28:00Z ljp $//// Copyright (c) Aditus Consulting. All rights reserved.//========================================================================*///===================================================// CLASS PiePlot3D// Description: Plots a 3D pie with a specified projection // angle between 20 and 70 degrees.//===================================================class PiePlot3D extends PiePlot { private $labelhintcolor="red",$showlabelhint=true; private $angle=50; private $edgecolor="", $edgeweight=1; private $iThickness=false; //---------------// CONSTRUCTOR function PiePlot3d($data) { $this->radius = 0.5; $this->data = $data; $this->title = new Text(""); $this->title->SetFont(FF_FONT1,FS_BOLD); $this->value = new DisplayValue(); $this->value->Show(); $this->value->SetFormat('%.0f%%'); }//---------------// PUBLIC METHODS // Set label arrays function SetLegends($aLegend) { $this->legends = array_reverse(array_slice($aLegend,0,count($this->data))); } function SetSliceColors($aColors) { $this->setslicecolors = $aColors; } function Legend($aGraph) { parent::Legend($aGraph); $aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol); } function SetCSIMTargets($targets,$alts=null) { $this->csimtargets = $targets; $this->csimalts = $alts; } // Should the slices be separated by a line? If color is specified as "" no line // will be used to separate pie slices. function SetEdge($aColor='black',$aWeight=1) { $this->edgecolor = $aColor; $this->edgeweight = $aWeight; } // Dummy function to make Pie3D behave in a similair way to 2D function ShowBorder($exterior=true,$interior=true) { JpGraphError::RaiseL(14001);//('Pie3D::ShowBorder() . Deprecated function. Use Pie3D::SetEdge() to control the edges around slices.'); } // Specify projection angle for 3D in degrees // Must be between 20 and 70 degrees function SetAngle($a) { if( $a<5 || $a>90 ) JpGraphError::RaiseL(14002);//("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees."); else $this->angle = $a; } function Add3DSliceToCSIM($i,$xc,$yc,$height,$width,$thick,$sa,$ea) { //Slice number, ellipse centre (x,y), height, width, start angle, end angle $sa *= M_PI/180; $ea *= M_PI/180; //add coordinates of the centre to the map $coords = "$xc, $yc"; //add coordinates of the first point on the arc to the map $xp = floor($width*cos($sa)/2+$xc); $yp = floor($yc-$height*sin($sa)/2); $coords.= ", $xp, $yp"; //If on the front half, add the thickness offset if ($sa >= M_PI && $sa <= 2*M_PI*1.01) { $yp = floor($yp+$thick); $coords.= ", $xp, $yp"; } //add coordinates every 0.2 radians $a=$sa+0.2; while ($a<$ea) { $xp = floor($width*cos($a)/2+$xc); if ($a >= M_PI && $a <= 2*M_PI*1.01) { $yp = floor($yc-($height*sin($a)/2)+$thick); } else { $yp = floor($yc-$height*sin($a)/2); } $coords.= ", $xp, $yp"; $a += 0.2; } //Add the last point on the arc $xp = floor($width*cos($ea)/2+$xc); $yp = floor($yc-$height*sin($ea)/2); if ($ea >= M_PI && $ea <= 2*M_PI*1.01) { $coords.= ", $xp, ".floor($yp+$thick); } $coords.= ", $xp, $yp"; $alt=''; if( !empty($this->csimalts[$i]) ) { $tmp=sprintf($this->csimalts[$i],$this->data[$i]); $alt="alt=\"$tmp\" title=\"$tmp\""; } if( !empty($this->csimtargets[$i]) ) $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtargets[$i]."\" $alt />\n"; } function SetLabels($aLabels,$aLblPosAdj="auto") { $this->labels = $aLabels; $this->ilabelposadj=$aLblPosAdj; } // Distance from the pie to the labels function SetLabelMargin($m) { $this->value->SetMargin($m); } // Show a thin line from the pie to the label for a specific slice function ShowLabelHint($f=true) { $this->showlabelhint=$f; } // Set color of hint line to label for each slice function SetLabelHintColor($c) { $this->labelhintcolor=$c; } function SetHeight($aHeight) { $this->iThickness = $aHeight; }// Normalize Angle between 0-360 function NormAngle($a) { // Normalize anle to 0 to 2M_PI // if( $a > 0 ) { while($a > 360) $a -= 360; } else { while($a < 0) $a += 360; } if( $a < 0 ) $a = 360 + $a; if( $a == 360 ) $a=0; return $a; } // Draw one 3D pie slice at position ($xc,$yc) with height $z function Pie3DSlice($img,$xc,$yc,$w,$h,$sa,$ea,$z,$fillcolor,$shadow=0.65) { // Due to the way the 3D Pie algorithm works we are // guaranteed that any slice we get into this method // belongs to either the left or right side of the // pie ellipse. Hence, no slice will cross 90 or 270 // point. if( ($sa < 90 && $ea > 90) || ( ($sa > 90 && $sa < 270) && $ea > 270) ) { JpGraphError::RaiseL(14003);//('Internal assertion failed. Pie3D::Pie3DSlice'); exit(1); } $p[] = array(); // Setup pre-calculated values $rsa = $sa/180*M_PI; // to Rad $rea = $ea/180*M_PI; // to Rad $sinsa = sin($rsa); $cossa = cos($rsa); $sinea = sin($rea); $cosea = cos($rea); // p[] is the points for the overall slice and // pt[] is the points for the top pie // Angular step when approximating the arc with a polygon train. $step = 0.05; if( $sa >= 270 ) { if( $ea > 360 || ($ea > 0 && $ea <= 90) ) { if( $ea > 0 && $ea <= 90 ) { // Adjust angle to simplify conditions in loops $rea += 2*M_PI; } $p = array($xc,$yc,$xc,$yc+$z, $xc+$w*$cossa,$z+$yc-$h*$sinsa); $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa); for( $a=$rsa; $a < 2*M_PI; $a += $step ) { $tca = cos($a); $tsa = sin($a); $p[] = $xc+$w*$tca; $p[] = $z+$yc-$h*$tsa; $pt[] = $xc+$w*$tca; $pt[] = $yc-$h*$tsa; } $pt[] = $xc+$w; $pt[] = $yc; $p[] = $xc+$w; $p[] = $z+$yc; $p[] = $xc+$w; $p[] = $yc; $p[] = $xc; $p[] = $yc; for( $a=2*M_PI+$step; $a < $rea; $a += $step ) { $pt[] = $xc + $w*cos($a); $pt[] = $yc - $h*sin($a); } $pt[] = $xc+$w*$cosea; $pt[] = $yc-$h*$sinea; $pt[] = $xc; $pt[] = $yc; } else { $p = array($xc,$yc,$xc,$yc+$z, $xc+$w*$cossa,$z+$yc-$h*$sinsa); $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa); $rea = $rea == 0.0 ? 2*M_PI : $rea; for( $a=$rsa; $a < $rea; $a += $step ) { $tca = cos($a); $tsa = sin($a); $p[] = $xc+$w*$tca; $p[] = $z+$yc-$h*$tsa; $pt[] = $xc+$w*$tca; $pt[] = $yc-$h*$tsa; } $pt[] = $xc+$w*$cosea; $pt[] = $yc-$h*$sinea; $pt[] = $xc; $pt[] = $yc; $p[] = $xc+$w*$cosea; $p[] = $z+$yc-$h*$sinea; $p[] = $xc+$w*$cosea; $p[] = $yc-$h*$sinea; $p[] = $xc; $p[] = $yc; } } elseif( $sa >= 180 ) { $p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea); $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea); for( $a=$rea; $a>$rsa; $a -= $step ) { $tca = cos($a); $tsa = sin($a); $p[] = $xc+$w*$tca; $p[] = $z+$yc-$h*$tsa; $pt[] = $xc+$w*$tca; $pt[] = $yc-$h*$tsa; } $pt[] = $xc+$w*$cossa; $pt[] = $yc-$h*$sinsa; $pt[] = $xc; $pt[] = $yc; $p[] = $xc+$w*$cossa; $p[] = $z+$yc-$h*$sinsa; $p[] = $xc+$w*$cossa; $p[] = $yc-$h*$sinsa; $p[] = $xc; $p[] = $yc; } elseif( $sa >= 90 ) { if( $ea > 180 ) { $p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea); $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea); for( $a=$rea; $a > M_PI; $a -= $step ) { $tca = cos($a); $tsa = sin($a); $p[] = $xc+$w*$tca; $p[] = $z + $yc - $h*$tsa; $pt[] = $xc+$w*$tca; $pt[] = $yc-$h*$tsa; } $p[] = $xc-$w; $p[] = $z+$yc; $p[] = $xc-$w; $p[] = $yc; $p[] = $xc; $p[] = $yc; $pt[] = $xc-$w; $pt[] = $z+$yc; $pt[] = $xc-$w; $pt[] = $yc; for( $a=M_PI-$step; $a > $rsa; $a -= $step ) { $pt[] = $xc + $w*cos($a); $pt[] = $yc - $h*sin($a); } $pt[] = $xc+$w*$cossa; $pt[] = $yc-$h*$sinsa; $pt[] = $xc; $pt[] = $yc; } else { // $sa >= 90 && $ea <= 180 $p = array($xc,$yc,$xc,$yc+$z, $xc+$w*$cosea,$z+$yc-$h*$sinea, $xc+$w*$cosea,$yc-$h*$sinea, $xc,$yc); $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea); for( $a=$rea; $a>$rsa; $a -= $step ) { $pt[] = $xc + $w*cos($a); $pt[] = $yc - $h*sin($a); } $pt[] = $xc+$w*$cossa; $pt[] = $yc-$h*$sinsa; $pt[] = $xc; $pt[] = $yc; } } else { // sa > 0 && ea < 90 $p = array($xc,$yc,$xc,$yc+$z, $xc+$w*$cossa,$z+$yc-$h*$sinsa, $xc+$w*$cossa,$yc-$h*$sinsa, $xc,$yc); $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa); for( $a=$rsa; $a < $rea; $a += $step ) { $pt[] = $xc + $w*cos($a); $pt[] = $yc - $h*sin($a); } $pt[] = $xc+$w*$cosea; $pt[] = $yc-$h*$sinea; $pt[] = $xc; $pt[] = $yc; } $img->PushColor($fillcolor.":".$shadow); $img->FilledPolygon($p); $img->PopColor(); $img->PushColor($fillcolor); $img->FilledPolygon($pt); $img->PopColor(); } function SetStartAngle($aStart) { if( $aStart < 0 || $aStart > 360 ) { JpGraphError::RaiseL(14004);//('Slice start angle must be between 0 and 360 degrees.'); } $this->startangle = $aStart; } // Draw a 3D Pie function Pie3D($aaoption,$img,$data,$colors,$xc,$yc,$d,$angle,$z, $shadow=0.65,$startangle=0,$edgecolor="",$edgeweight=1) { //--------------------------------------------------------------------------- // As usual the algorithm get more complicated than I originally // envisioned. I believe that this is as simple as it is possible // to do it with the features I want. It's a good exercise to start // thinking on how to do this to convince your self that all this // is really needed for the general case. // // The algorithm two draw 3D pies without "real 3D" is done in // two steps. // First imagine the pie cut in half through a thought line between // 12'a clock and 6'a clock. It now easy to imagine that we can plot // the individual slices for each half by starting with the topmost // pie slice and continue down to 6'a clock. // // In the algortithm this is done in three principal steps // Step 1. Do the knife cut to ensure by splitting slices that extends // over the cut line. This is done by splitting the original slices into // upto 3 subslices. // Step 2. Find the top slice for each half // Step 3. Draw the slices from top to bottom // // The thing that slightly complicates this scheme with all the // angle comparisons below is that we can have an arbitrary start // angle so we must take into account the different equivalence classes. // For the same reason we must walk through the angle array in a // modulo fashion. // // Limitations of algorithm: // * A small exploded slice which crosses the 270 degree point // will get slightly nagged close to the center due to the fact that // we print the slices in Z-order and that the slice left part // get printed first and might get slightly nagged by a larger // slice on the right side just before the right part of the small // slice. Not a major problem though. //--------------------------------------------------------------------------- // Determine the height of the ellippse which gives an // indication of the inclination angle $h = ($angle/90.0)*$d; $sum = 0; for($i=0; $i<count($data); ++$i ) { $sum += $data[$i]; } // Special optimization if( $sum==0 ) return; if( $this->labeltype == 2 ) { $this->adjusted_data = $this->AdjPercentage($data); } // Setup the start $accsum = 0; $a = $startangle; $a = $this->NormAngle($a); // // Step 1 . Split all slices that crosses 90 or 270 // $idx=0; $adjexplode=array();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -