Ignore:
Timestamp:
Apr 13, 2019, 8:05:15 PM (5 years ago)
Author:
roby
Message:
 
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/client/modules/Elezioni/grafici/jpgraph_pie3d.php

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