Filewatcher File Search
FTP Search
  
Directory (beta)
  
Content Search (beta)
   
pkg://jpgraph-1.16-0.fdr.2.src.rpm:3863612/jpgraph-1.16.tar.gz  info  downloads

jpgraph-1.16/0040755000076400001440000000000010065255153011612 5ustar  ljpusersjpgraph-1.16/src/0040755000076400001440000000000010065254151012376 5ustar  ljpusersjpgraph-1.16/src/imgdata_stars.inc0100644000076400001440000001477710065253722015733 0ustar  ljpusers<?php
//=======================================================================
// File:	IMGDATA_STARS.INC
// Description:	Base64 encoded images for stars
// Created: 	2003-03-20
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: imgdata_stars.inc,v 1.1 2003/03/23 13:37:37 aditus Exp $
//
// License:	This code is released under QPL 1.0 
// Copyright (C) 2003 Johan Persson 
//========================================================================


class ImgData_Stars extends ImgData {
    var $name = 'Stars';
    var $an = array(MARK_IMG_STAR => 'imgdata');

    var $colors = array('bluegreen','lightblue','purple','blue','green','pink','red','yellow');
    var $index  = array('bluegreen'=>3,'lightblue'=>4,'purple'=>1, 
			'blue'=>5,'green'=>0,'pink'=>7,'red'=>2,'yellow'=>6);
    var $maxidx = 7 ;
    var $imgdata ;

    function ImgData_Stars() {
//==========================================================
// File: bstar_green_001.png
//==========================================================
	$this->imgdata[0][0]= 329 ;
	$this->imgdata[0][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAAUV'.
	    'BMVEX///////+/v7+83rqcyY2Q/4R7/15y/1tp/05p/0lg/zdX'.
	    '/zdX/zVV/zdO/zFJ9TFJvDFD4yg+8Bw+3iU68hwurhYotxYosx'.
	    'YokBoTfwANgQFUp7DWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
	    'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJj'.
	    'CRyxgTAAAAcUlEQVR4nH3MSw6AIAwEUBL/IKBWwXL/g0pLojUS'.
	    'ZzGLl8ko9Zumhr5iy66/GH0dp49llNPB5sTotDY5PVuLG6tnM9'.
	    'CVKSIe1joSgPsAKSuANNaENFQvTAGzmheSkUpMBWeJZwqBT8wo'.
	    'hmysD4bnnPsC/x8ItUdGPfAAAAAASUVORK5CYII=' ; 
//==========================================================
// File: bstar_blred.png
//==========================================================
	$this->imgdata[1][0]= 325 ;
	$this->imgdata[1][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
	    'BMVEX///+/v79uRJ6jWPOSUtKrb+ejWO+gWPaGTruJTr6rZvF2'.
	    'RqC2ocqdVuCeV+egV/GsnLuIXL66rMSpcOyATbipY/OdWOp+VK'.
	    'aTU9WhV+yJKBoLAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
	    'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJwynv1'.
	    'XVAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
	    'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
	    'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
	    'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ; 

//==========================================================
// File: bstar_red_001.png
//==========================================================
	$this->imgdata[2][0]= 325 ;
	$this->imgdata[2][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
	    'BMVEX///+/v7+eRFHzWG3SUmHnb37vWGr2WHG7Tlm+TljxZneg'.
	    'Rk3KoaXgVmXnV2nxV227nJ++XGzErK3scIS4TVzzY3fqWG2mVF'.
	    'zVU2PsV2rJFw9VAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
	    'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJzCI0C'.
	    'lSAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
	    'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
	    'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
	    'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ; 

//==========================================================
// File: bstar_blgr_001.png
//==========================================================
	$this->imgdata[3][0]= 325 ;
	$this->imgdata[3][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
	    'BMVEX///+/v79Ehp5Yx/NSq9Jvw+dYwu9YzfZOmbtOmb5myPFG'.
	    'gqChvcpWteBXvedXxvGcsbtcpb6su8RwzOxNmrhjyvNYwupUjK'.
	    'ZTr9VXwOyJhmWNAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
	    'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJTC65k'.
	    'vQAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
	    'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
	    'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
	    'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ; 

//==========================================================
// File: bstar_blgr_002.png
//==========================================================
	$this->imgdata[4][0]= 325 ;
	$this->imgdata[4][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
	    'BMVEX///+/v79EnpxY8/FS0dJv5+dY7+9Y9vBOubtOur5m8fFG'.
	    'nKChycpW3uBX5+ZX8e2curtcvrqswsRw7OdNuLZj8/BY6udUpK'.
	    'ZT1dRX7OtNkrW5AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
	    'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJgXHeN'.
	    'wwAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
	    'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
	    'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
	    'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ; 

//==========================================================
// File: bstar_blue_001.png
//==========================================================
	$this->imgdata[5][0]= 325 ;
	$this->imgdata[5][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
	    'BMVEX///+/v79EY55Yi/NSetJvledYiO9YkPZOb7tObr5mkvFG'.
	    'X6ChrcpWgOBXhedXi/Gcpbtcf76sssRwnOxNcbhjk/NYiepUbK'.
	    'ZTfdVXh+ynNEzzAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
	    'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJhStyP'.
	    'zCAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
	    'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
	    'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
	    'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ; 

//==========================================================
// File: bstar_oy_007.png
//==========================================================
	$this->imgdata[6][0]= 325 ;
	$this->imgdata[6][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
	    'BMVEX///+/v7+ejUTz11jSvVLn02/v1lj21li7q06+r07x2mag'.
	    'lUbKxKHgy1bnz1fx1Ve7t5y+qlzEwqzs03C4pE3z2WPqz1imml'.
	    'TVv1Ps01dGRjeyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
	    'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJjsGGc'.
	    'GbAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
	    'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
	    'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
	    'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ; 

//==========================================================
// File: bstar_lred.png
//==========================================================
	$this->imgdata[7][0]= 325 ;
	$this->imgdata[7][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
	    'BMVEX///+/v7+eRJPzWN3SUr7nb9TvWNj2WOS7Tqi+TqnxZtyg'.
	    'Ro/KocPgVsjnV9LxV927nLa+XLTErL7scN24TarzY9/qWNemVJ'.
	    'jVU8LsV9VCwcc9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
	    'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJxi9ZY'.
	    'GoAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
	    'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
	    'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
	    'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ; 
    }
}

?>jpgraph-1.16/src/jpgraph_iconplot.php0100644000076400001440000000705110065253722016454 0ustar  ljpusers<?php
//=======================================================================
// File:	JPGRAPH_ICONPLOT.PHP
// Description:	PHP4 Graph Plotting library. Extension module.
// Created: 	2004-02-18
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: jpgraph_iconplot.php,v 1.1.2.1 2004/02/21 08:54:39 aditus Exp $
//
// License:	This code is released under QPL 1.0
// Copyright (C) 2001,2002,2003,2004 Johan Persson
//========================================================================


//===================================================
// CLASS IconPlot
// Description: Make it possible to add a (small) image
// to the graph
//===================================================
class IconPlot {
    var $iHorAnchor='left',$iVertAnchor='top';
    var $iX=0,$iY=0;
    var $iFile='';
    var $iScale=1.0,$iMix=100;
    var $iAnchors = array('left','right','top','bottom','center');
    var $iCountryFlag='',$iCountryStdSize=3;

    function IconPlot($aFile="",$aX=0,$aY=0,$aScale=1.0,$aMix=100) {
	$this->iFile = $aFile;
	$this->iX=$aX;
	$this->iY=$aY;
	$this->iScale= $aScale;
	if( $aMix < 0 || $aMix > 100 ) {
	    JpGraphError::Raise('Mix value for icon must be between 0 and 100.');
	}
	$this->iMix = $aMix ;
    }

    function SetCountryFlag($aFlag,$aX=0,$aY=0,$aScale=1.0,$aMix=100,$aStdSize=3) {
	$this->iCountryFlag = $aFlag;
	$this->iX=$aX;
	$this->iY=$aY;
	$this->iScale= $aScale;
	if( $aMix < 0 || $aMix > 100 ) {
	    JpGraphError::Raise('Mix value for icon must be between 0 and 100.');
	}
	$this->iMix = $aMix;
	$this->iCountryStdSize = $aStdSize;
    }

    function SetPos($aX,$aY) {
	$this->iX=$aX;
	$this->iY=$aY;
    }

    function SetScale($aScale) {
	$this->iScale = $aScale;
    }

    function SetMix($aMix) {
	if( $aMix < 0 || $aMix > 100 ) {
	    JpGraphError::Raise('Mix value for icon must be between 0 and 100.');
	}
	$this->iMix = $aMix ;
    }

    function SetAnchor($aXAnchor='left',$aYAnchor='center') {
	if( !in_array($aXAnchor,$this->iAnchors) ||
	    !in_array($aYAnchor,$this->iAnchors) ) {
	    JpGraphError::Raise("Anchor position for icons must be one of 'top', 'bottom', 'left', 'right' or 'center'");
	}
	$this->iHorAnchor=$aXAnchor;
	$this->iVertAnchor=$aYAnchor;
    }
    
    function Stroke($aImg) {
	if( $this->iFile != '' && $this->iCountryFlag != '' ) {
	    JpGraphError::Raise('It is not possible to specify both an image file and a country flag for the same icon.');	
	}
	if( $this->iFile != '' ) {
	    $gdimg = Graph::LoadBkgImage('',$this->iFile);
	}
	else {
	    if( ! class_exists('FlagImages') ) {
		JpGraphError::Raise('In order to use Country flags as icons you must include the "jpgraph_flags.php" file.');
	    }
	    $fobj = new FlagImages($this->iCountryStdSize);
	    $dummy='';
	    $gdimg = $fobj->GetImgByName($this->iCountryFlag,$dummy);
	}
	if( $this->iX >= 0  && $this->iX <= 1.0 ) {
	    $w = imagesx($aImg->img);
	    $this->iX = round($w*$this->iX);
	}
	if( $this->iY >= 0  && $this->iY <= 1.0 ) {
	    $h = imagesy($aImg->img);
	    $this->iY = round($h*$this->iY);
	}
	$iconw = imagesx($gdimg);
	$iconh = imagesy($gdimg);
	
	if( $this->iHorAnchor == 'center' ) 
	    $this->iX -= round($iconw*$this->iScale/2);
	if( $this->iHorAnchor == 'right' ) 
	    $this->iX -= round($iconw*$this->iScale);
	if( $this->iVertAnchor == 'center' ) 
	    $this->iY -= round($iconh*$this->iScale/2);
	if( $this->iVertAnchor == 'bottom' ) 
	    $this->iY -= round($iconh*$this->iScale);

	$aImg->CopyMerge($gdimg,$this->iX,$this->iY,0,0,
			 round($iconw*$this->iScale),round($iconh*$this->iScale),
			 $iconw,$iconh,
			 $this->iMix);
    }
}

?>
jpgraph-1.16/src/jpgraph_error.php0100644000076400001440000001032710065253722015756 0ustar  ljpusers<?php
/*=======================================================================
// File: 	JPGRAPH_ERROR.PHP
// Description:	Error plot extension for JpGraph
// Created: 	2001-01-08
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: jpgraph_error.php,v 1.16 2003/02/04 22:47:16 aditus Exp $
//
// License:	This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/

//===================================================
// CLASS ErrorPlot
// Description: Error plot with min/max value for
// each datapoint
//===================================================
class ErrorPlot extends Plot {
    var $errwidth=2;
//---------------
// CONSTRUCTOR
    function ErrorPlot(&$datay,$datax=false) {
	$this->Plot($datay,$datax);
	$this->numpoints /= 2;
    }
//---------------
// PUBLIC METHODS
	
    // Gets called before any axis are stroked
    function PreStrokeAdjust(&$graph) {
	if( $this->center ) {
	    $a=0.5; $b=0.5;
	    ++$this->numpoints;			
	} else {
	    $a=0; $b=0;
	}
	$graph->xaxis->scale->ticks->SetXLabelOffset($a);
	$graph->SetTextScaleOff($b);						
	//$graph->xaxis->scale->ticks->SupressMinorTickMarks();
    }
	
    // Method description
    function Stroke(&$img,&$xscale,&$yscale) {
	$numpoints=count($this->coords[0])/2;
	$img->SetColor($this->color);
	$img->SetLineWeight($this->weight);	

	if( isset($this->coords[1]) ) {
	    if( count($this->coords[1])!=$numpoints )
		JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
	    else
		$exist_x = true;
	}
	else 
	    $exist_x = false;

	if( $exist_x )
	    $xs=$this->coords[1][0];
	else
	    $xs=0;

		
	for( $i=0; $i<$numpoints; ++$i) {
	    if( $exist_x ) $x=$this->coords[1][$i];
	    else $x=$i;
	    $xt = $xscale->Translate($x);
	    $yt1 = $yscale->Translate($this->coords[0][$i*2]);
	    $yt2 = $yscale->Translate($this->coords[0][$i*2+1]);
	    $img->Line($xt,$yt1,$xt,$yt2);
	    $img->Line($xt-$this->errwidth,$yt1,$xt+$this->errwidth,$yt1);
	    $img->Line($xt-$this->errwidth,$yt2,$xt+$this->errwidth,$yt2);
	}			
	return true;
    }
} // Class


//===================================================
// CLASS ErrorLinePlot
// Description: Combine a line and error plot
// THIS IS A DEPRECATED PLOT TYPE JUST KEPT FOR
// BACKWARD COMPATIBILITY
//===================================================
class ErrorLinePlot extends ErrorPlot {
    var $line=null;
//---------------
// CONSTRUCTOR
    function ErrorLinePlot(&$datay,$datax=false) {
	$this->ErrorPlot($datay,$datax);
	// Calculate line coordinates as the average of the error limits
	for($i=0; $i < count($datay); $i+=2 ) {
	    $ly[]=($datay[$i]+$datay[$i+1])/2;
	}		
	$this->line=new LinePlot($ly,$datax);
    }

//---------------
// PUBLIC METHODS
    function Legend(&$graph) {
	if( $this->legend != "" )
	    $graph->legend->Add($this->legend,$this->color);
	$this->line->Legend($graph);
    }
			
    function Stroke(&$img,&$xscale,&$yscale) {
	parent::Stroke($img,$xscale,$yscale);
	$this->line->Stroke($img,$xscale,$yscale);
    }
} // Class


//===================================================
// CLASS LineErrorPlot
// Description: Combine a line and error plot
//===================================================
class LineErrorPlot extends ErrorPlot {
    var $line=null;
//---------------
// CONSTRUCTOR
    // Data is (val, errdeltamin, errdeltamax)
    function LineErrorPlot(&$datay,$datax=false) {
	$ly=array(); $ey=array();
	$n = count($datay);
	if( $n % 3 != 0 ) {
	    JpGraphError::Raise('Error in input data to LineErrorPlot.'.
		'Number of data points must be a multiple of 3');
	}
	for($i=0; $i < count($datay); $i+=3 ) {
	    $ly[]=$datay[$i];
	    $ey[]=$datay[$i]+$datay[$i+1];
	    $ey[]=$datay[$i]+$datay[$i+2];
	}		
	$this->ErrorPlot($ey,$datax);
	$this->line=new LinePlot($ly,$datax);
    }

//---------------
// PUBLIC METHODS
    function Legend(&$graph) {
	if( $this->legend != "" )
	    $graph->legend->Add($this->legend,$this->color);
	$this->line->Legend($graph);
    }
			
    function Stroke(&$img,&$xscale,&$yscale) {
	parent::Stroke($img,$xscale,$yscale);
	$this->line->Stroke($img,$xscale,$yscale);
    }
} // Class


/* EOF */
?>jpgraph-1.16/src/jpgraph_radar.php0100644000076400001440000004355210065253722015724 0ustar  ljpusers<?php
/*=======================================================================
// File:	JPGRAPH_RADAR.PHP
// Description: Radar plot extension for JpGraph
// Created: 	2001-02-04
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: jpgraph_radar.php,v 1.7.2.5 2004/06/12 17:46:01 aditus Exp $
//
// License:	This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/

require_once('jpgraph_plotmark.inc');

class RadarLogTicks extends Ticks {
//---------------
// CONSTRUCTOR
    function RadarLogTicks() {
    }
//---------------
// PUBLIC METHODS	

    // TODO: Add Argument grid
    function Stroke(&$aImg,&$grid,$aPos,$aAxisAngle,&$aScale,&$aMajPos,&$aMajLabel) {
	$start = $aScale->GetMinVal();
	$limit = $aScale->GetMaxVal();
	$nextMajor = 10*$start;
	$step = $nextMajor / 10.0;
	$count=1; 
				
	$ticklen_maj=5;
	$dx_maj=round(sin($aAxisAngle)*$ticklen_maj);
	$dy_maj=round(cos($aAxisAngle)*$ticklen_maj);
	$ticklen_min=3;
	$dx_min=round(sin($aAxisAngle)*$ticklen_min);
	$dy_min=round(cos($aAxisAngle)*$ticklen_min);
			
	$aMajPos=array();
	$aMajLabel=array();
			
	if( $this->supress_first )
	    $aMajLabel[]="";
	else
	    $aMajLabel[]=$start;	
	$yr=$aScale->RelTranslate($start);	
	$xt=round($yr*cos($aAxisAngle))+$aScale->scale_abs[0];
	$yt=$aPos-round($yr*sin($aAxisAngle));
	$aMajPos[]=$xt+2*$dx_maj;
	$aMajPos[]=$yt-$aImg->GetFontheight()/2;
	$grid[]=$xt;
	$grid[]=$yt;

	$aImg->SetLineWeight($this->weight);			
		
	for($y=$start; $y<=$limit; $y+=$step,++$count  ) {
	    $yr=$aScale->RelTranslate($y);	
	    $xt=round($yr*cos($aAxisAngle))+$aScale->scale_abs[0];
	    $yt=$aPos-round($yr*sin($aAxisAngle));
	    if( $count % 10 == 0 ) {
		$grid[]=$xt;
		$grid[]=$yt;
		$aMajPos[]=$xt+2*$dx_maj;
		$aMajPos[]=$yt-$aImg->GetFontheight()/2;							
		if( !$this->supress_tickmarks )	{		
		    if( $this->majcolor!="" ) $aImg->PushColor($this->majcolor);
		    $aImg->Line($xt+$dx_maj,$yt+$dy_maj,$xt-$dx_maj,$yt-$dy_maj);
		    if( $this->majcolor!="" ) $aImg->PopColor();
		}
		$aMajLabel[]=$nextMajor;	
		$nextMajor *= 10;
		$step *= 10;	
		$count=1; 				
	    }
	    else
		if( !$this->supress_minor_tickmarks )	{
		    if( $this->mincolor!="" ) $aImg->PushColor($this->mincolor);
		    $aImg->Line($xt+$dx_min,$yt+$dy_min,$xt-$dx_min,$yt-$dy_min);
		    if( $this->mincolor!="" ) $aImg->PopColor();
		}
	}		
    }		
}
	
class RadarLinearTicks extends LinearTicks {
//---------------
// CONSTRUCTOR
    function RadarLinearTicks() {
	// Empty
    }

//---------------
// PUBLIC METHODS	

    // TODO: Add argument grid
    function Stroke(&$aImg,&$grid,$aPos,$aAxisAngle,&$aScale,&$aMajPos,&$aMajLabel) {
	// Prepare to draw linear ticks
	$maj_step_abs = abs($aScale->scale_factor*$this->major_step);	
	$min_step_abs = abs($aScale->scale_factor*$this->minor_step);	
	$nbrmaj = floor(($aScale->world_abs_size)/$maj_step_abs);
	$nbrmin = floor(($aScale->world_abs_size)/$min_step_abs);
	$skip = round($nbrmin/$nbrmaj); // Don't draw minor ontop of major

	// Draw major ticks
	$ticklen2=$this->major_abs_size;
	$dx=round(sin($aAxisAngle)*$ticklen2);
	$dy=round(cos($aAxisAngle)*$ticklen2);
	$label=$aScale->scale[0]+$this->major_step;
		
	$aImg->SetLineWeight($this->weight);
		
	for($i=1; $i<=$nbrmaj; ++$i) {
	    $xt=round($i*$maj_step_abs*cos($aAxisAngle))+$aScale->scale_abs[0];
	    $yt=$aPos-round($i*$maj_step_abs*sin($aAxisAngle));
	    $aMajLabel[]=$label;
	    $label += $this->major_step;
	    $grid[]=$xt;
	    $grid[]=$yt;
	    $aMajPos[($i-1)*2]=$xt+2*$dx;
	    $aMajPos[($i-1)*2+1]=$yt-$aImg->GetFontheight()/2;				
	    if( !$this->supress_tickmarks ) {
		if( $this->majcolor!="" ) $aImg->PushColor($this->majcolor);
		$aImg->Line($xt+$dx,$yt+$dy,$xt-$dx,$yt-$dy);
		if( $this->majcolor!="" ) $aImg->PopColor();
	    }
	}

	// Draw minor ticks
	$ticklen2=$this->minor_abs_size;
	$dx=round(sin($aAxisAngle)*$ticklen2);
	$dy=round(cos($aAxisAngle)*$ticklen2);
	if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks)	{
	    if( $this->mincolor!="" ) $aImg->PushColor($this->mincolor);						
	    for($i=1; $i<=$nbrmin; ++$i) {
		if( ($i % $skip) == 0 ) continue;
		$xt=round($i*$min_step_abs*cos($aAxisAngle))+$aScale->scale_abs[0];
		$yt=$aPos-round($i*$min_step_abs*sin($aAxisAngle));
		$aImg->Line($xt+$dx,$yt+$dy,$xt-$dx,$yt-$dy);
	    }
	    if( $this->mincolor!="" ) $aImg->PopColor();
	}
    }
}

	

//===================================================
// CLASS RadarAxis
// Description: Implements axis for the spider graph
//===================================================
class RadarAxis extends Axis {
    var $title_color="navy";
    var $title=null;
//---------------
// CONSTRUCTOR
    function RadarAxis(&$img,&$aScale,$color=array(0,0,0)) {
	parent::Axis($img,$aScale,$color);
	$this->len=$img->plotheight;
	$this->title = new Text();
	$this->title->SetFont(FF_FONT1,FS_BOLD);
	$this->color = array(0,0,0);
    }
//---------------
// PUBLIC METHODS	
    function SetTickLabels($l) {
	$this->ticks_label = $l;
    }
	
	
    // Stroke the axis 
    // $pos 			= Vertical position of axis
    // $aAxisAngle = Axis angle
    // $grid			= Returns an array with positions used to draw the grid
    //	$lf			= Label flag, TRUE if the axis should have labels
    function Stroke($pos,$aAxisAngle,&$grid,$title,$lf) {
	$this->img->SetColor($this->color);
		
	// Determine end points for the axis
	$x=round($this->scale->world_abs_size*cos($aAxisAngle)+$this->scale->scale_abs[0]);
	$y=round($pos-$this->scale->world_abs_size*sin($aAxisAngle));
		
	// Draw axis
	$this->img->SetColor($this->color);
	$this->img->SetLineWeight($this->weight);
	if( !$this->hide )
	    $this->img->Line($this->scale->scale_abs[0],$pos,$x,$y);
	
	$this->scale->ticks->Stroke($this->img,$grid,$pos,$aAxisAngle,$this->scale,$majpos,$majlabel);
		
	// Draw labels
	if( $lf && !$this->hide ) {
	    $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);	
	    $this->img->SetTextAlign("left","top");
	    $this->img->SetColor($this->color);
			
	    // majpos contsins (x,y) coordinates for labels
	    for($i=0; $i<count($majpos)/2; ++$i) {
		if( $this->ticks_label != null )
		    $this->img->StrokeText($majpos[$i*2],$majpos[$i*2+1],$this->ticks_label[$i]);
		else
		    $this->img->StrokeText($majpos[$i*2],$majpos[$i*2+1],$majlabel[$i]);
	    }
	}
	$this->_StrokeAxisTitle($pos,$aAxisAngle,$title);
    }
//---------------
// PRIVATE METHODS	
	
    function _StrokeAxisTitle($pos,$aAxisAngle,$title) {
	$this->title->Set($title);
	$marg=6+$this->title->margin;
	$xt=round(($this->scale->world_abs_size+$marg)*cos($aAxisAngle)+$this->scale->scale_abs[0]);
	$yt=round($pos-($this->scale->world_abs_size+$marg)*sin($aAxisAngle));

	// 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.
	$h=$this->img->GetFontHeight();
	$w=$this->img->GetTextWidth($title);
	while( $aAxisAngle > 2*M_PI ) $aAxisAngle -= 2*M_PI;
	if( $aAxisAngle>=7*M_PI/4 || $aAxisAngle <= M_PI/4 ) $dx=0;
	if( $aAxisAngle>=M_PI/4 && $aAxisAngle <= 3*M_PI/4 ) $dx=($aAxisAngle-M_PI/4)*2/M_PI; 
	if( $aAxisAngle>=3*M_PI/4 && $aAxisAngle <= 5*M_PI/4 ) $dx=1;
	if( $aAxisAngle>=5*M_PI/4 && $aAxisAngle <= 7*M_PI/4 ) $dx=(1-($aAxisAngle-M_PI*5/4)*2/M_PI);
		
	if( $aAxisAngle>=7*M_PI/4 ) $dy=(($aAxisAngle-M_PI)-3*M_PI/4)*2/M_PI;
	if( $aAxisAngle<=M_PI/4 ) $dy=(1-$aAxisAngle*2/M_PI);
	if( $aAxisAngle>=M_PI/4 && $aAxisAngle <= 3*M_PI/4 ) $dy=1;
	if( $aAxisAngle>=3*M_PI/4 && $aAxisAngle <= 5*M_PI/4 ) $dy=(1-($aAxisAngle-3*M_PI/4)*2/M_PI);
	if( $aAxisAngle>=5*M_PI/4 && $aAxisAngle <= 7*M_PI/4 ) $dy=0;
		
	if( !$this->hide ) {
	    $this->title->Stroke($this->img,$xt-$dx*$w,$yt-$dy*$h,$title);
	}
    }
		
	
} // Class


//===================================================
// CLASS RadarGrid
// Description: Draws grid for the spider graph
//===================================================
class RadarGrid extends Grid {
//------------
// CONSTRUCTOR
    function RadarGrid() {
    }

//----------------
// PRIVATE METHODS	
    function Stroke(&$img,&$grid) {
	if( !$this->show ) return;
	$nbrticks = count($grid[0])/2;
	$nbrpnts = count($grid);
	$img->SetColor($this->grid_color);
	$img->SetLineWeight($this->weight);
	for($i=0; $i<$nbrticks; ++$i) {
	    for($j=0; $j<$nbrpnts; ++$j) {
		$pnts[$j*2]=$grid[$j][$i*2];
		$pnts[$j*2+1]=$grid[$j][$i*2+1];
	    }
	    for($k=0; $k<$nbrpnts; ++$k ){
		$l=($k+1)%$nbrpnts;
		if( $this->type == "solid" )
		    $img->Line($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1]);
		elseif( $this->type == "dotted" )
		    $img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],1,6);
		elseif( $this->type == "dashed" )
		    $img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],2,4);
		elseif( $this->type == "longdashed" )
		    $img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],8,6);
	    }
	    $pnts=array();
	}
    }
} // Class


//===================================================
// CLASS RadarPlot
// Description: Plot a spiderplot
//===================================================
class RadarPlot {
    var $data=array();
    var $fill=false, $fill_color=array(200,170,180);
    var $color=array(0,0,0);
    var $legend="";
    var $weight=1;
    var $linestyle='solid';
    var $mark=null;
//---------------
// CONSTRUCTOR
    function RadarPlot($data) {
	$this->data = $data;
	$this->mark = new PlotMark();
    }

//---------------
// PUBLIC METHODS	
    function Min() {
	return Min($this->data);
    }
	
    function Max() {
	return Max($this->data);
    }
	
    function SetLegend($legend) {
	$this->legend=$legend;
    }

    function SetLineStyle($aStyle) {
	$this->linestyle=$aStyle;
    }
	
    function SetLineWeight($w) {
	$this->weight=$w;
    }
		
    function SetFillColor($aColor) {
	$this->fill_color = $aColor;
	$this->fill = true;		
    }
    
    function SetFill($f=true) {
	$this->fill = $f;
    }
    
    function SetColor($aColor,$aFillColor=false) {
	$this->color = $aColor;
	if( $aFillColor ) {
	    $this->SetFillColor($aFillColor);
	    $this->fill = true;
	}
    }
	
    function GetCSIMareas() {
	JpGraphError::Raise("Client side image maps not supported for RadarPlots.");
    }
	
    function Stroke(&$img, $pos, &$scale, $startangle) {
	$nbrpnts = count($this->data);
	$astep=2*M_PI/$nbrpnts;		
	$a=$startangle;
		
	// Rotate each point to the correct axis-angle
	// TODO: Update for LogScale
	for($i=0; $i<$nbrpnts; ++$i) {
	    //$c=$this->data[$i];
	    $cs=$scale->RelTranslate($this->data[$i]);
	    $x=round($cs*cos($a)+$scale->scale_abs[0]);
	    $y=round($pos-$cs*sin($a));
	    /*
	      $c=log10($c);
	      $x=round(($c-$scale->scale[0])*$scale->scale_factor*cos($a)+$scale->scale_abs[0]);
	      $y=round($pos-($c-$scale->scale[0])*$scale->scale_factor*sin($a));		
	    */
	    $pnts[$i*2]=$x;
	    $pnts[$i*2+1]=$y;
	    $a += $astep;
	}
	if( $this->fill ) {
	    $img->SetColor($this->fill_color);
	    $img->FilledPolygon($pnts);
	}
	$img->SetLineWeight($this->weight);
	$img->SetColor($this->color);
	$img->SetLineStyle($this->linestyle);
	$pnts[]=$pnts[0];
	$pnts[]=$pnts[1];
	$img->Polygon($pnts);
	$img->SetLineStyle('solid'); // Reset line style to default
	// Add plotmarks on top
	if( $this->mark->show ) {
	    for($i=0; $i < $nbrpnts; ++$i) {
		$this->mark->Stroke($img,$pnts[$i*2],$pnts[$i*2+1]); 
	    }
	}

    }
	
//---------------
// PRIVATE METHODS
    function GetCount() {
	return count($this->data);
    }
	
    function Legend(&$graph) {
	if( $this->legend=="" ) return;
	if( $this->fill )
	    $graph->legend->Add($this->legend,$this->fill_color,$this->mark);
	else
	    $graph->legend->Add($this->legend,$this->color,$this->mark);	
    }
	
} // Class

//===================================================
// CLASS RadarGraph
// Description: Main container for a spider graph
//===================================================
class RadarGraph extends Graph {
    var $posx;
    var $posy;
    var $len;		
    var $plots=null, $axis_title=null;
    var $grid,$axis=null;
//---------------
// CONSTRUCTOR
    function RadarGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
	$this->Graph($width,$height,$cachedName,$timeout,$inline);
	$this->posx=$width/2;
	$this->posy=$height/2;
	$this->len=min($width,$height)*0.35;
	$this->SetColor(array(255,255,255));
	$this->SetTickDensity(TICKD_NORMAL);
	$this->SetScale("lin");
	$this->SetGridDepth(DEPTH_FRONT);

    }

//---------------
// PUBLIC METHODS
    function SupressTickMarks($f=true) {
    	if( ERR_DEPRECATED )
    		JpGraphError::Raise('RadarGraph::SupressTickMarks() is deprecated. Use HideTickMarks() instead.');
		$this->axis->scale->ticks->SupressTickMarks($f);
    }

    function HideTickMarks($aFlag=true) {
		$this->axis->scale->ticks->SupressTickMarks($aFlag);
    }
    
    function ShowMinorTickmarks($aFlag=true) {
    	$this->yscale->ticks->SupressMinorTickMarks(!$aFlag);
    }
	
    function SetScale($axtype,$ymin=1,$ymax=1) {
	if( $axtype != "lin" && $axtype != "log" ) {
	    JpGraphError::Raise("Illegal scale for spiderplot ($axtype). Must be \"lin\" or \"log\"");
	}
	if( $axtype=="lin" ) {
	    $this->yscale = & new LinearScale($ymin,$ymax);
	    $this->yscale->ticks = & new RadarLinearTicks();
	    $this->yscale->ticks->SupressMinorTickMarks();
	}
	elseif( $axtype=="log" ) {
	    $this->yscale = & new LogScale($ymin,$ymax);
	    $this->yscale->ticks = & new RadarLogTicks();
	}
		
	$this->axis = & new RadarAxis($this->img,$this->yscale);
	$this->grid = & new RadarGrid();		
    }

    function SetSize($aSize) {
	if( $aSize<0.1 || $aSize>1 )
	    JpGraphError::Raise("Radar Plot size must be between 0.1 and 1. (Your value=$s)");
	$this->len=min($this->img->width,$this->img->height)*$aSize/2;
    }

    function SetPlotSize($aSize) {
	$this->SetSize($aSize);
    }

    function SetTickDensity($densy=TICKD_NORMAL) {
	$this->ytick_factor=25;		
	switch( $densy ) {
	    case TICKD_DENSE:
		$this->ytick_factor=12;			
	    break;
	    case TICKD_NORMAL:
		$this->ytick_factor=25;			
	    break;
	    case TICKD_SPARSE:
		$this->ytick_factor=40;			
	    break;
	    case TICKD_VERYSPARSE:
		$this->ytick_factor=70;			
	    break;		
	    default:
		JpGraphError::Raise("RadarPlot Unsupported Tick density: $densy");
	}
    }

    function SetPos($px,$py=0.5) {
	$this->SetCenter($px,$py);
    }

    function SetCenter($px,$py=0.5) {
	assert($px > 0 && $py > 0 );
	$this->posx=$this->img->width*$px;
	$this->posy=$this->img->height*$py;
    }

    function SetColor($c) {
	$this->SetMarginColor($c);
    }
			
    function SetTitles($title) {
	$this->axis_title = $title;
    }

    function Add(&$splot) {
	$this->plots[]=$splot;
    }
	
    function GetPlotsYMinMax() {
	$min=$this->plots[0]->Min();
	$max=$this->plots[0]->Max();
	foreach( $this->plots as $p ) {
	    $max=max($max,$p->Max());
	    $min=min($min,$p->Min());
	}
	if( $min < 0 ) 
	    JpGraphError::Raise("Minimum data $min (Radar plots only makes sence to use when all data points > 0)");
	return array($min,$max);
    }	

    // Stroke the Radar graph
    function Stroke($aStrokeFileName="") {
	// Set Y-scale
	if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
	    list($min,$max) = $this->GetPlotsYMinMax();
	    $this->yscale->AutoScale($this->img,0,$max,$this->len/$this->ytick_factor);
	}
	// Set start position end length of scale (in absolute pixels)
	$this->yscale->SetConstants($this->posx,$this->len);
		
	// We need as many axis as there are data points
	$nbrpnts=$this->plots[0]->GetCount();
		
	// If we have no titles just number the axis 1,2,3,...
	if( $this->axis_title==null ) {
	    for($i=0; $i < $nbrpnts; ++$i ) 
		$this->axis_title[$i] = $i+1;
	}
	elseif(count($this->axis_title)<$nbrpnts) 
	    JpGraphError::Raise("Number of titles does not match number of points in plot.");
	for($i=0; $i<count($this->plots); ++$i )
	    if( $nbrpnts != $this->plots[$i]->GetCount() )
		JpGraphError::Raise("Each spider plot must have the same number of data points.");

	if( $this->background_image != "" ) {
	    $this->StrokeFrameBackground();
	}
	else {	
	    $this->StrokeFrame();
	}
	$astep=2*M_PI/$nbrpnts;

	// Prepare legends
	for($i=0; $i<count($this->plots); ++$i)
	    $this->plots[$i]->Legend($this);
	$this->legend->Stroke($this->img);			
	$this->footer->Stroke($this->img);			

	if( $this->grid_depth == DEPTH_BACK ) {
	    // Draw axis and grid
	    for( $i=0,$a=M_PI/2; $i < $nbrpnts; ++$i, $a += $astep ) {
		$this->axis->Stroke($this->posy,$a,$grid[$i],$this->axis_title[$i],$i==0);
	    }	
	}
		
	// Plot points
	$a=M_PI/2;
	for($i=0; $i<count($this->plots); ++$i )
	    $this->plots[$i]->Stroke($this->img, $this->posy, $this->yscale, $a);
		
	if( $this->grid_depth != DEPTH_BACK ) {
	    // Draw axis and grid
	    for( $i=0,$a=M_PI/2; $i < $nbrpnts; ++$i, $a += $astep ) {
		$this->axis->Stroke($this->posy,$a,$grid[$i],$this->axis_title[$i],$i==0);
	    }	
	}
	$this->grid->Stroke($this->img,$grid);
	$this->StrokeTitles();
	
	// Stroke texts
	if( $this->texts != null ) {
	    foreach( $this->texts as $t) 
		$t->Stroke($this->img);
	}

	// Should we do any final image transformation
	if( $this->iImgTrans ) {
	    if( !class_exists('ImgTrans') ) {
		require_once('jpgraph_imgtrans.php');
	    }
	       
	    $tform = new ImgTrans($this->img->img);
	    $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
					     $this->iImgTransDirection,$this->iImgTransHighQ,
					     $this->iImgTransMinSize,$this->iImgTransFillColor,
					     $this->iImgTransBorder);
	}
	
	// If the filename is given as the special "__handle"
	// then the image handler is returned and the image is NOT
	// streamed back
	if( $aStrokeFileName == _IMG_HANDLER ) {
	    return $this->img->img;
	    }
	else {
	    // Finally stream the generated picture					
	    $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
				       $aStrokeFileName);		
	}
    }
} // Class

/* EOF */
?>jpgraph-1.16/src/jpgraph_pie.php0100644000076400001440000006616610065253722015416 0ustar  ljpusers<?php
/*=======================================================================
// File:	JPGRAPH_PIE.PHP
// Description:	Pie plot extension for JpGraph
// Created: 	2001-02-14
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: jpgraph_pie.php,v 1.49.2.16 2004/06/19 12:22:05 aditus Exp $
//
// License:	This code is released under QPL
// Copyright (C) 2001,2002,2003 Johan Persson
//========================================================================
*/


// Defines for PiePlot::SetLabelType()
DEFINE("PIE_VALUE_ABS",1);
DEFINE("PIE_VALUE_PER",0);
DEFINE("PIE_VALUE_PERCENTAGE",0);
DEFINE("PIE_VALUE_ADJPERCENTAGE",2);
DEFINE("PIE_VALUE_ADJPER",2);

//===================================================
// CLASS PiePlot
// Description: Draws a pie plot
//===================================================
class PiePlot {
    var $posx=0.5,$posy=0.5;
    var $radius=0.3;
    var $explode_radius=array(),$explode_all=false,$explode_r=20;
    var $labels=null, $legends=null;
    var $csimtargets=null;  // Array of targets for CSIM
    var $csimareas='';		// Generated CSIM text	
    var $csimalts=null;		// ALT tags for corresponding target
    var $data=null;
    var $title;
    var $startangle=0;
    var $weight=1, $color="black";
    var $legend_margin=6,$show_labels=true;
    var $themearr = array(
	"earth" 	=> array(136,34,40,45,46,62,63,134,74,10,120,136,141,168,180,77,209,218,346,395,89,430),
	"pastel" => array(27,415,128,59,66,79,105,110,42,147,152,230,236,240,331,337,405,38),
	"water"  => array(8,370,24,40,335,56,213,237,268,14,326,387,10,388),
	"sand"   => array(27,168,34,170,19,50,65,72,131,209,46,393));
    var $theme="earth";
    var $setslicecolors=array();
    var $labeltype=0; // Default to percentage
    var $pie_border=true,$pie_interior_border=true;
    var $value;
    var $ishadowcolor='',$ishadowdrop=4;
    var $ilabelposadj=1;
    var $legendcsimtargets = array();
    var $legendcsimalts = array();
    var $adjusted_data = array();
	
//---------------
// CONSTRUCTOR
    function PiePlot($data) {
	$this->data = array_reverse($data);
	$this->title = new Text("");
	$this->title->SetFont(FF_FONT1,FS_BOLD);
	$this->value = new DisplayValue();
	$this->value->Show();
	$this->value->SetFormat('%.1f%%');
    }

//---------------
// PUBLIC METHODS	
    function SetCenter($x,$y=0.5) {
	$this->posx = $x;
	$this->posy = $y;
    }

    function SetColor($aColor) {
	$this->color = $aColor;
    }
	
    function SetSliceColors($aColors) {
	$this->setslicecolors = $aColors;
    }
	
    function SetShadow($aColor='darkgray',$aDropWidth=4) {
	$this->ishadowcolor = $aColor;
	$this->ishadowdrop = $aDropWidth;
    }

    function SetCSIMTargets($targets,$alts=null) {
	$this->csimtargets=array_reverse($targets);
	if( is_array($alts) )
	    $this->csimalts=array_reverse($alts);
    }
	
    function GetCSIMareas() {
	return $this->csimareas;
    }

    function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {  
        //Slice number, ellipse centre (x,y), height, width, start angle, end angle
	while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
	while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;

	$sa = 2*M_PI - $sa;
	$ea = 2*M_PI - $ea;

	//add coordinates of the centre to the map
	$coords = "$xc, $yc";

	//add coordinates of the first point on the arc to the map
	$xp = floor(($radius*cos($ea))+$xc);
	$yp = floor($yc-$radius*sin($ea));
	$coords.= ", $xp, $yp";
	
	//add coordinates every 0.2 radians
	$a=$ea+0.2;
	while ($a<$sa) {
	    $xp = floor($radius*cos($a)+$xc);
	    $yp = floor($yc-$radius*sin($a));
	    $coords.= ", $xp, $yp";
	    $a += 0.2;
	}
		
	//Add the last point on the arc
	$xp = floor($radius*cos($sa)+$xc);
	$yp = floor($yc-$radius*sin($sa));
	$coords.= ", $xp, $yp";
	if( !empty($this->csimtargets[$i]) ) {
	    $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
		$this->csimtargets[$i]."\"";
	    if( !empty($this->csimalts[$i]) ) {
		$tmp=sprintf($this->csimalts[$i],$this->data[$i]);
		$this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
	    }
	    $this->csimareas .= ">\n";
	}
    }

	
    function SetTheme($aTheme) {
	if( in_array($aTheme,array_keys($this->themearr)) )
	    $this->theme = $aTheme;
	else
	    JpGraphError::Raise("PiePLot::SetTheme() Unknown theme: $aTheme");
    }
	
    function ExplodeSlice($e,$radius=20) {
	if( ! is_integer($e) ) 
	    JpGraphError::Raise('Argument to PiePlot::ExplodeSlice() must be an integer');
	$this->explode_radius[$e]=$radius;
    }

    function ExplodeAll($radius=20) {
	$this->explode_all=true;
	$this->explode_r = $radius;
    }

    function Explode($aExplodeArr) {
	if( !is_array($aExplodeArr) ) {
	    JpGraphError::Raise("Argument to PiePlot::Explode() must be an array with integer distances.");
	}
	$this->explode_radius = $aExplodeArr;
    }

    function SetStartAngle($aStart) {
	if( $aStart < 0 || $aStart > 360 ) {
	    JpGraphError::Raise('Slice start angle must be between 0 and 360 degrees.');
	}
	$this->startangle = 360-$aStart;
	$this->startangle *= M_PI/180;
    }
	
    function SetFont($family,$style=FS_NORMAL,$size=10) {
		JpGraphError::Raise('PiePlot::SetFont() is deprecated. Use PiePlot->value->SetFont() instead.');
    }
	
    // Size in percentage
    function SetSize($aSize) {
	if( ($aSize>0 && $aSize<=0.5) || ($aSize>10 && $aSize<1000) )
	    $this->radius = $aSize;
	else
	    JpGraphError::Raise("PiePlot::SetSize() Radius for pie must either be specified as a fraction
                                [0, 0.5] of the size of the image or as an absolute size in pixels 
                                in the range [10, 1000]");
    }
	
    function SetFontColor($aColor) {
	JpGraphError::Raise('PiePlot::SetFontColor() is deprecated. Use PiePlot->value->SetColor() instead.');
    }
	
    // Set label arrays
    function SetLegends($aLegend) {
	$this->legends = $aLegend;
    }

    // Set text labels for slices 
    function SetLabels($aLabels,$aLblPosAdj="auto") {
	$this->labels = array_reverse($aLabels);
	$this->ilabelposadj=$aLblPosAdj;
    }

    function SetLabelPos($aLblPosAdj) {
	$this->ilabelposadj=$aLblPosAdj;
    }
	
    // Should we display actual value or percentage?
    function SetLabelType($t) {
	if( $t < 0 || $t > 2 ) 
	    JpGraphError::Raise("PiePlot::SetLabelType() Type for pie plots must be 0 or 1 (not $t).");
	$this->labeltype=$t;
    }

    function SetValueType($aType) {
	$this->SetLabelType($aType);
    }

    // Should the circle around a pie plot be displayed
    function ShowBorder($exterior=true,$interior=true) {
	$this->pie_border = $exterior;
	$this->pie_interior_border = $interior;
    }
	
    // Setup the legends
    function Legend(&$graph) {
	$colors = array_keys($graph->img->rgb->rgb_table);
   	sort($colors);	
   	$ta=$this->themearr[$this->theme];	
   	$n = count($this->data);

   	if( $this->setslicecolors==null ) {
	    $numcolors=count($ta);
	    if( get_class($this)==='pieplot3d' ) {
		$ta = array_reverse(array_slice($ta,0,$n));
	    }
	}
   	else {
	    $this->setslicecolors = array_slice($this->setslicecolors,0,$n);
	    $numcolors=count($this->setslicecolors); 
	    if( $graph->pieaa && get_class($this)==='pieplot' ) { 
		$this->setslicecolors = array_reverse($this->setslicecolors);
	    }
	}
		
	$sum=0;
	for($i=0; $i < $n; ++$i)
	    $sum += $this->data[$i];

	// Bail out with error if the sum is 0
	if( $sum==0 )
	    JpGraphError::Raise("Illegal pie plot. Sum of all data is zero for Pie!");

	// Make sure we don't plot more values than data points
	// (in case the user added more legends than data points)
	$n = min(count($this->legends),count($this->data));
	if( $this->legends != "" ) {
	    $this->legends = array_reverse(array_slice($this->legends,0,$n));
	}
	for( $i=$n-1; $i >= 0; --$i ) {
	    $l = $this->legends[$i];
	    // Replace possible format with actual values
	    if( $this->labeltype==0 ) {
		$l = sprintf($l,100*$this->data[$i]/$sum);
		$alt = sprintf($this->csimalts[$i],$this->data[$i]);

	    }
	    elseif( $this->labeltype == 1)  {
		$l = sprintf($l,$this->data[$i]);
		$alt = sprintf($this->csimalts[$i],$this->data[$i]);
	    
	    }
	    else {
		$l = sprintf($l,$this->adjusted_data[$i]);
		$alt = sprintf($this->csimalts[$i],$this->adjusted_data[$i]);
	    }
	    
				
	    if( $this->setslicecolors==null ) {
		$graph->legend->Add($l,$colors[$ta[$i%$numcolors]],"",0,
				    $this->csimtargets[$i],$alt);
	    }
	    else {
		$graph->legend->Add($l,$this->setslicecolors[$i%$numcolors],"",0,
				    $this->csimtargets[$i],$alt);
	    }
	}
    }
	
    // Adjust the rounded percetage value so that the sum of
    // of the pie slices are always 100%
    // Using the Hare/Niemeyer method
    function AdjPercentage($aData,$aPrec=0) {
	$mul=100;
	if( $aPrec > 0 && $aPrec < 3 ) {
	    if( $aPrec == 1 ) 
		$mul=1000;
		else
		    $mul=10000;
	}
	
	$tmp = array();
	$result = array();
	$quote_sum=0;
	$n = count($aData) ;
	for( $i=0, $sum=0; $i < $n; ++$i )
	    $sum+=$aData[$i];
	foreach($aData as $index => $value) {
	    $tmp_percentage=$value/$sum*$mul;
	    $result[$index]=floor($tmp_percentage);
	    $tmp[$index]=$tmp_percentage-$result[$index];
	    $quote_sum+=$result[$index];
	}
	if( $quote_sum == $mul) {
	    if( $mul > 100 ) {
		$tmp = $mul / 100;
		for( $i=0; $i < $n; ++$i ) {
		    $result[$i] /= $tmp ;
		}
	    }
	    return $result;
	}
	arsort($tmp,SORT_NUMERIC);
	reset($tmp);
	for($i=0; $i < $mul-$quote_sum; $i++)
	{
	    $result[key($tmp)]++;
	    next($tmp);
	}
	if( $mul > 100 ) {
	    $tmp = $mul / 100;
	    for( $i=0; $i < $n; ++$i ) {
		$result[$i] /= $tmp ;
	    }
	}
	return $result;
    }


    function Stroke(&$img,$aaoption=0) {
	// aaoption is used to handle antialias
	// aaoption == 0 a normal pie
	// aaoption == 1 just the body
	// aaoption == 2 just the values

	// Explode scaling. If anti anti alias we scale the image
	// twice and we also need to scale the exploding distance
	$expscale = $aaoption === 1 ? 2 : 1;

	if( $this->labeltype == 2 ) {
	    // Adjust the data so that it will add up to 100%
	    $this->adjusted_data = $this->AdjPercentage($this->data);
	}

	$colors = array_keys($img->rgb->rgb_table);
   	sort($colors);	
   	$ta=$this->themearr[$this->theme];	
	$n = count($this->data);
   	
   	if( $this->setslicecolors==null ) {
	    $numcolors=count($ta);
	}
   	else {
	    // We need to create an array of colors as long as the data
	    // since we need to reverse it to get the colors in the right order
	    $numcolors=count($this->setslicecolors); 
	    if( $n > $numcolors ) {
		$i = 2*$numcolors;
		while( $n > $i ) {
		    $this->setslicecolors = array_merge($this->setslicecolors,$this->setslicecolors);
		    $i += $n;
		}
		$tt = array_slice($this->setslicecolors,0,$n % $numcolors);
		$this->setslicecolors = array_merge($this->setslicecolors,$tt);
		$this->setslicecolors = array_reverse($this->setslicecolors);
	    }
	}

	// Draw the slices
	$sum=0;
	for($i=0; $i < $n; ++$i)
	    $sum += $this->data[$i];
	
	// Bail out with error if the sum is 0
	if( $sum==0 )
	    JpGraphError::Raise("Sum of all data is 0 for Pie.");
	
	// Set up the pie-circle
	if( $this->radius <= 1 )
	    $radius = floor($this->radius*min($img->width,$img->height));
	else {
	    $radius = $aaoption === 1 ? $this->radius*2 : $this->radius;
	}

	if( $this->posx <= 1 && $this->posx > 0 )
	    $xc = round($this->posx*$img->width);
	else
	    $xc = $this->posx ;
	
	if( $this->posy <= 1 && $this->posy > 0 )
	    $yc = round($this->posy*$img->height);
	else
	    $yc = $this->posy ;
		
	$n = count($this->data);

	if( $this->explode_all )
	    for($i=0; $i < $n; ++$i)
		$this->explode_radius[$i]=$this->explode_r;

	if( $this->ishadowcolor != "" && $aaoption !== 2) {
	    $accsum=0;
	    $angle2 = $this->startangle;
	    $img->SetColor($this->ishadowcolor);
	    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
	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 StrokeAllLabels($img,$xc,$yc,$radius) {
	$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-$i-1]); 
	}
    }

    // 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";
	    
	    // 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) {
		$r *= $this->ilabelposadj;
	    }

	    $r += $img->GetFontHeight()/1.5 + $this->value->margin ;
	    $xt=round($r*cos($a)+$xc);
	    $yt=round($yc-$r*sin($a));

	    while( $a < 0 ) $a += 2*M_PI;
	    while( $a > 2*M_PI ) $a -= 2*M_PI;

	    if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
	    if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI; 
	    if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
	    if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
	    
	    if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
	    if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
	    if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
	    if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
	    if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
	    
	    $oldmargin = $this->value->margin ;
	    $this->value->margin = 0;
	    $this->value->Stroke($img,$label,$xt-$dx*$w,$yt-$dy*$h);
	    $this->value->margin = $oldmargin ;
	}
    }	
} // Class


//===================================================
// CLASS PiePlotC
// Description: Same as a normal pie plot but with a 
// filled circle in the center
//===================================================
class PiePlotC extends PiePlot {
    var $imidsize=0.5;		// Fraction of total width
    var $imidcolor='white';
    var $midtitle='';
    var $middlecsimtarget="",$middlecsimalt="";

    function PiePlotC($data,$aCenterTitle='') {
	parent::PiePlot($data);
	$this->midtitle = new Text();
	$this->midtitle->ParagraphAlign('center');
    }

    function SetMid($aTitle,$aColor='white',$aSize=0.5) {
	$this->midtitle->Set($aTitle);
	$this->imidsize = $aSize ; 
	$this->imidcolor = $aColor ; 
    }

    function SetMidTitle($aTitle) {
	$this->midtitle->Set($aTitle);
    }

    function SetMidSize($aSize) {
	$this->imidsize = $aSize ; 
    }

    function SetMidColor($aColor) {
	$this->imidcolor = $aColor ; 
    }

    function SetMidCSIM($aTarget,$aAlt) {
	$this->middlecsimtarget = $aTarget;
	$this->middlecsimalt = $aAlt;
    }

    function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {  
        //Slice number, ellipse centre (x,y), radius, start angle, end angle
	while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
	while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;

	$sa = 2*M_PI - $sa;
	$ea = 2*M_PI - $ea;

	// Add inner circle first point
	$xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
	$yp = floor($yc-($this->imidsize*$radius*sin($ea)));
	$coords = "$xp, $yp";
	
	//add coordinates every 0.25 radians
	$a=$ea+0.25;
	while ($a < $sa) {
	    $xp = floor(($this->imidsize*$radius*cos($a)+$xc));
	    $yp = floor($yc-($this->imidsize*$radius*sin($a)));
	    $coords.= ", $xp, $yp";
	    $a += 0.25;
	}

	// Make sure we end at the last point
	$xp = floor(($this->imidsize*$radius*cos($sa)+$xc));
	$yp = floor($yc-($this->imidsize*$radius*sin($sa)));
	$coords.= ", $xp, $yp";

	// Straight line to outer circle
	$xp = floor($radius*cos($sa)+$xc);
	$yp = floor($yc-$radius*sin($sa));
	$coords.= ", $xp, $yp";	

	//add coordinates every 0.25 radians
	$a=$sa - 0.25;
	while ($a > $ea) {
	    $xp = floor($radius*cos($a)+$xc);
	    $yp = floor($yc-$radius*sin($a));
	    $coords.= ", $xp, $yp";
	    $a -= 0.25;
	}
		
	//Add the last point on the arc
	$xp = floor($radius*cos($ea)+$xc);
	$yp = floor($yc-$radius*sin($ea));
	$coords.= ", $xp, $yp";

	// Close the arc
	$xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
	$yp = floor($yc-($this->imidsize*$radius*sin($ea)));
	$coords .= ", $xp, $yp";

	if( !empty($this->csimtargets[$i]) ) {
	    $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
		$this->csimtargets[$i]."\"";
	    if( !empty($this->csimalts[$i]) ) {
		$tmp=sprintf($this->csimalts[$i],$this->data[$i]);
		$this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
	    }
	    $this->csimareas .= ">\n";
	}
    }


    function Stroke($img,$aaoption=0) {

	// Stroke the pie but don't stroke values
	$tmp =  $this->value->show;
	$this->value->show = false;
	parent::Stroke($img,$aaoption);
	$this->value->show = $tmp;

 	$xc = round($this->posx*$img->width);
	$yc = round($this->posy*$img->height);

	$radius = floor($this->radius * min($img->width,$img->height)) ;


	if( $this->imidsize > 0 && $aaoption !== 2 ) {

	    if( $this->ishadowcolor != "" ) {
		$img->SetColor($this->ishadowcolor);
		$img->FilledCircle($xc+$this->ishadowdrop,$yc+$this->ishadowdrop,
				   round($radius*$this->imidsize));
	    }

	    $img->SetColor($this->imidcolor);
	    $img->FilledCircle($xc,$yc,round($radius*$this->imidsize));

	    if(  $this->pie_border && $aaoption === 0 ) {
		$img->SetColor($this->color);
		$img->Circle($xc,$yc,round($radius*$this->imidsize));
	    }

	    if( !empty($this->middlecsimtarget) )
		$this->AddMiddleCSIM($xc,$yc,round($radius*$this->imidsize));

	}

	if( $this->value->show && $aaoption !== 1) {
	    $this->StrokeAllLabels($img,$xc,$yc,$radius);
	    $this->midtitle->Pos($xc,$yc,'center','center');
	    $this->midtitle->Stroke($img);
	}

    }

    function AddMiddleCSIM($xc,$yc,$r) {
	$this->csimareas .= "<area shape=\"circle\" coords=\"$xc,$yc,$r\" href=\"".
	    $this->middlecsimtarget."\"";
	if( !empty($this->middlecsimalt) ) {
	    $tmp = $this->middlecsimalt;
	    $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
	}
	$this->csimareas .= ">\n";
    }

    function StrokeLabel($label,$img,$xc,$yc,$a,$r) {

	if( $this->ilabelposadj === 'auto' )
	    $this->ilabelposadj = (1-$this->imidsize)/2+$this->imidsize;

	parent::StrokeLabel($label,$img,$xc,$yc,$a,$r);

    }

}


//===================================================
// CLASS PieGraph
// Description: 
//===================================================
class PieGraph extends Graph {
    var $posx, $posy, $radius;		
    var $legends=array();	
    var $plots=array();
    var $pieaa = false ;
//---------------
// CONSTRUCTOR
    function PieGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
	$this->Graph($width,$height,$cachedName,$timeout,$inline);
	$this->posx=$width/2;
	$this->posy=$height/2;
	$this->SetColor(array(255,255,255));		
    }

//---------------
// PUBLIC METHODS	
    function Add($aObj) {

	if( is_array($aObj) && count($aObj) > 0 )
	    $cl = get_class($aObj[0]);
	else
	    $cl = get_class($aObj);

	if( $cl == 'text' ) 
	    $this->AddText($aObj);
	elseif( $cl == 'iconplot' ) 
	    $this->AddIcon($aObj);
	else
	    $this->plots[] = $aObj;
    }

    function SetAntiAliasing($aFlg=true) {
	$this->pieaa = $aFlg;
    }
	
    function SetColor($c) {
	$this->SetMarginColor($c);
    }


    function DisplayCSIMAreas() {
	    $csim="";
	    foreach($this->plots as $p ) {
		$csim .= $p->GetCSIMareas();
	    }
	    //$csim.= $this->legend->GetCSIMareas();
	    if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
		$this->img->SetColor($this->csimcolor);
		for ($i=0; $i<count($coords[0]); $i++) {
		    if ($coords[1][$i]=="poly") {
			preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
			$this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
			for ($j=0; $j<count($pts[0]); $j++) {
			    $this->img->LineTo($pts[1][$j],$pts[2][$j]);
			}
		    } else if ($coords[1][$i]=="rect") {
			$pts = preg_split('/,/', $coords[2][$i]);
			$this->img->SetStartPoint($pts[0],$pts[1]);
			$this->img->LineTo($pts[2],$pts[1]);
			$this->img->LineTo($pts[2],$pts[3]);
			$this->img->LineTo($pts[0],$pts[3]);
			$this->img->LineTo($pts[0],$pts[1]);
						
		    }
		}
	    }
    }

    // Method description
    function Stroke($aStrokeFileName="") {
	// If the filename is the predefined value = '_csim_special_'
	// we assume that the call to stroke only needs to do enough
	// to correctly generate the CSIM maps.
	// We use this variable to skip things we don't strictly need
	// to do to generate the image map to improve performance
	// a best we can. Therefor you will see a lot of tests !$_csim in the
	// code below.
	$_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);

	// We need to know if we have stroked the plot in the
	// GetCSIMareas. Otherwise the CSIM hasn't been generated
	// and in the case of GetCSIM called before stroke to generate
	// CSIM without storing an image to disk GetCSIM must call Stroke.
	$this->iHasStroked = true;

		
		
	$n = count($this->plots);

	if( $this->pieaa ) {

	    if( !$_csim ) {
		if( $this->background_image != "" ) {
		    $this->StrokeFrameBackground();		
		}
		else {
		    $this->StrokeFrame();		
		}
	    }


	    $w = $this->img->width;
	    $h = $this->img->height;
	    $oldimg = $this->img->img;

	    $this->img->CreateImgCanvas(2*$w,2*$h);
	    
	    $this->img->SetColor( $this->margin_color );
	    $this->img->FilledRectangle(0,0,2*$w-1,2*$h-1);

	    // Make all icons *2 i size since we will be scaling down the
	    // imahe to do the anti aliasing
	    $ni = count($this->iIcons);
	    for($i=0; $i < $ni; ++$i) {
		$this->iIcons[$i]->iScale *= 2 ;
	    }
	    $this->StrokeIcons();

	    for($i=0; $i < $n; ++$i) {
		if( $this->plots[$i]->posx > 1 ) 
		    $this->plots[$i]->posx *= 2 ;
		if( $this->plots[$i]->posy > 1 ) 
		    $this->plots[$i]->posy *= 2 ;

		$this->plots[$i]->Stroke($this->img,1);

		if( $this->plots[$i]->posx > 1 ) 
		    $this->plots[$i]->posx /= 2 ;
		if( $this->plots[$i]->posy > 1 ) 
		    $this->plots[$i]->posy /= 2 ;
	    }

	    $indent = $this->doframe ? ($this->frame_weight + ($this->doshadow ? $this->shadow_width : 0 )) : 0 ;
	    $indent += $this->framebevel ? $this->framebeveldepth + 1 : 0 ;
	    $this->img->CopyCanvasH($oldimg,$this->img->img,$indent,$indent,$indent,$indent,
				    $w-2*$indent,$h-2*$indent,2*($w-$indent),2*($h-$indent));

	    $this->img->img = $oldimg ;
	    $this->img->width = $w ;
	    $this->img->height = $h ;

	    for($i=0; $i < $n; ++$i) {
		$this->plots[$i]->Stroke($this->img,2); // Stroke labels
		$this->plots[$i]->Legend($this);
	    }

	}
	else {

	    if( !$_csim ) {
		if( $this->background_image != "" ) {
		    $this->StrokeFrameBackground();		
		}
		else {
		    $this->StrokeFrame();		
		}
	    }

	    $this->StrokeIcons();

	    for($i=0; $i < $n; ++$i) {
		$this->plots[$i]->Stroke($this->img);
		$this->plots[$i]->Legend($this);
	    }
	}


	$this->legend->Stroke($this->img);
	$this->footer->Stroke($this->img);

	if( !$_csim ) {	
	    $this->StrokeTitles();

	    // Stroke texts
	    if( $this->texts != null ) {
		$n = count($this->texts);
		for($i=0; $i < $n; ++$i ) {
		    $this->texts[$i]->Stroke($this->img);
		}
	    }

	    if( _JPG_DEBUG ) {
		$this->DisplayCSIMAreas();
	    }

	    // Should we do any final image transformation
	    if( $this->iImgTrans ) {
		if( !class_exists('ImgTrans') ) {
		    require_once('jpgraph_imgtrans.php');
		    //JpGraphError::Raise('In order to use image transformation you must include the file jpgraph_imgtrans.php in your script.');
		}
	       
		$tform = new ImgTrans($this->img->img);
		$this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
						 $this->iImgTransDirection,$this->iImgTransHighQ,
						 $this->iImgTransMinSize,$this->iImgTransFillColor,
						 $this->iImgTransBorder);
	    }


	    // If the filename is given as the special "__handle"
	    // then the image handler is returned and the image is NOT
	    // streamed back
	    if( $aStrokeFileName == _IMG_HANDLER ) {
		return $this->img->img;
	    }
	    else {
		// Finally stream the generated picture					
		$this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
					   $aStrokeFileName);		
	    }
	}
    }
} // Class

/* EOF */
?>
jpgraph-1.16/src/jpgraph_regstat.php0100644000076400001440000000646610065253722016307 0ustar  ljpusers<?php 
/*=======================================================================
// File:	JPGRAPH_REGSTAT.PHP
// Description: Regression and statistical analysis helper classes
// Created: 	2002-12-01
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: jpgraph_regstat.php,v 1.2 2003/03/08 11:29:21 aditus Exp $
//
// License:	This code is released under QPL
// Copyright (C) 2002 Johan Persson
//========================================================================
*/

//------------------------------------------------------------------------
// CLASS Spline
// Create a new data array from an existing data array but with more points.
// The new points are interpolated using a cubic spline algorithm
//------------------------------------------------------------------------
class Spline {
    // 3:rd degree polynom approximation

    var $xdata,$ydata;   // Data vectors
    var $y2;		 // 2:nd derivate of ydata	
    var $n=0;

    function Spline($xdata,$ydata) {
	$this->y2 = array();
	$this->xdata = $xdata;
	$this->ydata = $ydata;

	$n = count($ydata);
	$this->n = $n;

	// Natural spline 2:derivate == 0 at endpoints
	$this->y2[0]    = 0.0;
	$this->y2[$n-1] = 0.0;
	$delta[0] = 0.0;

	// Calculate 2:nd derivate
	for($i=1; $i < $n-1; ++$i) {
	    $d = ($xdata[$i+1]-$xdata[$i-1]);
	    if( $d == 0  ) {
		JpGraphError::Raise('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
	    }
	    $s = ($xdata[$i]-$xdata[$i-1])/$d;
	    $p = $s*$this->y2[$i-1]+2.0;
	    $this->y2[$i] = ($s-1.0)/$p;
	    $delta[$i] = ($ydata[$i+1]-$ydata[$i])/($xdata[$i+1]-$xdata[$i]) - 
		         ($ydata[$i]-$ydata[$i-1])/($xdata[$i]-$xdata[$i-1]);
	    $delta[$i] = (6.0*$delta[$i]/($xdata[$i+1]-$xdata[$i-1])-$s*$delta[$i-1])/$p;
	}

	// Backward substitution
	for( $j=$n-2; $j >= 0; --$j ) {
	    $this->y2[$j] = $this->y2[$j]*$this->y2[$j+1] + $delta[$j];
	}
    }

    // Return the two new data vectors
    function Get($num=50) {
	$n = $this->n ;
	$step = ($this->xdata[$n-1]-$this->xdata[0]) / ($num-1);
	$xnew=array();
	$ynew=array();
	$xnew[0] = $this->xdata[0];
	$ynew[0] = $this->ydata[0];
	for( $j=1; $j < $num; ++$j ) {
	    $xnew[$j] = $xnew[0]+$j*$step;
	    $ynew[$j] = $this->Interpolate($xnew[$j]);
	}
	return array($xnew,$ynew);
    }

    // Return a single interpolated Y-value from an x value
    function Interpolate($xpoint) {

	$max = $this->n-1;
	$min = 0;

	// Binary search to find interval
	while( $max-$min > 1 ) {
	    $k = ($max+$min) / 2;
	    if( $this->xdata[$k] > $xpoint ) 
		$max=$k;
	    else 
		$min=$k;
	}	

	// Each interval is interpolated by a 3:degree polynom function
	$h = $this->xdata[$max]-$this->xdata[$min];

	if( $h == 0  ) {
	    JpGraphError::Raise('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
	}


	$a = ($this->xdata[$max]-$xpoint)/$h;
	$b = ($xpoint-$this->xdata[$min])/$h;
	return $a*$this->ydata[$min]+$b*$this->ydata[$max]+
	     (($a*$a*$a-$a)*$this->y2[$min]+($b*$b*$b-$b)*$this->y2[$max])*($h*$h)/6.0;
    }
}

// EOF
?>jpgraph-1.16/src/imgdata_diamonds.inc0100644000076400001440000002032610065253722016360 0ustar  ljpusers<?php
//=======================================================================
// File:	IMGDATA_DIAMONDS.INC
// Description:	Base64 encoded images for diamonds
// Created: 	2003-03-20
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: imgdata_diamonds.inc,v 1.1 2003/03/23 13:37:36 aditus Exp $
//
// License:	This code is released under QPL 1.0 
// Copyright (C) 2003 Johan Persson 
//========================================================================

class ImgData_Diamonds extends ImgData {
    var $name = 'Diamonds';
    var $an = array(MARK_IMG_DIAMOND =>'imgdata');
    var $colors = array('lightblue','darkblue','gray', 
			'blue','pink','purple','red','yellow');
    var $index  = array('lightblue' =>7,'darkblue'=>2,'gray'=>6, 
			'blue'=>4,'pink'=>1,'purple'=>5,'red'=>0,'yellow'=>3);

    var $maxidx = 7 ;
    var $imgdata ;

    function ImgData_Diamonds() {
//==========================================================
// File: diam_red.png
//==========================================================
	$this->imgdata[0][0]= 668 ;
	$this->imgdata[0][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/F'.
	    'BMVEX///////+cAAD/AADOAABjAABrAADWGBjOCAj/CAj/GBj/'.
	    'EBCcCAiMOTl7KSl7ISFzGBilGBjOEBBrCAjv5+eMQkK1QkKtMT'.
	    'GtKSnWKSn/KSlzEBCcEBDexsb/tbXOe3ucWlqcUlKUSkr/e3vn'.
	    'a2u9UlL/a2uEMTHeUlLeSkqtOTn/UlL/SkrWOTn/QkL/OTmlIS'.
	    'H/MTH/ISH39/f/9/f35+fezs7/5+fvzs7WtbXOra3nvb3/zs7G'.
	    'nJzvtbXGlJTepaW9jIy1hITWlJS1e3uta2ulY2P/lJTnhITne3'.
	    'vGY2O9Wlr/c3PeY2O1Skr/Y2P/WlreQkLWISGlEBCglEUaAAAA'.
	    'AXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAA'.
	    'sSAdLdfvwAAAAHdElNRQfTAwsWEw5WI4qnAAABGUlEQVR4nHXQ'.
	    '1XLDMBAFUKUCM1NiO8zcpIxpp8z0//9SWY7b2LHv6EU6s1qtAN'.
	    'iMBAojLPkigpJvogKC4pxDuQipjanlICXof1RQDkYEF21mKIfg'.
	    '/GGKtjAmOKt9oSyuCU7OhyiDCQnjowGfRnooCJIkiWJvv8NxnG'.
	    'nyNAwFcekvZpPP3mu7Vrp8fOq8DYbTyjdnAvBj7Jbd7nP95urs'.
	    '+MC2D6unF+Cu0VJULQBAlsOQuueN3Hrp2nGUvqppemBZ0aU7Se'.
	    'SXvYZFMKaLJn7MH3btJmZEMEmGSOreqy0SI/4ffo3uiUOYEACy'.
	    'OFopmNWlP5uZd9uPWmUoxvK9ilO9NtBo6mS7KkZD0fOJYqgGBU'.
	    'S/T7OKCAA9tfsFOicXcbxt29cAAAAASUVORK5CYII=' ; 

//==========================================================
// File: diam_pink.png
//==========================================================
	$this->imgdata[1][0]= 262 ;
	$this->imgdata[1][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
	    'BMVEX///+AgID/M5n/Zpn/zMz/mZn1xELhAAAAAXRSTlMAQObY'.
	    'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
	    'AHdElNRQfTAwsWEi3tX8qUAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
	    'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
	    '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
	    'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
	    '==' ; 

//==========================================================
// File: diam_blue.png
//==========================================================
	$this->imgdata[2][0]= 662 ;
	$this->imgdata[2][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA+V'.
	    'BMVEX///+AgIAAAJwAAP8AAM4AAGMAAGsQEP8YGHMQEHMYGP8Q'.
	    'EKUICJwICM5KSpQxMYQpKXsYGNYQEM4ICGsICP97e85aWpw5OY'.
	    'xSUv85ObVCQt4xMa0pKa0hIaUpKf+9vd6EhLVra+dzc/9SUr1r'.
	    'a/9aWt5SUt5CQrVaWv9KSv8hIXs5Of8xMf8pKdYhIdYYGKUhIf'.
	    '/Ozs739//v7/fn5+/v7//n5/fW1ufOzufOzu/W1v+trc69veel'.
	    'pc6trd6UlMa9vf+MjL21tfe1tf+UlNZzc61ra6Wlpf+EhOeMjP'.
	    '9ra8ZSUpyEhP9CQoxKSrVCQv85Od4xMdYQENZnJhlWAAAAAXRS'.
	    'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
	    'LdfvwAAAAHdElNRQfTAwsWEx3Snct5AAABFklEQVR4nHXR5XbD'.
	    'IBgGYM6AuHsaqbvOfeuknev9X8xISbplSd5/8JyXwwcA/I0AKm'.
	    'PFchVBdvKNKggKQx2VIoRwMZihMiQE49YUlWBCcPL0hYq4ITh+'.
	    'qKECUoLDZWqoQNA766F/mJHlHXblPJJNiyURhM5eU9cNw5BlmS'.
	    'IrLOLxhzfotF7vwO2j3ez2ap/TmW4AIM7DoN9+tu+vLk6Pdg9O'.
	    '6ufXjfXLm6pxPACSJIpRFAa+/26DhuK6qjbiON40k0N3skjOvm'.
	    'NijBmchF5mi+1jhQqDmWyIzPp1hUlrv8On5l+6mMm1tigFNyrt'.
	    '5R97g+FKKyGKkTNKesXPJTZXOFIrUoKiypcTQVHjK4g8H2dWEQ'.
	    'B8bvUDLSQXSr41rmEAAAAASUVORK5CYII=' ; 

//==========================================================
// File: diam_yellow.png
//==========================================================
	$this->imgdata[3][0]= 262 ;
	$this->imgdata[3][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
	    'BMVEX///+AgIBmMwCZZgD/zADMmQD/QLMZAAAAAXRSTlMAQObY'.
	    'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
	    'AHdElNRQfTAwsWEwcv/zIDAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
	    'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
	    '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
	    'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
	    '==' ; 

//==========================================================
// File: diam_lightblue.png
//==========================================================
	$this->imgdata[4][0]= 671 ;
	$this->imgdata[4][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/1'.
	    'BMVEX///+AgIAAnP8A//8Azv8AY/8Aa/8I//8Y1v8Izv8Y//8Q'.
	    '//8InP8Qzv8Ypf85jP8he/8Yc/8Ia/8pe/8p//8p1v9Ctf8xrf'.
	    '8prf8QnP8Qc/9CjP+1//97//9r//9S//9K//9C//85//8x//8h'.
	    '//9r5/9K3v9S3v851v97zv9Svf85rf8hpf/G3v9SnP9anP9KlP'.
	    '8xhP/n7//v7+f3///n///O//+U//9z//9j//9a//975/9C3v8h'.
	    '1v+E5/+17/9j3v/O7//n9/+95/+l3v9jxv+U1v8Qpf9avf9Ktf'.
	    '+Uxv+11v97tf9rrf+cxv+Mvf9jpf+tzv+Etf/O3v/39/8Akkxr'.
	    'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'.
	    'IAAAsSAdLdfvwAAAAHdElNRQfTAwsWEiHk6Ya/AAABGUlEQVR4'.
	    'nHXQ13KDMBAF0J2o0E01GHDvJa7p3em95/+/JQJMYjDc0Yt0Zr'.
	    'VaAaxHgtxwbSGPkGQpOIeQ2ORxJiJmNWYZyAhZR0WcgQGhViU0'.
	    'nEGoedDHGxgRapRPcRpXhOr7XZzCmLjaXk9IIjvkOEmSRLG62+'.
	    'F5XlEElhA5sW21GvXj6mGlDBfnJ51lr9svnvEKwH1hu2QPbwd3'.
	    'N9eXVzuL7/Hn29frdKaamgcgy67L3HFG9gDefV+dm5qme4YRXL'.
	    'oVR374mRqUELZYosf84XAxISFRQuMh4rrH8YxGSP6HX6H97NNQ'.
	    'KEAaR08qCeuSnx2a8zIPWqUowtKHSRK91rAw0elmVYQFVc8mhq'.
	    '7p5RD7Ps3IIwA9sfsFxFUX6eZ4Zh4AAAAASUVORK5CYII=' ; 

//==========================================================
// File: diam_purple.png
//==========================================================
	$this->imgdata[5][0]= 657 ;
	$this->imgdata[5][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/F'.
	    'BMVEX///////8xAP/OAP+cAP9jAP9rAP+cCP85CP/OEP9SKf/O'.
	    'CP9CEP9zGP9rCP+lGP/WOf/WIf9KIf9jOf+MQv+EMf97If9zEP'.
	    '+1Sv+lIf/ne//eUv/na//n5//Oxv/Wzv+chP9zUv97Wv9rQv9a'.
	    'Mf9KGP/v5/+te/97Kf+9Y/+tOf+tKf+lEP/vtf/WMf/WKf/v7+'.
	    'f39/+tnP+9rf9rSv9jQv9CGP+ljP+EY//Gtf+tlP+Ma/9zSv/e'.
	    'zv+UUv+9lP+cWv+lY/+cUv+MOf+EKf+UQv/Opf/OhP/Ga/+1Qv'.
	    '/Oe/+9Uv/ntf/eWv/eSv/WGP/3zv/vlP/WEP//9/+pL4oHAAAA'.
	    'AXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAA'.
	    'sSAdLdfvwAAAAHdElNRQfTAwsWEjX+M1LCAAABDklEQVR4nHXQ'.
	    '1bLDIBAGYFqIEW+ksbr7cXd3ff93OUCamdOE/Mxw882yywLwPz'.
	    '+gNKotlRFUVnNUQlCxTMRFCKEdE+MgpJaEiIOU4DKaoSIygtb3'.
	    'FBUQrm3xjPK4JvXjK0A5hFniYSBtIilQVYUm+X0KTVNiYah+2q'.
	    'ulFb8nUbSovD2+TCavwXQWmnMA6ro+di+uR5cPzfPhVqPV3N1p'.
	    'n3b3+rimAWAYhP3xnXd7P6oc9vadPsa1wYEs00dFQRAFehlX21'.
	    '25Sg9NOgwF5jeNTjVL9om0TjDc1lmeCKZ17nFPzhPtSRt6J06R'.
	    'WKUoeG3MoXRa/wjLHGLodwZcotPqjsYngnWslRBZH91hWTbpD2'.
	    'EdF1ECWW1SAAAAAElFTkSuQmCC' ; 

//==========================================================
// File: diam_gray.png
//==========================================================
	$this->imgdata[6][0]= 262 ;
	$this->imgdata[6][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
	    'BMVEX//////wAzMzNmZmbMzMyZmZlq4Qo5AAAAAXRSTlMAQObY'.
	    'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
	    'AHdElNRQfTAwsWExZFTxLxAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
	    'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
	    '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
	    'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
	    '==' ; 

//==========================================================
// File: diam_blgr.png
//==========================================================
	$this->imgdata[7][0]= 262 ;
	$this->imgdata[7][1]= 
	    'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
	    'BMVEX///+AgIBmzP9m///M//+Z//8hMmBVAAAAAXRSTlMAQObY'.
	    'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
	    'AHdElNRQfTAwsWEwCxm6egAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
	    'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
	    '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
	    'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
	    '==' ; 
    }
}

?>jpgraph-1.16/src/jpgraph_pie3d.php0100644000076400001440000006102110065253722015626 0ustar  ljpusers<?php
/*=======================================================================
// File:	JPGRAPH_PIE3D.PHP
// Description: 3D Pie plot extension for JpGraph
// Created: 	2001-03-24
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: jpgraph_pie3d.php,v 1.46.2.5 2004/06/19 12:22:04 aditus Exp $
//
// License:	This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/

//===================================================
// CLASS PiePlot3D
// Description: Plots a 3D pie with a specified projection 
// angle between 20 and 70 degrees.
//===================================================
class PiePlot3D extends PiePlot {
    var $labelhintcolor="red",$showlabelhint=true;
    var $angle=50;	
    var $edgecolor="", $edgeweight=1;
    var $iThickness=false;
	
//---------------
// CONSTRUCTOR
    function PiePlot3d(&$data) {
	$this->radius = 0.5;
	$this->data = $data;
	$this->title = new Text("");
	$this->title->SetFont(FF_FONT1,FS_BOLD);
	$this->value = new DisplayValue();
	$this->value->Show();
	$this->value->SetFormat('%.0f%%');
    }

//---------------
// PUBLIC METHODS	
	
    // Set label arrays
    function SetLegends($aLegend) {
	$this->legends = array_reverse($aLegend);
    }

    function SetSliceColors($aColors) {
	$this->setslicecolors = $aColors;
    }

    function Legend(&$aGraph) {
	parent::Legend($aGraph);
	$aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol);
    }

    function SetCSIMTargets($targets,$alts=null) {
	$this->csimtargets = $targets;
	$this->csimalts = $alts;
    }

    // Should the slices be separated by a line? If color is specified as "" no line
    // will be used to separate pie slices.
    function SetEdge($aColor,$aWeight=1) {
	$this->edgecolor = $aColor;
	$this->edgeweight = $aWeight;
    }

    // Specify projection angle for 3D in degrees
    // Must be between 20 and 70 degrees
    function SetAngle($a) {
	if( $a<5 || $a>90 )
	    JpGraphError::Raise("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees.");
	else
	    $this->angle = $a;
    }

    function AddSliceToCSIM($i,$xc,$yc,$height,$width,$thick,$sa,$ea) {  //Slice number, ellipse centre (x,y), height, width, start angle, end angle

	$sa *= M_PI/180;
	$ea *= M_PI/180;

	//add coordinates of the centre to the map
	$coords = "$xc, $yc";

	//add coordinates of the first point on the arc to the map
	$xp = floor($width*cos($sa)/2+$xc);
	$yp = floor($yc-$height*sin($sa)/2);
	$coords.= ", $xp, $yp";

	//If on the front half, add the thickness offset
	if ($sa >= M_PI && $sa <= 2*M_PI*1.01) {
	    $yp = floor($yp+$thick);
	    $coords.= ", $xp, $yp";
	}
		
	//add coordinates every 0.2 radians
	$a=$sa+0.2;
	while ($a<$ea) {
	    $xp = floor($width*cos($a)/2+$xc);
	    if ($a >= M_PI && $a <= 2*M_PI*1.01) {
		$yp = floor($yc-($height*sin($a)/2)+$thick);
	    } else {
		$yp = floor($yc-$height*sin($a)/2);
	    }
	    $coords.= ", $xp, $yp";
	    $a += 0.2;
	}
		
	//Add the last point on the arc
	$xp = floor($width*cos($ea)/2+$xc);
	$yp = floor($yc-$height*sin($ea)/2);


	if ($ea >= M_PI && $ea <= 2*M_PI*1.01) {
	    $coords.= ", $xp, ".floor($yp+$thick);
	}
	$coords.= ", $xp, $yp";
	$alt='';
	if( !empty($this->csimalts[$i]) ) {										
	    $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
	    $alt="alt=\"$tmp\" title=\"$tmp\"";
	}
	if( !empty($this->csimtargets[$i]) )
	    $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtargets[$i]."\" $alt>\n";
    }

    function SetLabels($aLabels,$aLblPosAdj="auto") {
	$this->labels = $aLabels;
	$this->ilabelposadj=$aLblPosAdj;
    }

	
    // Distance from the pie to the labels
    function SetLabelMargin($m) {
	$this->value->SetMargin($m);
    }
	
    // Show a thin line from the pie to the label for a specific slice
    function ShowLabelHint($f=true) {
	$this->showlabelhint=$f;
    }
	
    // Set color of hint line to label for each slice
    function SetLabelHintColor($c) {
	$this->labelhintcolor=$c;
    }

    function SetHeight($aHeight) {
      $this->iThickness = $aHeight;
    }


// Normalize Angle between 0-360
    function NormAngle($a) {
	// Normalize anle to 0 to 2M_PI
	// 
	if( $a > 0 ) {
	    while($a > 360) $a -= 360;
	}
	else {
	    while($a < 0) $a += 360;
	}
	if( $a < 0 )
	    $a = 360 + $a;

	if( $a == 360 ) $a=0;
	return $a;
    }

    

// Draw one 3D pie slice at position ($xc,$yc) with height $z
    function Pie3DSlice($img,$xc,$yc,$w,$h,$sa,$ea,$z,$fillcolor,$shadow=0.65) {
	
	// Due to the way the 3D Pie algorithm works we are
	// guaranteed that any slice we get into this method
	// belongs to either the left or right side of the
	// pie ellipse. Hence, no slice will cross 90 or 270
	// point.
	if( ($sa < 90 && $ea > 90) || ( ($sa > 90 && $sa < 270) && $ea > 270) ) {
	    JpGraphError::Raise('Internal assertion failed. Pie3D::Pie3DSlice');
	    exit(1);
	}

	$p[] = array();

	// Setup pre-calculated values
	$rsa = $sa/180*M_PI;	// to Rad
	$rea = $ea/180*M_PI;	// to Rad
	$sinsa = sin($rsa);
	$cossa = cos($rsa);
	$sinea = sin($rea);
	$cosea = cos($rea);

	// p[] is the points for the overall slice and
	// pt[] is the points for the top pie

	// Angular step when approximating the arc with a polygon train.
	$step = 0.05;

	if( $sa >= 270 ) {
	    if( $ea > 360 || ($ea > 0 && $ea <= 90) ) {
		if( $ea > 0 && $ea <= 90 ) {
		    // Adjust angle to simplify conditions in loops
		    $rea += 2*M_PI;
		}

		$p = array($xc,$yc,$xc,$yc+$z,
			   $xc+$w*$cossa,$z+$yc-$h*$sinsa);
		$pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);

		for( $a=$rsa; $a < 2*M_PI; $a += $step ) {
		    $tca = cos($a);
		    $tsa = sin($a);
		    $p[] = $xc+$w*$tca;
		    $p[] = $z+$yc-$h*$tsa;
		    $pt[] = $xc+$w*$tca;
		    $pt[] = $yc-$h*$tsa;
		}

		$pt[] = $xc+$w;
		$pt[] = $yc;

		$p[] = $xc+$w;
		$p[] = $z+$yc;
		$p[] = $xc+$w;
		$p[] = $yc;
		$p[] = $xc;
		$p[] = $yc;

		for( $a=2*M_PI+$step; $a < $rea; $a += $step ) {
		    $pt[] = $xc + $w*cos($a);
		    $pt[] = $yc - $h*sin($a);
		}
		    
		$pt[] = $xc+$w*$cosea;
		$pt[] = $yc-$h*$sinea;
		$pt[] = $xc;
		$pt[] = $yc;

	    }
	    else {
		$p = array($xc,$yc,$xc,$yc+$z,
			   $xc+$w*$cossa,$z+$yc-$h*$sinsa);
		$pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
		    
		$rea = $rea == 0.0 ? 2*M_PI : $rea;
		for( $a=$rsa; $a < $rea; $a += $step ) {
		    $tca = cos($a);
		    $tsa = sin($a);
		    $p[] = $xc+$w*$tca;
		    $p[] = $z+$yc-$h*$tsa;
		    $pt[] = $xc+$w*$tca;
		    $pt[] = $yc-$h*$tsa;
		}

		$pt[] = $xc+$w*$cosea;
		$pt[] = $yc-$h*$sinea;
		$pt[] = $xc;
		$pt[] = $yc;
		    
		$p[] = $xc+$w*$cosea;
		$p[] = $z+$yc-$h*$sinea;
		$p[] = $xc+$w*$cosea;
		$p[] = $yc-$h*$sinea;
		$p[] = $xc;
		$p[] = $yc;
	    }
	}
	elseif( $sa >= 180 ) {
	    $p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea);
	    $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
		
	    for( $a=$rea; $a>$rsa; $a -= $step ) {
		$tca = cos($a);
		$tsa = sin($a);
		$p[] = $xc+$w*$tca;
		$p[] = $z+$yc-$h*$tsa;
		$pt[] = $xc+$w*$tca;
		$pt[] = $yc-$h*$tsa;
	    }

	    $pt[] = $xc+$w*$cossa;
	    $pt[] = $yc-$h*$sinsa;
	    $pt[] = $xc;
	    $pt[] = $yc;
		
	    $p[] = $xc+$w*$cossa;
	    $p[] = $z+$yc-$h*$sinsa;
	    $p[] = $xc+$w*$cossa;
	    $p[] = $yc-$h*$sinsa;
	    $p[] = $xc;
	    $p[] = $yc;
	
	}
	elseif( $sa >= 90 ) {
	    if( $ea > 180 ) {
		$p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea);
		$pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);

		for( $a=$rea; $a > M_PI; $a -= $step ) {
		    $tca = cos($a);
		    $tsa = sin($a);		    
		    $p[] = $xc+$w*$tca;
		    $p[] = $z + $yc - $h*$tsa;
		    $pt[] = $xc+$w*$tca;
		    $pt[] = $yc-$h*$tsa;
		}

		$p[] = $xc-$w;
		$p[] = $z+$yc;
		$p[] = $xc-$w;
		$p[] = $yc;
		$p[] = $xc;
		$p[] = $yc;

		$pt[] = $xc-$w;
		$pt[] = $z+$yc;
		$pt[] = $xc-$w;
		$pt[] = $yc;

		for( $a=M_PI-$step; $a > $rsa; $a -= $step ) {
		    $pt[] = $xc + $w*cos($a);
		    $pt[] = $yc - $h*sin($a);
		}

		$pt[] = $xc+$w*$cossa;
		$pt[] = $yc-$h*$sinsa;
		$pt[] = $xc;
		$pt[] = $yc;

	    }
	    else { // $sa >= 90 && $ea <= 180
		$p = array($xc,$yc,$xc,$yc+$z,
			   $xc+$w*$cosea,$z+$yc-$h*$sinea,
			   $xc+$w*$cosea,$yc-$h*$sinea,
			   $xc,$yc);

		$pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);

		for( $a=$rea; $a>$rsa; $a -= $step ) {
		    $pt[] = $xc + $w*cos($a);
		    $pt[] = $yc - $h*sin($a);
		}

		$pt[] = $xc+$w*$cossa;
		$pt[] = $yc-$h*$sinsa;
		$pt[] = $xc;
		$pt[] = $yc;

	    }
	}
	else { // sa > 0 && ea < 90

	    $p = array($xc,$yc,$xc,$yc+$z,
		       $xc+$w*$cossa,$z+$yc-$h*$sinsa,
		       $xc+$w*$cossa,$yc-$h*$sinsa,
		       $xc,$yc);

	    $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);

	    for( $a=$rsa; $a < $rea; $a += $step ) {
		$pt[] = $xc + $w*cos($a);
		$pt[] = $yc - $h*sin($a);
	    }

	    $pt[] = $xc+$w*$cosea;
	    $pt[] = $yc-$h*$sinea;
	    $pt[] = $xc;
	    $pt[] = $yc;
	}
	    
	$img->PushColor($fillcolor.":".$shadow);
	$img->FilledPolygon($p);
	$img->PopColor();

	$img->PushColor($fillcolor);
	$img->FilledPolygon($pt);
	$img->PopColor();
    }

    function SetStartAngle($aStart) {
	if( $aStart < 0 || $aStart > 360 ) {
	    JpGraphError::Raise('Slice start angle must be between 0 and 360 degrees.');
	}
	$this->startangle = $aStart;
    }
    
// Draw a 3D Pie
    function Pie3D($aaoption,$img,$data,$colors,$xc,$yc,$d,$angle,$z,
		   $shadow=0.65,$startangle=0,$edgecolor="",$edgeweight=1) {

	//---------------------------------------------------------------------------
	// As usual the algorithm get more complicated than I originally
	// envisioned. I believe that this is as simple as it is possible
	// to do it with the features I want. It's a good exercise to start
	// thinking on how to do this to convince your self that all this
	// is really needed for the general case.
	//
	// The algorithm two draw 3D pies without "real 3D" is done in
	// two steps.
	// First imagine the pie cut in half through a thought line between
	// 12'a clock and 6'a clock. It now easy to imagine that we can plot 
	// the individual slices for each half by starting with the topmost
	// pie slice and continue down to 6'a clock.
	// 
	// In the algortithm this is done in three principal steps
	// Step 1. Do the knife cut to ensure by splitting slices that extends 
	// over the cut line. This is done by splitting the original slices into
	// upto 3 subslices.
	// Step 2. Find the top slice for each half
	// Step 3. Draw the slices from top to bottom
	//
	// The thing that slightly complicates this scheme with all the
	// angle comparisons below is that we can have an arbitrary start
	// angle so we must take into account the different equivalence classes.
	// For the same reason we must walk through the angle array in a 
	// modulo fashion.
	//
	// Limitations of algorithm: 
	// * A small exploded slice which crosses the 270 degree point
	//   will get slightly nagged close to the center due to the fact that
	//   we print the slices in Z-order and that the slice left part
	//   get printed first and might get slightly nagged by a larger
	//   slice on the right side just before the right part of the small
	//   slice. Not a major problem though. 
	//---------------------------------------------------------------------------

    
	// Determine the height of the ellippse which gives an
	// indication of the inclination angle
	$h = ($angle/90.0)*$d;
	$sum = 0;
	for($i=0; $i<count($data); ++$i ) {
	    $sum += $data[$i];
	}
	
	// Special optimization
	if( $sum==0 ) return;

	if( $this->labeltype == 2 ) {
	    $this->adjusted_data = $this->AdjPercentage($data);
	}

	// Setup the start
	$accsum = 0;
	$a = $startangle;
	$a = $this->NormAngle($a);

	// 
	// Step 1 . Split all slices that crosses 90 or 270
	//
	$idx=0;
	$adjexplode=array(); 
	$numcolors = count($colors);
	for($i=0; $i<count($data); ++$i, ++$idx ) {
	    $da = $data[$i]/$sum * 360;

	    if( empty($this->explode_radius[$i]) )
		$this->explode_radius[$i]=0;

	    $expscale=1;
	    if( $aaoption == 1 ) 
		$expscale=2;

	    $la = $a + $da/2;
	    $explode = array( $xc + $this->explode_radius[$i]*cos($la*M_PI/180)*$expscale,
		              $yc - $this->explode_radius[$i]*sin($la*M_PI/180) * ($h/$d) *$expscale );
	    $adjexplode[$idx] = $explode;
	    $labeldata[$i] = array($la,$explode[0],$explode[1]);
	    $originalangles[$i] = array($a,$a+$da);

	    $ne = $this->NormAngle($a+$da);
	    if( $da <= 180 ) {
		// If the slice size is <= 90 it can at maximum cut across
		// one boundary (either 90 or 270) where it needs to be split
		$split=-1; // no split
		if( ($da<=90 && ($a <= 90 && $ne > 90)) ||
		    (($da <= 180 && $da >90)  && (($a < 90 || $a >= 270) && $ne > 90)) ) {
		    $split = 90;
		}
		elseif( ($da<=90 && ($a <= 270 && $ne > 270)) ||
		        (($da<=180 && $da>90) && ($a >= 90 && $a < 270 && ($a+$da) > 270 )) ) {
		    $split = 270;
		} 
		if( $split > 0 ) { // split in two
		    $angles[$idx] = array($a,$split);
		    $adjcolors[$idx] = $colors[$i % $numcolors];
		    $adjexplode[$idx] = $explode;
		    $angles[++$idx] = array($split,$ne);
		    $adjcolors[$idx] = $colors[$i % $numcolors];
		    $adjexplode[$idx] = $explode;
		}
		else { // no split
		    $angles[$idx] = array($a,$ne);
		    $adjcolors[$idx] = $colors[$i  % $numcolors];
		    $adjexplode[$idx] = $explode;	
		}
	    }
	    else { 
		// da>180
		// Slice may, depending on position, cross one or two
		// bonudaries

		if( $a < 90 ) 
		    $split = 90;
		elseif( $a <= 270 )
		    $split = 270;
		else 
		    $split = 90;

		$angles[$idx] = array($a,$split);
		$adjcolors[$idx] = $colors[$i % $numcolors];
		$adjexplode[$idx] = $explode;
		//if( $a+$da > 360-$split ) { 
		// For slices larger than 270 degrees we might cross
		// another boundary as well. This means that we must
		// split the slice further. The comparison gets a little
		// bit complicated since we must take into accound that
		// a pie might have a startangle >0 and hence a slice might
		// wrap around the 0 angle.
		// Three cases:
		//  a) Slice starts before 90 and hence gets a split=90, but 
		//     we must also check if we need to split at 270
		//  b) Slice starts after 90 but before 270 and slices
		//     crosses 90 (after a wrap around of 0)
		//  c) If start is > 270 (hence the firstr split is at 90)
		//     and the slice is so large that it goes all the way
		//     around 270.
		if( ($a < 90 && ($a+$da > 270)) ||
		    ($a > 90 && $a<=270 && ($a+$da>360+90) ) ||
		    ($a > 270 && $this->NormAngle($a+$da)>270) ) { 
		    $angles[++$idx] = array($split,360-$split);
		    $adjcolors[$idx] = $colors[$i % $numcolors];
		    $adjexplode[$idx] = $explode;
		    $angles[++$idx] = array(360-$split,$ne);
		    $adjcolors[$idx] = $colors[$i % $numcolors];
		    $adjexplode[$idx] = $explode;
		}	
		else {
		    // Just a simple split to the previous decided
		    // angle.
		    $angles[++$idx] = array($split,$ne);
		    $adjcolors[$idx] = $colors[$i % $numcolors];
		    $adjexplode[$idx] = $explode;
		}
	    }
	    $a += $da;
	    $a = $this->NormAngle($a);
	}

	// Total number of slices 
	$n = count($angles);

	for($i=0; $i<$n; ++$i) {
	    list($dbgs,$dbge) = $angles[$i];
	}

	// 
	// Step 2. Find start index (first pie that starts in upper left quadrant)
	//
	$minval = $angles[0][0];
	$min = 0;
	for( $i=0; $i<$n; ++$i ) {
	    if( $angles[$i][0] < $minval ) {
		$minval = $angles[$i][0];
		$min = $i;
	    }
	}
	$j = $min;
	$cnt = 0;
	while( $angles[$j][1] <= 90 ) {
	    $j++;
	    if( $j>=$n) {
		$j=0;
	    }
	    if( $cnt > $n ) {
		JpGraphError::Raise("Pie3D Internal error (#1). Trying to wrap twice when looking for start index");
	    }
	    ++$cnt;
	}
	$start = $j;

	// 
	// Step 3. Print slices in z-order
	//
	$cnt = 0;
	
	// First stroke all the slices between 90 and 270 (left half circle)
	// counterclockwise
	    
	while( $angles[$j][0] < 270  && $aaoption !== 2 ) {

	    list($x,$y) = $adjexplode[$j];

	    $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1],
			      $z,$adjcolors[$j],$shadow);
	
	    $last = array($x,$y,$j);

	    $j++;
	    if( $j >= $n ) $j=0;
	    if( $cnt > $n ) {
		JpGraphError::Raise("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
	    }
	    ++$cnt;
	}
     
	$slice_left = $n-$cnt;
	$j=$start-1;
	if($j<0) $j=$n-1;
	$cnt = 0;
	
	// The stroke all slices from 90 to -90 (right half circle)
	// clockwise
	while( $cnt < $slice_left  && $aaoption !== 2 ) {

	    list($x,$y) = $adjexplode[$j];

	    $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1],
			      $z,$adjcolors[$j],$shadow);
	    $j--;
	    if( $cnt > $n ) {
		JpGraphError::Raise("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
	    }
	    if($j<0) $j=$n-1;
	    $cnt++;
	}
	
	// Now do a special thing. Stroke the last slice on the left
	// halfcircle one more time.  This is needed in the case where 
	// the slice close to 270 have been exploded. In that case the
	// part of the slice close to the center of the pie might be 
	// slightly nagged.
	if( $aaoption !== 2 )
	    $this->Pie3DSlice($img,$last[0],$last[1],$d,$h,$angles[$last[2]][0],
			      $angles[$last[2]][1],$z,$adjcolors[$last[2]],$shadow);


	if( $aaoption !== 1 ) {
	    // Now print possible labels and add csim
	    $img->SetFont($this->value->ff,$this->value->fs);
	    $margin = $img->GetFontHeight()/2 + $this->value->margin ;
	    for($i=0; $i < count($data); ++$i ) {
		$la = $labeldata[$i][0];
		$x = $labeldata[$i][1] + cos($la*M_PI/180)*($d+$margin);
		$y = $labeldata[$i][2] - sin($la*M_PI/180)*($h+$margin);
		if( $la > 180 && $la < 360 ) $y += $z;
		if( $this->labeltype == 0 ) {
		    if( $sum > 0 )
			$l = 100*$data[$i]/$sum;
		    else
			$l = 0;
		}
		elseif( $this->labeltype == 1 ) {
		    $l = $data[$i];
		}
		else {
		    $l = $this->adjusted_data[$i];
		}
		if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
		    $l=sprintf($this->labels[$i],$l);

		$this->StrokeLabels($l,$img,$labeldata[$i][0]*M_PI/180,$x,$y,$z);
	    
		$this->AddSliceToCSIM($i,$labeldata[$i][1],$labeldata[$i][2],$h*2,$d*2,$z,
				      $originalangles[$i][0],$originalangles[$i][1]);
	    }	
	}

	// 
	// Finally add potential lines in pie
	//

	if( $edgecolor=="" || $aaoption !== 0 ) return;

	$accsum = 0;
	$a = $startangle;
	$a = $this->NormAngle($a);

	$a *= M_PI/180.0;

	$idx=0;
	$img->PushColor($edgecolor);
	$img->SetLineWeight($edgeweight);
	
	$fulledge = true;
	for($i=0; $i < count($data) && $fulledge; ++$i ) {
	    if( empty($this->explode_radius[$i]) )
		$this->explode_radius[$i]=0;
	    if( $this->explode_radius[$i] > 0 ) {
		$fulledge = false;
	    }
	}
	    

	for($i=0; $i < count($data); ++$i, ++$idx ) {

	    $da = $data[$i]/$sum * 2*M_PI;
	    $this->StrokeFullSliceFrame($img,$xc,$yc,$a,$a+$da,$d,$h,$z,$edgecolor,
					$this->explode_radius[$i],$fulledge);
	    $a += $da;
	}
	$img->PopColor();
    }

    function StrokeFullSliceFrame($img,$xc,$yc,$sa,$ea,$w,$h,$z,$edgecolor,$exploderadius,$fulledge) {
	$step = 0.02;

	if( $exploderadius > 0 ) {
	    $la = ($sa+$ea)/2;
	    $xc += $exploderadius*cos($la);
	    $yc -= $exploderadius*sin($la) * ($h/$w) ;
	    
	}

	$p = array($xc,$yc,$xc+$w*cos($sa),$yc-$h*sin($sa));

	for($a=$sa; $a < $ea; $a += $step ) {
	    $p[] = $xc + $w*cos($a);
	    $p[] = $yc - $h*sin($a);
	}

	$p[] = $xc+$w*cos($ea);
	$p[] = $yc-$h*sin($ea);
	$p[] = $xc;
	$p[] = $yc;

	$img->SetColor($edgecolor);
	$img->Polygon($p);

	// Unfortunately we can't really draw the full edge around the whole of
	// of the slice if any of the slices are exploded. The reason is that
	// this algorithm is to simply. There are cases where the edges will
	// "overwrite" other slices when they have been exploded.
	// Doing the full, proper 3D hidden lines stiff is actually quite
	// tricky. So for exploded pies we only draw the top edge. Not perfect
	// but the "real" solution is much more complicated.
	if( $fulledge && !( $sa > 0 && $sa < M_PI && $ea < M_PI) ) { 

	    if($sa < M_PI && $ea > M_PI) 
		$sa = M_PI;
 
	    if($sa < 2*M_PI && (($ea >= 2*M_PI) || ($ea > 0 && $ea < $sa ) ) )
		$ea = 2*M_PI;

	    if( $sa >= M_PI && $ea <= 2*M_PI ) {
		$p = array($xc + $w*cos($sa),$yc - $h*sin($sa),
			   $xc + $w*cos($sa),$z + $yc - $h*sin($sa));
		
		for($a=$sa+$step; $a < $ea; $a += $step ) {
		    $p[] = $xc + $w*cos($a);
		    $p[] = $z + $yc - $h*sin($a);
		}
		$p[] = $xc + $w*cos($ea);
		$p[] = $z + $yc - $h*sin($ea);
		$p[] = $xc + $w*cos($ea);
		$p[] = $yc - $h*sin($ea);
		$img->SetColor($edgecolor);
		$img->Polygon($p);	    
	    }
	}
    }

    function Stroke($img,$aaoption=0) {
	$n = count($this->data);

	// If user hasn't set the colors use the theme array
   	if( $this->setslicecolors==null ) {
	    $colors = array_keys($img->rgb->rgb_table);
	    sort($colors);	
	    $idx_a=$this->themearr[$this->theme];	
	    $ca = array();
	    $m = count($idx_a);
	    for($i=0; $i < $m; ++$i)
		$ca[$i] = $colors[$idx_a[$i]];
	    $ca = array_reverse(array_slice($ca,0,$n));
	}
   	else {
	    $ca = $this->setslicecolors;
	}
	

	if( $this->posx <= 1 && $this->posx > 0 )
	    $xc = round($this->posx*$img->width);
	else
	    $xc = $this->posx ;
	
	if( $this->posy <= 1 && $this->posy > 0 )
	    $yc = round($this->posy*$img->height);
	else
	    $yc = $this->posy ;
   			
	if( $this->radius <= 1 ) {
	    $width = floor($this->radius*min($img->width,$img->height));
	    // Make sure that the pie doesn't overflow the image border
	    // The 0.9 factor is simply an extra margin to leave some space
	    // between the pie an the border of the image.
	    $width = min($width,min($xc*0.9,($yc*90/$this->angle-$width/4)*0.9));
	}
	else {
	    $width = $this->radius * ($aaoption === 1 ? 2 : 1 ) ;
	}

	// Add a sanity check for width
	if( $width < 1 ) { 
	    JpGraphError::Raise("Width for 3D Pie is 0. Specify a size > 0");
	    exit();
	}

	// Establish a thickness. By default the thickness is a fifth of the
	// pie slice width (=pie radius) but since the perspective depends
	// on the inclination angle we use some heuristics to make the edge
	// slightly thicker the less the angle.
	
	// Has user specified an absolute thickness? In that case use
	// that instead

	if( $this->iThickness ) {
	  $thick = $this->iThickness;
	  $thick *= ($aaoption === 1 ? 2 : 1 );
	}
	else
	  $thick = $width/12;
	$a = $this->angle;
	if( $a <= 30 ) $thick *= 1.6;
	elseif( $a <= 40 ) $thick *= 1.4;
	elseif( $a <= 50 ) $thick *= 1.2;
	elseif( $a <= 60 ) $thick *= 1.0;
	elseif( $a <= 70 ) $thick *= 0.8;
	elseif( $a <= 80 ) $thick *= 0.7;
	else $thick *= 0.6;

	$thick = floor($thick);

	if( $this->explode_all )
	    for($i=0; $i < $n; ++$i)
		$this->explode_radius[$i]=$this->explode_r;

	$this->Pie3D($aaoption,$img,$this->data, $ca, $xc, $yc, $width, $this->angle, 
	             $thick, 0.65, $this->startangle, $this->edgecolor, $this->edgeweight);

	// Adjust title position
	if( $aaoption != 1 ) {
	    $this->title->Pos($xc,$yc-$this->title->GetFontHeight($img)-$width/2-$this->title->margin,			      "center","bottom");
	    $this->title->Stroke($img);
	}
    }

//---------------
// PRIVATE METHODS	

    // Position the labels of each slice
    function StrokeLabels($label,$img,$a,$xp,$yp,$z) {
	$this->value->halign="left";
	$this->value->valign="top";

	// 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);
	while( $a > 2*M_PI ) $a -= 2*M_PI;
	if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
	if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI; 
	if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
	if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
		
	if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
	if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
	if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
	if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
	if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
	
	$x = round($xp-$dx*$w);
	$y = round($yp-$dy*$h);

	
        // Mark anchor point for debugging 
	/*
	$img->SetColor('red');
	$img->Line($xp-10,$yp,$xp+10,$yp);
	$img->Line($xp,$yp-10,$xp,$yp+10);
	*/
	$oldmargin = $this->value->margin;
	$this->value->margin=0;
	$this->value->Stroke($img,$label,$x,$y);
	$this->value->margin=$oldmargin;

    }	
} // Class

/* EOF */
?>
jpgraph-1.16/src/jpg-config.inc0100644000076400001440000001737510065253722015131 0ustar  ljpusers<?php
//=======================================================================
// File:	JPG-CONFIG.INC
// Description:	Configuration file for JpGraph library
// Created: 	2004-03-27
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: jpg-config.inc,v 1.1.2.1 2004/03/27 12:35:55 aditus Exp $
//
// License:	This code is released under QPL 1.0
// Copyright (C) 2004 Johan Persson
//========================================================================



//------------------------------------------------------------------------
// Directories for cache and font directory. 
//
// CACHE_DIR:
// The full absolute name of the directory to be used to store the
// cached image files. This directory will not be used if the USE_CACHE
// define (further down) is false. If you enable the cache please note that
// this directory MUST be readable and writable for the process running PHP.
// Must end with '/'
//
// TTF_DIR:
// Directory where TTF fonts can be found. Must end with '/'
//
// The default values used if these defines are left commented out are:
//
// UNIX:
//   CACHE_DIR /tmp/jpgraph_cache/
//   TTF_DIR   /usr/X11R6/lib/X11/fonts/truetype/
//
// WINDOWS:
//   CACHE_DIR $SERVER_TEMP/jpgraph_cache/
//   TTF_DIR   $SERVER_SYSTEMROOT/fonts/
//
//------------------------------------------------------------------------
// DEFINE("CACHE_DIR","/tmp/jpgraph_cache/");
// DEFINE("TTF_DIR","/usr/X11R6/lib/X11/fonts/truetype/");


//-------------------------------------------------------------------------
// Cache directory specification for use with CSIM graphs that are
// using the cache.
// The directory must be the filesysystem name as seen by PHP
// and the 'http' version must be the same directory but as 
// seen by the HTTP server relative to the 'htdocs' ddirectory. 
// If a relative path is specified it is taken to be relative from where
// the image script is executed.
// Note: The default setting is to create a subdirectory in the 
// directory from where the image script is executed and store all files
// there. As ususal this directory must be writeable by the PHP process.
DEFINE("CSIMCACHE_DIR","csimcache/"); 
DEFINE("CSIMCACHE_HTTP_DIR","csimcache/");

//------------------------------------------------------------------------
// Defines for font setup
//------------------------------------------------------------------------

// Actual name of the TTF file used together with FF_CHINESE aka FF_BIG5
// This is the TTF file being used when the font family is specified as
// either FF_CHINESE or FF_BIG5
DEFINE('CHINESE_TTF_FONT','bkai00mp.ttf');

// Special unicode cyrillic language support
DEFINE("LANGUAGE_CYRILLIC",false);

// If you are setting this config to true the conversion
// will assume that the input text is windows 1251, if
// false it will assume koi8-r
DEFINE("CYRILLIC_FROM_WINDOWS",false);

//------------------------------------------------------------------------
// Various JpGraph Settings. Adjust accordingly to your
// preferences. Note that cache functionality is turned off by
// default (Enable by setting USE_CACHE to true)
//------------------------------------------------------------------------

// Deafult graphic format set to "auto" which will automatically
// choose the best available format in the order png,gif,jpeg
// (The supported format depends on what your PHP installation supports)
DEFINE("DEFAULT_GFORMAT","auto");

// Should the image be a truecolor image? 
// Note 1: Has only effect with GD 2.0.1 and above.
// Note 2: GD 2.0.1 + PHP 4.0.6 on Win32 crashes when trying to use 
// trucolor.
// Note 3: MUST be enabled to get background images working with GD2
DEFINE('USE_TRUECOLOR',true);

// Specify what version of the GD library is installed.
// If this is set to 'auto' the version will be automatically 
// determined.
// However since determining the library takes ~1ms you can also 
// manually specify the version if you know what version you have. 
// This means that you should 
// set this define to true if you have GD 2.x installed to save 1ms. 
DEFINE("USE_LIBRARY_GD2",'auto');

// Should the cache be used at all? By setting this to false no
// files will be generated in the cache directory.  
// The difference from READ_CACHE being that setting READ_CACHE to
// false will still create the image in the cache directory
// just not use it. By setting USE_CACHE=false no files will even
// be generated in the cache directory.
DEFINE("USE_CACHE",false);

// Should we try to find an image in the cache before generating it? 
// Set this define to false to bypass the reading of the cache and always
// regenerate the image. Note that even if reading the cache is 
// disabled the cached will still be updated with the newly generated
// image. Set also "USE_CACHE" below.
DEFINE("READ_CACHE",true);

// Determine if the error handler should be image based or purely
// text based. Image based makes it easier since the script will
// always return an image even in case of errors.
DEFINE("USE_IMAGE_ERROR_HANDLER",true);

// Determine if the library should also setup the default PHP
// error handler to generate a graphic error mesage. This is useful
// during development to be able to see the error message as an image
// instead as a "red-cross" in a page where an image is expected.
DEFINE("INSTALL_PHP_ERR_HANDLER",false);

// If the color palette is full should JpGraph try to allocate
// the closest match? If you plan on using background images or
// gradient fills it might be a good idea to enable this.
// If not you will otherwise get an error saying that the color palette is 
// exhausted. The drawback of using approximations is that the colors 
// might not be exactly what you specified. 
// Note1: This does only apply to paletted images, not truecolor 
// images since they don't have the limitations of maximum number
// of colors.
DEFINE("USE_APPROX_COLORS",true);

// Should usage of deprecated functions and parameters give a fatal error?
// (Useful to check if code is future proof.)
DEFINE("ERR_DEPRECATED",true);

// Should the time taken to generate each picture be branded to the lower
// left in corner in each generated image? Useful for performace measurements
// generating graphs
DEFINE("BRAND_TIMING",false);

// What format should be used for the timing string?
DEFINE("BRAND_TIME_FORMAT","(%01.3fs)");

//------------------------------------------------------------------------
// The following constants should rarely have to be changed !
//------------------------------------------------------------------------

// What group should the cached file belong to
// (Set to "" will give the default group for the "PHP-user")
// Please note that the Apache user must be a member of the
// specified group since otherwise it is impossible for Apache
// to set the specified group.
DEFINE("CACHE_FILE_GROUP","wwwadmin");

// What permissions should the cached file have
// (Set to "" will give the default persmissions for the "PHP-user")
DEFINE("CACHE_FILE_MOD",0664);

// Decide if we should use the bresenham circle algorithm or the
// built in Arc(). Bresenham gives better visual apperance of circles 
// but is more CPU intensive and slower then the built in Arc() function
// in GD. Turned off by default for speed
DEFINE("USE_BRESENHAM",false);

// Special file name to indicate that we only want to calc
// the image map in the call to Graph::Stroke() used
// internally from the GetHTMLCSIM() method.
DEFINE("_CSIM_SPECIALFILE","_csim_special_");

// HTTP GET argument that is used with image map
// to indicate to the script to just generate the image
// and not the full CSIM HTML page.
DEFINE("_CSIM_DISPLAY","_jpg_csimd");

// Special filename for Graph::Stroke(). If this filename is given
// then the image will NOT be streamed to browser of file. Instead the
// Stroke call will return the handler for the created GD image.
DEFINE("_IMG_HANDLER","__handle");


?>
jpgraph-1.16/src/jpgraph_gradient.php0100644000076400001440000002624010065253722016423 0ustar  ljpusers<?php
/*=======================================================================
// File:	JPGRAPH_GRADIENT.PHP
// Description:	Create a color gradient
// Created: 	2003-02-01
// Author:	Johan Persson (johanp@aditus.nu)
// Ver:		$Id: jpgraph_gradient.php,v 1.1.2.11 2004/06/12 14:29:18 aditus Exp $
//
// License:	This code is released under QPL
// Copyright (C) 2003 Johan Persson
//========================================================================
*/

// Styles for gradient color fill
DEFINE("GRAD_VER",1);
DEFINE("GRAD_VERT",1);
DEFINE("GRAD_HOR",2);
DEFINE("GRAD_MIDHOR",3);
DEFINE("GRAD_MIDVER",4);
DEFINE("GRAD_CENTER",5);
DEFINE("GRAD_WIDE_MIDVER",6);
DEFINE("GRAD_WIDE_MIDHOR",7);
DEFINE("GRAD_LEFT_REFLECTION",8);
DEFINE("GRAD_RIGHT_REFLECTION",9);
DEFINE("GRAD_RAISED_PANEL",10);

  
//===================================================
// CLASS Gradient
// Description: Handles gradient fills. This is to be
// considered a "friend" class of Class Image.
//===================================================
class Gradient {
    var $img=null;
    var $numcolors=100;
//---------------
// CONSTRUCTOR
    function Gradient(&$img) {
	$this->img = $img;
    }


    function SetNumColors($aNum) {
	$this->numcolors=$aNum;
    }
//---------------
// PUBLIC METHODS	
    // Produce a gradient filled rectangle with a smooth transition between
    // two colors.
    // ($xl,$yt) 	Top left corner
    // ($xr,$yb)	Bottom right
    // $from_color	Starting color in gradient
    // $to_color	End color in the gradient
    // $style		Which way is the gradient oriented?
    function FilledRectangle($xl,$yt,$xr,$yb,$from_color,$to_color,$style=1) {
	switch( $style ) {	
	    case GRAD_VER:  
		$steps = round(abs($xr-$xl));
		$delta = $xr>=$xl ? 1 : -1;
		$this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
		for( $i=0, $x=$xl; $i < $steps; ++$i ) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($x,$yt,$x,$yb);
		    $x += $delta;
		}
		break;

	    case GRAD_HOR: 
		$steps = round(abs($yb-$yt));
		$delta = $yb>=$yt ? 1 : -1;
		$this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
		for($i=0,$y=$yt; $i < $steps; ++$i) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($xl,$y,$xr,$y);
		    $y += $delta;
		}
		break;

	    case GRAD_MIDHOR: 
		$steps = round(abs($yb-$yt)/2);
		$delta = $yb >= $yt ? 1 : -1;
		$this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
		for($y=$yt, $i=0; $i < $steps;  ++$i) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($xl,$y,$xr,$y);
		    $y += $delta;
		}
		--$i;
		if( abs($yb-$yt) % 2 == 1 ) --$steps;
		for($j=0; $j < $steps; ++$j, --$i) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($xl,$y,$xr,$y);
		    $y += $delta;
		}
		$this->img->Line($xl,$y,$xr,$y);
		break;

	    case GRAD_MIDVER: 
		$steps = round(abs($xr-$xl)/2);
		$delta = $xr>=$xl ? 1 : -1;
		$this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
		for($x=$xl, $i=0; $i < $steps; ++$i) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($x,$yb,$x,$yt);
		    $x += $delta;
		}
		--$i;
		if( abs($xr-$xl) % 2 == 1 ) --$steps;
		for($j=0; $j < $steps; ++$j, --$i) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($x,$yb,$x,$yt);
		    $x += $delta;
		}
		$this->img->Line($x,$yb,$x,$yt);		
		break;

	    case GRAD_WIDE_MIDVER: 
		$diff = round(abs($xr-$xl));
		$steps = floor(abs($diff)/3);
		$firststep = $diff - 2*$steps ; 
		$delta = $xr >= $xl ? 1 : -1;
		$this->GetColArray($from_color,$to_color,$firststep,$colors,$this->numcolors);
		for($x=$xl, $i=0; $i < $firststep; ++$i) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($x,$yb,$x,$yt);
		    $x += $delta;
		}
		--$i;
		$this->img->current_color = $colors[$i];
		for($j=0; $j< $steps; ++$j) {
		    $this->img->Line($x,$yb,$x,$yt);
		    $x += $delta;
		}
		
		for($j=0; $j < $steps; ++$j, --$i) {
		    $this->img->current_color = $colors[$i];				
		    $this->img->Line($x,$yb,$x,$yt);	
		    $x += $delta;
		}				
		break;

	    case GRAD_WIDE_MIDHOR:
		$diff = round(abs($yb-$yt));
		$steps = floor(abs($diff)/3);
		$firststep = $diff - 2*$steps ; 
		$delta = $yb >= $yt? 1 : -1;
		$this->GetColArray($from_color,$to_color,$firststep,$colors,$this->numcolors);
		for($y=$yt, $i=0; $i < $firststep;  ++$i) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($xl,$y,$xr,$y);
		    $y += $delta;
		}
		--$i;
		$this->img->current_color = $colors[$i];
		for($j=0; $j < $steps; ++$j) {
		    $this->img->Line($xl,$y,$xr,$y);
		    $y += $delta;
		}
		for($j=0; $j < $steps; ++$j, --$i) {
		    $this->img->current_color = $colors[$i];				
		    $this->img->Line($xl,$y,$xr,$y);
		    $y += $delta;
		}				
		break;	    

	    case GRAD_LEFT_REFLECTION: 
		$steps1 = round(0.3*abs($xr-$xl));
		$delta = $xr>=$xl ? 1 : -1;		

		$from_color = $this->img->rgb->Color($from_color);
		$adj = 1.4;
		$m = ($adj-1.0)*(255-min(255,min($from_color[0],min($from_color[1],$from_color[2]))));
		$from_color2 = array(min(255,$from_color[0]+$m), 
				    min(255,$from_color[1]+$m), min(255,$from_color[2]+$m));		

		$this->GetColArray($from_color2,$to_color,$steps1,$colors,$this->numcolors);
		$n = count($colors);
		for($x=$xl, $i=0; $i < $steps1 && $i < $n; ++$i) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($x,$yb,$x,$yt);
		    $x += $delta;
		}
		$steps2 = max(1,round(0.08*abs($xr-$xl)));
		$this->img->SetColor($to_color);
		for($j=0; $j< $steps2; ++$j) {
		    $this->img->Line($x,$yb,$x,$yt);
		    $x += $delta;
		}
		$steps = abs($xr-$xl)-$steps1-$steps2;
		$this->GetColArray($to_color,$from_color,$steps,$colors,$this->numcolors);   
		$n = count($colors);
		for($i=0; $i < $steps && $i < $n; ++$i) {
		    $this->img->current_color = $colors[$i];
		    $this->img->Line($x,$yb,$x,$yt);
		    $x += $