📄 jpgraph_pie.php
字号:
$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->Pos($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.
$img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
$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,$radius) {
// Default value
if( $this->ilabelposadj === 'auto' )
$this->ilabelposadj = 0.65;
$r = $radius;
// 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.
$img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
$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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -