📄 jpgraph_pie.php
字号:
for($i=0; $sum > 0 && $i < $n; ++$i) { $j = $n-$i-1; $d = $this->data[$i]; $angle1 = $angle2; $accsum += $d; $angle2 = $this->startangle+2*M_PI*$accsum/$sum; if( empty($this->explode_radius[$j]) ) $this->explode_radius[$j]=0; $la = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1); $xcm = $xc + $this->explode_radius[$j]*cos($la)*$expscale; $ycm = $yc - $this->explode_radius[$j]*sin($la)*$expscale; $xcm += $this->ishadowdrop*$expscale; $ycm += $this->ishadowdrop*$expscale; $img->CakeSlice($xcm,$ycm,$radius,$radius, $angle1*180/M_PI,$angle2*180/M_PI,$this->ishadowcolor); } } $accsum=0; $angle2 = $this->startangle; $img->SetColor($this->color); for($i=0; $sum>0 && $i < $n; ++$i) { $j = $n-$i-1; if( empty($this->explode_radius[$j]) ) $this->explode_radius[$j]=0; $d = $this->data[$i]; $angle1 = $angle2; $accsum += $d; $angle2 = $this->startangle+2*M_PI*$accsum/$sum; $this->la[$i] = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1); if( $d == 0 ) continue; if( $this->setslicecolors==null ) $slicecolor=$colors[$ta[$i%$numcolors]]; else $slicecolor=$this->setslicecolors[$i%$numcolors]; if( $this->pie_interior_border && $aaoption===0 ) $img->SetColor($this->color); else $img->SetColor($slicecolor); $arccolor = $this->pie_border && $aaoption===0 ? $this->color : ""; $xcm = $xc + $this->explode_radius[$j]*cos($this->la[$i])*$expscale; $ycm = $yc - $this->explode_radius[$j]*sin($this->la[$i])*$expscale; if( $aaoption !== 2 ) { $img->CakeSlice($xcm,$ycm,$radius-1,$radius-1, $angle1*180/M_PI,$angle2*180/M_PI,$slicecolor,$arccolor); } if( $this->csimtargets && $aaoption !== 1 ) { $this->AddSliceToCSIM($i,$xcm,$ycm,$radius,$angle1,$angle2); } } // Format the titles for each slice if( $aaoption !== 2 ) { for( $i=0; $i < $n; ++$i) { if( $this->labeltype==0 ) { if( $sum != 0 ) $l = 100.0*$this->data[$i]/$sum; else $l = 0.0; } elseif( $this->labeltype==1 ) { $l = $this->data[$i]*1.0; } else { $l = $this->adjusted_data[$i]; } if( isset($this->labels[$i]) && is_string($this->labels[$i]) ) $this->labels[$i]=sprintf($this->labels[$i],$l); else $this->labels[$i]=$l; } } if( $this->value->show && $aaoption !== 1 ) { $this->StrokeAllLabels($img,$xc,$yc,$radius); } // Adjust title position if( $aaoption !== 1 ) { $this->title->SetPos($xc, $yc-$this->title->GetFontHeight($img)-$radius-$this->title->margin, "center","bottom"); $this->title->Stroke($img); } }//---------------// PRIVATE METHODS function NormAngle($a) { while( $a < 0 ) $a += 2*M_PI; while( $a > 2*M_PI ) $a -= 2*M_PI; return $a; } function Quadrant($a) { $a=$this->NormAngle($a); if( $a > 0 && $a <= M_PI/2 ) return 0; if( $a > M_PI/2 && $a <= M_PI ) return 1; if( $a > M_PI && $a <= 1.5*M_PI ) return 2; if( $a > 1.5*M_PI ) return 3; } function StrokeGuideLabels($img,$xc,$yc,$radius) { $n = count($this->labels); //----------------------------------------------------------------------- // Step 1 of the algorithm is to construct a number of clusters // a cluster is defined as all slices within the same quadrant (almost) // that has an angualr distance less than the treshold //----------------------------------------------------------------------- $tresh_hold=25 * M_PI/180; // 25 degrees difference to be in a cluster $incluster=false; // flag if we are currently in a cluster or not $clusters = array(); // array of clusters $cidx=-1; // running cluster index // Go through all the labels and construct a number of clusters for($i=0; $i < $n-1; ++$i) { // Calc the angle distance between two consecutive slices $a1=$this->la[$i]; $a2=$this->la[$i+1]; $q1 = $this->Quadrant($a1); $q2 = $this->Quadrant($a2); $diff = abs($a1-$a2); if( $diff < $tresh_hold ) { if( $incluster ) { $clusters[$cidx][1]++; // Each cluster can only cover one quadrant // Do we cross a quadrant ( and must break the cluster) if( $q1 != $q2 ) { // If we cross a quadrant boundary we normally start a // new cluster. However we need to take the 12'a clock // and 6'a clock positions into a special consideration. // Case 1: WE go from q=1 to q=2 if the last slice on // the cluster for q=1 is close to 12'a clock and the // first slice in q=0 is small we extend the previous // cluster if( $q1 == 1 && $q2 == 0 && $a2 > (90-15)*M_PI/180 ) { if( $i < $n-2 ) { $a3 = $this->la[$i+2]; // If there isn't a cluster coming up with the next-next slice // we extend the previous cluster to cover this slice as well if( abs($a3-$a2) >= $tresh_hold ) { $clusters[$cidx][1]++; $i++; } } } elseif( $q1 == 3 && $q2 == 2 && $a2 > (270-15)*M_PI/180 ) { if( $i < $n-2 ) { $a3 = $this->la[$i+2]; // If there isn't a cluster coming up with the next-next slice // we extend the previous cluster to cover this slice as well if( abs($a3-$a2) >= $tresh_hold ) { $clusters[$cidx][1]++; $i++; } } } if( $q1==2 && $q2==1 && $a2 > (180-15)*M_PI/180 ) { $clusters[$cidx][1]++; $i++; } $incluster = false; } } elseif( $q1 == $q2) { $incluster = true; // Now we have a special case for quadrant 0. If we previously // have a cluster of one in quadrant 0 we just extend that // cluster. If we don't do this then we risk that the label // for the cluster of one will cross the guide-line if( $q1 == 0 && $cidx > -1 && $clusters[$cidx][1] == 1 && $this->Quadrant($this->la[$clusters[$cidx][0]]) == 0 ) { $clusters[$cidx][1]++; } else { $cidx++; $clusters[$cidx][0] = $i; $clusters[$cidx][1] = 1; } } else { // Create a "cluster" of one since we are just crossing // a quadrant $cidx++; $clusters[$cidx][0] = $i; $clusters[$cidx][1] = 1; } } else { if( $incluster ) { // Add the last slice $clusters[$cidx][1]++; $incluster = false; } else { // Create a "cluster" of one $cidx++; $clusters[$cidx][0] = $i; $clusters[$cidx][1] = 1; } } } // Handle the very last slice if( $incluster ) { $clusters[$cidx][1]++; } else { // Create a "cluster" of one $cidx++; $clusters[$cidx][0] = $i; $clusters[$cidx][1] = 1; } /* if( true ) { // Debug printout in labels for( $i=0; $i <= $cidx; ++$i ) { for( $j=0; $j < $clusters[$i][1]; ++$j ) { $a = $this->la[$clusters[$i][0]+$j]; $aa = round($a*180/M_PI); $q = $this->Quadrant($a); $this->labels[$clusters[$i][0]+$j]="[$q:$aa] $i:$j"; } } } */ //----------------------------------------------------------------------- // Step 2 of the algorithm is use the clusters and draw the labels // and guidelines //----------------------------------------------------------------------- // We use the font height as the base factor for how far we need to // spread the labels in the Y-direction. $this->value->ApplyFont($img); $fh = $img->GetFontHeight(); $origvstep=$fh*$this->iGuideVFactor; $this->value->SetMargin(0); // Number of clusters found $nc = count($clusters); // Walk through all the clusters for($i=0; $i < $nc; ++$i) { // Start angle and number of slices in this cluster $csize = $clusters[$i][1]; $a = $this->la[$clusters[$i][0]]; $q = $this->Quadrant($a); // Now set up the start and end conditions to make sure that // in each cluster we walk through the all the slices starting with the slice // closest to the equator. Since all slices are numbered clockwise from "3'a clock" // we have different conditions depending on in which quadrant the slice lies within. if( $q == 0 ) { $start = $csize-1; $idx = $start; $step = -1; $vstep = -$origvstep; } elseif( $q == 1 ) { $start = 0; $idx = $start; $step = 1; $vstep = -$origvstep; } elseif( $q == 2 ) { $start = $csize-1; $idx = $start; $step = -1; $vstep = $origvstep; } elseif( $q == 3 ) { $start = 0; $idx = $start; $step = 1; $vstep = $origvstep; } // Walk through all slices within this cluster for($j=0; $j < $csize; ++$j) { // Now adjust the position of the labels in each cluster starting // with the slice that is closest to the equator of the pie $a = $this->la[$clusters[$i][0]+$idx]; // Guide line start in the center of the arc of the slice $r = $radius+$this->explode_radius[$n-1-($clusters[$i][0]+$idx)]; $x = round($r*cos($a)+$xc); $y = round($yc-$r*sin($a)); // The distance from the arc depends on chosen font and the "R-Factor" $r += $fh*$this->iGuideLineRFactor; // Should the labels be placed curved along the pie or in straight columns // outside the pie? if( $this->iGuideLineCurve ) $xt=round($r*cos($a)+$xc); // If this is the first slice in the cluster we need some first time // proessing if( $idx == $start ) { if( ! $this->iGuideLineCurve ) $xt=round($r*cos($a)+$xc); $yt=round($yc-$r*sin($a)); // Some special consideration in case this cluster starts // in quadrant 1 or 3 very close to the "equator" (< 20 degrees) // and the previous clusters last slice is within the tolerance. // In that case we add a font height to this labels Y-position // so it doesn't collide with // the slice in the previous cluster $prevcluster = ($i + ($nc-1) ) % $nc; $previdx=$clusters[$prevcluster][0]+$clusters[$prevcluster][1]-1; if( $q == 1 && $a > 160*M_PI/180 ) { // Get the angle for the previous clusters last slice $diff = abs($a-$this->la[$previdx]); if( $diff < $tresh_hold ) { $yt -= $fh; } } elseif( $q == 3 && $a > 340*M_PI/180 ) { // We need to subtract 360 to compare angle distance between // q=0 and q=3 $diff = abs($a-$this->la[$previdx]-360*M_PI/180); if( $diff < $tresh_hold ) { $yt += $fh; } } } else { // The step is at minimum $vstep but if the slices are relatively large // we make sure that we add at least a step that corresponds to the vertical // distance between the centers at the arc on the slice $prev_a = $this->la[$clusters[$i][0]+($idx-$step)]; $dy = abs($radius*(sin($a)-sin($prev_a))*1.2); if( $vstep > 0 ) $yt += max($vstep,$dy); else $yt += min($vstep,-$dy); } $label = $this->labels[$clusters[$i][0]+$idx]; if( $csize == 1 ) { // A "meta" cluster with only one slice $r = $radius+$this->explode_radius[$n-1-($clusters[$i][0]+$idx)]; $rr = $r+$img->GetFontHeight()/2; $xt=round($rr*cos($a)+$xc); $yt=round($yc-$rr*sin($a)); $this->StrokeLabel($label,$img,$xc,$yc,$a,$r); if( $this->iShowGuideLineForSingle ) $this->guideline->Stroke($img,$x,$y,$xt,$yt); } else { $this->guideline->Stroke($img,$x,$y,$xt,$yt); if( $q==1 || $q==2 ) { // Left side of Pie $this->guideline->Stroke($img,$xt,$yt,$xt-$this->guidelinemargin,$yt); $lbladj = -$this->guidelinemargin-5; $this->value->halign = "right"; $this->value->valign = "center"; } else { // Right side of pie $this->guideline->Stroke($img,$xt,$yt,$xt+$this->guidelinemargin,$yt); $lbladj = $this->guidelinemargin+5; $this->value->halign = "left"; $this->value->valign = "center"; } $this->value->Stroke($img,$label,$xt+$lbladj,$yt); } // Udate idx to point to next slice in the cluster to process $idx += $step; } } } function StrokeAllLabels($img,$xc,$yc,$radius) { // First normalize all angles for labels $n = count($this->la); for($i=0; $i < $n; ++$i) { $this->la[$i] = $this->NormAngle($this->la[$i]); } if( $this->guideline->iShow ) { $this->StrokeGuideLabels($img,$xc,$yc,$radius); } else { $n = count($this->labels); for($i=0; $i < $n; ++$i) { $this->StrokeLabel($this->labels[$i],$img,$xc,$yc, $this->la[$i], $radius + $this->explode_radius[$n-1-$i]); } } } // Position the labels of each slice function StrokeLabel($label,$img,$xc,$yc,$a,$r) { // Default value if( $this->ilabelposadj === 'auto' ) $this->ilabelposadj = 0.65; // We position the values diferently depending on if they are inside // or outside the pie if( $this->ilabelposadj < 1.0 ) { $this->value->SetAlign('center','center'); $this->value->margin = 0; $xt=round($this->ilabelposadj*$r*cos($a)+$xc); $yt=round($yc-$this->ilabelposadj*$r*sin($a)); $this->value->Stroke($img,$label,$xt,$yt); } else { $this->value->halign = "left"; $this->value->valign = "top"; $this->value->margin = 0; // Position the axis title. // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text // that intersects with the extension of the corresponding axis. The code looks a little // bit messy but this is really the only way of having a reasonable position of the // axis titles. $this->value->ApplyFont($img); $h=$img->GetTextHeight($label); // For numeric values the format of the display value // must be taken into account if( is_numeric($label) ) { if( $label > 0 ) $w=$img->GetTextWidth(sprintf($this->value->format,$label)); else $w=$img->GetTextWidth(sprintf($this->value->negformat,$label)); } else $w=$img->GetTextWidth($label); if( $this->ilabelposadj > 1.0 && $this->ilabelposadj < 5.0) { $r *= $this->ilabelposadj; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -