PageRenderTime 48ms CodeModel.GetById 14ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/code/ryzom/tools/server/admin/jpgraph/jpgraph_pie.php

https://bitbucket.org/mattraykowski/ryzomcore_demoshard
PHP | 1341 lines | 941 code | 196 blank | 204 comment | 245 complexity | d50e3e4870c07a44acb73a922d948998 MD5 | raw file
   1<?php
   2/*=======================================================================
   3// File:	JPGRAPH_PIE.PHP
   4// Description:	Pie plot extension for JpGraph
   5// Created: 	2001-02-14
   6// Author:	Johan Persson (johanp@aditus.nu)
   7// Ver:		$Id: jpgraph_pie.php,v 1.1 2006/07/07 13:37:14 powles Exp $
   8//
   9// Copyright (c) Aditus Consulting. All rights reserved.
  10//========================================================================
  11*/
  12
  13
  14// Defines for PiePlot::SetLabelType()
  15DEFINE("PIE_VALUE_ABS",1);
  16DEFINE("PIE_VALUE_PER",0);
  17DEFINE("PIE_VALUE_PERCENTAGE",0);
  18DEFINE("PIE_VALUE_ADJPERCENTAGE",2);
  19DEFINE("PIE_VALUE_ADJPER",2);
  20
  21//===================================================
  22// CLASS PiePlot
  23// Description: Draws a pie plot
  24//===================================================
  25class PiePlot {
  26    var $posx=0.5,$posy=0.5;
  27    var $radius=0.3;
  28    var $explode_radius=array(),$explode_all=false,$explode_r=20;
  29    var $labels=null, $legends=null;
  30    var $csimtargets=null;  // Array of targets for CSIM
  31    var $csimareas='';		// Generated CSIM text	
  32    var $csimalts=null;		// ALT tags for corresponding target
  33    var $data=null;
  34    var $title;
  35    var $startangle=0;
  36    var $weight=1, $color="black";
  37    var $legend_margin=6,$show_labels=true;
  38    var $themearr = array(
  39	"earth" 	=> array(136,34,40,45,46,62,63,134,74,10,120,136,141,168,180,77,209,218,346,395,89,430),
  40	"pastel" => array(27,415,128,59,66,79,105,110,42,147,152,230,236,240,331,337,405,38),
  41	"water"  => array(8,370,24,40,335,56,213,237,268,14,326,387,10,388),
  42	"sand"   => array(27,168,34,170,19,50,65,72,131,209,46,393));
  43    var $theme="earth";
  44    var $setslicecolors=array();
  45    var $labeltype=0; // Default to percentage
  46    var $pie_border=true,$pie_interior_border=true;
  47    var $value;
  48    var $ishadowcolor='',$ishadowdrop=4;
  49    var $ilabelposadj=1;
  50    var $legendcsimtargets = array();
  51    var $legendcsimalts = array();
  52    var $adjusted_data = array();
  53    var $guideline = null,$guidelinemargin=10;
  54    var $iShowGuideLineForSingle = false;
  55    var $iGuideLineCurve = false,$iGuideVFactor=1.4,$iGuideLineRFactor=0.8;
  56//---------------
  57// CONSTRUCTOR
  58    function PiePlot($data) {
  59	$this->data = array_reverse($data);
  60	$this->title = new Text("");
  61	$this->title->SetFont(FF_FONT1,FS_BOLD);
  62	$this->value = new DisplayValue();
  63	$this->value->Show();
  64	$this->value->SetFormat('%.1f%%');
  65	$this->guideline = new LineProperty();
  66    }
  67
  68//---------------
  69// PUBLIC METHODS	
  70    function SetCenter($x,$y=0.5) {
  71	$this->posx = $x;
  72	$this->posy = $y;
  73    }
  74
  75    // Enable guideline and set drwaing policy
  76    function SetGuideLines($aFlg=true,$aCurved=true,$aAlways=false) {
  77	$this->guideline->Show($aFlg);
  78	$this->iShowGuideLineForSingle = $aAlways;
  79	$this->iGuideLineCurve = $aCurved;
  80    }
  81
  82    // Adjuste the distance between labels and labels and pie
  83    function SetGuideLinesAdjust($aVFactor,$aRFactor=0.8) {
  84	$this->iGuideVFactor=$aVFactor;
  85	$this->iGuideLineRFactor=$aRFactor;
  86    }
  87
  88    function SetColor($aColor) {
  89	$this->color = $aColor;
  90    }
  91	
  92    function SetSliceColors($aColors) {
  93	$this->setslicecolors = $aColors;
  94    }
  95	
  96    function SetShadow($aColor='darkgray',$aDropWidth=4) {
  97	$this->ishadowcolor = $aColor;
  98	$this->ishadowdrop = $aDropWidth;
  99    }
 100
 101    function SetCSIMTargets($targets,$alts=null) {
 102	$this->csimtargets=array_reverse($targets);
 103	if( is_array($alts) )
 104	    $this->csimalts=array_reverse($alts);
 105    }
 106	
 107    function GetCSIMareas() {
 108	return $this->csimareas;
 109    }
 110
 111    function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {  
 112
 113        //Slice number, ellipse centre (x,y), height, width, start angle, end angle
 114	while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
 115	while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
 116
 117	$sa = 2*M_PI - $sa;
 118	$ea = 2*M_PI - $ea;
 119
 120	// Special case when we have only one slice since then both start and end
 121	// angle will be == 0
 122	if( abs($sa - $ea) < 0.0001 ) {
 123	    $sa=2*M_PI; $ea=0;
 124	}
 125
 126	//add coordinates of the centre to the map
 127	$xc = floor($xc);$yc=floor($yc);
 128	$coords = "$xc, $yc";
 129
 130	//add coordinates of the first point on the arc to the map
 131	$xp = floor(($radius*cos($ea))+$xc);
 132	$yp = floor($yc-$radius*sin($ea));
 133	$coords.= ", $xp, $yp";
 134	//add coordinates every 0.2 radians
 135	$a=$ea+0.2;
 136
 137	// If we cross the 260-limit with a slice we need to handle
 138	// the fact that end angle is smaller than start
 139	if( $sa < $ea ) {
 140	    while ($a <= 2*M_PI) {
 141		$xp = floor($radius*cos($a)+$xc);
 142		$yp = floor($yc-$radius*sin($a));
 143		$coords.= ", $xp, $yp";
 144		$a += 0.2;
 145	    }
 146	    $a -= 2*M_PI;
 147	}
 148
 149	while ($a < $sa) {
 150	    $xp = floor($radius*cos($a)+$xc);
 151	    $yp = floor($yc-$radius*sin($a));
 152	    $coords.= ", $xp, $yp";
 153	    $a += 0.2;
 154	}
 155		
 156	//Add the last point on the arc
 157	$xp = floor($radius*cos($sa)+$xc);
 158	$yp = floor($yc-$radius*sin($sa));
 159	$coords.= ", $xp, $yp";
 160	if( !empty($this->csimtargets[$i]) ) {
 161	    $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtargets[$i]."\"";
 162	    $tmp="";
 163	    if( !empty($this->csimalts[$i]) ) {
 164		$tmp=sprintf($this->csimalts[$i],$this->data[$i]);
 165		$this->csimareas .= " title=\"$tmp\"";
 166	    }
 167	    $this->csimareas .= " alt=\"$tmp\" />\n";
 168	}
 169    }
 170
 171	
 172    function SetTheme($aTheme) {
 173	if( in_array($aTheme,array_keys($this->themearr)) )
 174	    $this->theme = $aTheme;
 175	else
 176	    JpGraphError::RaiseL(15001,$aTheme);//("PiePLot::SetTheme() Unknown theme: $aTheme");
 177    }
 178	
 179    function ExplodeSlice($e,$radius=20) {
 180	if( ! is_integer($e) ) 
 181	    JpGraphError::RaiseL(15002);//('Argument to PiePlot::ExplodeSlice() must be an integer');
 182	$this->explode_radius[$e]=$radius;
 183    }
 184
 185    function ExplodeAll($radius=20) {
 186	$this->explode_all=true;
 187	$this->explode_r = $radius;
 188    }
 189
 190    function Explode($aExplodeArr) {
 191	if( !is_array($aExplodeArr) ) {
 192	    JpGraphError::RaiseL(15003);
 193//("Argument to PiePlot::Explode() must be an array with integer distances.");
 194	}
 195	$this->explode_radius = $aExplodeArr;
 196    }
 197
 198    function SetStartAngle($aStart) {
 199	if( $aStart < 0 || $aStart > 360 ) {
 200	    JpGraphError::RaiseL(15004);//('Slice start angle must be between 0 and 360 degrees.');
 201	}
 202	$this->startangle = 360-$aStart;
 203	$this->startangle *= M_PI/180;
 204    }
 205	
 206    function SetFont($family,$style=FS_NORMAL,$size=10) {
 207	JpGraphError::RaiseL(15005);//('PiePlot::SetFont() is deprecated. Use PiePlot->value->SetFont() instead.');
 208    }
 209	
 210    // Size in percentage
 211    function SetSize($aSize) {
 212	if( ($aSize>0 && $aSize<=0.5) || ($aSize>10 && $aSize<1000) )
 213	    $this->radius = $aSize;
 214	else
 215	    JpGraphError::RaiseL(15006);
 216//("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]");
 217    }
 218	
 219    function SetFontColor($aColor) {
 220	JpGraphError::RaiseL(15007);
 221//('PiePlot::SetFontColor() is deprecated. Use PiePlot->value->SetColor() instead.');
 222    }
 223	
 224    // Set label arrays
 225    function SetLegends($aLegend) {
 226	$this->legends = $aLegend;
 227    }
 228
 229    // Set text labels for slices 
 230    function SetLabels($aLabels,$aLblPosAdj="auto") {
 231	$this->labels = array_reverse($aLabels);
 232	$this->ilabelposadj=$aLblPosAdj;
 233    }
 234
 235    function SetLabelPos($aLblPosAdj) {
 236	$this->ilabelposadj=$aLblPosAdj;
 237    }
 238	
 239    // Should we display actual value or percentage?
 240    function SetLabelType($t) {
 241	if( $t < 0 || $t > 2 ) 
 242	    JpGraphError::RaiseL(15008,$t);
 243//("PiePlot::SetLabelType() Type for pie plots must be 0 or 1 (not $t).");
 244	$this->labeltype=$t;
 245    }
 246
 247    // Deprecated. 
 248    function SetValueType($aType) {
 249	$this->SetLabelType($aType);
 250    }
 251
 252    // Should the circle around a pie plot be displayed
 253    function ShowBorder($exterior=true,$interior=true) {
 254	$this->pie_border = $exterior;
 255	$this->pie_interior_border = $interior;
 256    }
 257	
 258    // Setup the legends (Framework method)
 259    function Legend(&$graph) {
 260	$colors = array_keys($graph->img->rgb->rgb_table);
 261   	sort($colors);	
 262   	$ta=$this->themearr[$this->theme];	
 263   	$n = count($this->data);
 264
 265   	if( $this->setslicecolors==null ) {
 266	    $numcolors=count($ta);
 267	    if( is_a($this,'PiePlot3D') ) {
 268		$ta = array_reverse(array_slice($ta,0,$n));
 269	    }
 270	}
 271   	else {
 272	    $this->setslicecolors = array_slice($this->setslicecolors,0,$n);
 273	    $numcolors=count($this->setslicecolors); 
 274	    if( $graph->pieaa && is_a($this,'PiePlot') ) { 
 275		$this->setslicecolors = array_reverse($this->setslicecolors);
 276	    }
 277	}
 278		
 279	$sum=0;
 280	for($i=0; $i < $n; ++$i)
 281	    $sum += $this->data[$i];
 282
 283	// Bail out with error if the sum is 0
 284	if( $sum==0 )
 285	    JpGraphError::RaiseL(15009);//("Illegal pie plot. Sum of all data is zero for Pie!");
 286
 287	// Make sure we don't plot more values than data points
 288	// (in case the user added more legends than data points)
 289	$n = min(count($this->legends),count($this->data));
 290	if( $this->legends != "" ) {
 291	    $this->legends = array_reverse(array_slice($this->legends,0,$n));
 292	}
 293	for( $i=$n-1; $i >= 0; --$i ) {
 294	    $l = $this->legends[$i];
 295	    // Replace possible format with actual values
 296	    if( count($this->csimalts) > $i ) {
 297		$fmt = $this->csimalts[$i];
 298	    }
 299	    else {
 300		$fmt = "%d"; // Deafult Alt if no other has been specified
 301	    }
 302	    if( $this->labeltype==0 ) {
 303		$l = sprintf($l,100*$this->data[$i]/$sum);
 304		$alt = sprintf($fmt,$this->data[$i]);
 305		
 306	    }
 307	    elseif( $this->labeltype == 1)  {
 308		$l = sprintf($l,$this->data[$i]);
 309		$alt = sprintf($fmt,$this->data[$i]);
 310		
 311	    }
 312	    else {
 313		$l = sprintf($l,$this->adjusted_data[$i]);
 314		$alt = sprintf($fmt,$this->adjusted_data[$i]);
 315	    }
 316
 317	    if( $this->setslicecolors==null ) {
 318		$graph->legend->Add($l,$colors[$ta[$i%$numcolors]],"",0,$this->csimtargets[$i],$alt);
 319	    }
 320	    else {
 321		$graph->legend->Add($l,$this->setslicecolors[$i%$numcolors],"",0,$this->csimtargets[$i],$alt);
 322	    }
 323	}
 324    }
 325	
 326    // Adjust the rounded percetage value so that the sum of
 327    // of the pie slices are always 100%
 328    // Using the Hare/Niemeyer method
 329    function AdjPercentage($aData,$aPrec=0) {
 330	$mul=100;
 331	if( $aPrec > 0 && $aPrec < 3 ) {
 332	    if( $aPrec == 1 ) 
 333		$mul=1000;
 334		else
 335		    $mul=10000;
 336	}
 337	
 338	$tmp = array();
 339	$result = array();
 340	$quote_sum=0;
 341	$n = count($aData) ;
 342	for( $i=0, $sum=0; $i < $n; ++$i )
 343	    $sum+=$aData[$i];
 344	foreach($aData as $index => $value) {
 345	    $tmp_percentage=$value/$sum*$mul;
 346	    $result[$index]=floor($tmp_percentage);
 347	    $tmp[$index]=$tmp_percentage-$result[$index];
 348	    $quote_sum+=$result[$index];
 349	}
 350	if( $quote_sum == $mul) {
 351	    if( $mul > 100 ) {
 352		$tmp = $mul / 100;
 353		for( $i=0; $i < $n; ++$i ) {
 354		    $result[$i] /= $tmp ;
 355		}
 356	    }
 357	    return $result;
 358	}
 359	arsort($tmp,SORT_NUMERIC);
 360	reset($tmp);
 361	for($i=0; $i < $mul-$quote_sum; $i++)
 362	{
 363	    $result[key($tmp)]++;
 364	    next($tmp);
 365	}
 366	if( $mul > 100 ) {
 367	    $tmp = $mul / 100;
 368	    for( $i=0; $i < $n; ++$i ) {
 369		$result[$i] /= $tmp ;
 370	    }
 371	}
 372	return $result;
 373    }
 374
 375
 376    function Stroke(&$img,$aaoption=0) {
 377	// aaoption is used to handle antialias
 378	// aaoption == 0 a normal pie
 379	// aaoption == 1 just the body
 380	// aaoption == 2 just the values
 381
 382	// Explode scaling. If anti anti alias we scale the image
 383	// twice and we also need to scale the exploding distance
 384	$expscale = $aaoption === 1 ? 2 : 1;
 385
 386	if( $this->labeltype == 2 ) {
 387	    // Adjust the data so that it will add up to 100%
 388	    $this->adjusted_data = $this->AdjPercentage($this->data);
 389	}
 390
 391	$colors = array_keys($img->rgb->rgb_table);
 392   	sort($colors);	
 393   	$ta=$this->themearr[$this->theme];	
 394	$n = count($this->data);
 395   	
 396   	if( $this->setslicecolors==null ) {
 397	    $numcolors=count($ta);
 398	}
 399   	else {
 400	    $this->setslicecolors = array_reverse(array_slice($this->setslicecolors,0,$n));
 401	    $numcolors=count($this->setslicecolors); 
 402	    $tt = array_slice($this->setslicecolors,$n % $numcolors);
 403	    $tt2 = array_slice($this->setslicecolors,0,$n % $numcolors);
 404	    $tt2 = array_merge($tt, $tt2);
 405	    $this->setslicecolors = $tt + $tt2;
 406	}
 407
 408	// Draw the slices
 409	$sum=0;
 410	for($i=0; $i < $n; ++$i)
 411	    $sum += $this->data[$i];
 412	
 413	// Bail out with error if the sum is 0
 414	if( $sum==0 )
 415	    JpGraphError::RaiseL(15009);//("Sum of all data is 0 for Pie.");
 416	
 417	// Set up the pie-circle
 418	if( $this->radius <= 1 )
 419	    $radius = floor($this->radius*min($img->width,$img->height));
 420	else {
 421	    $radius = $aaoption === 1 ? $this->radius*2 : $this->radius;
 422	}
 423
 424	if( $this->posx <= 1 && $this->posx > 0 )
 425	    $xc = round($this->posx*$img->width);
 426	else
 427	    $xc = $this->posx ;
 428	
 429	if( $this->posy <= 1 && $this->posy > 0 )
 430	    $yc = round($this->posy*$img->height);
 431	else
 432	    $yc = $this->posy ;
 433		
 434	$n = count($this->data);
 435
 436	if( $this->explode_all )
 437	    for($i=0; $i < $n; ++$i)
 438		$this->explode_radius[$i]=$this->explode_r;
 439
 440	if( $this->ishadowcolor != "" && $aaoption !== 2) {
 441	    $accsum=0;
 442	    $angle2 = $this->startangle;
 443	    $img->SetColor($this->ishadowcolor);
 444	    for($i=0; $sum > 0 && $i < $n; ++$i) {
 445		$j = $n-$i-1;
 446		$d = $this->data[$i];
 447		$angle1 = $angle2;
 448		$accsum += $d;
 449		$angle2 = $this->startangle+2*M_PI*$accsum/$sum;
 450		if( empty($this->explode_radius[$j]) )
 451		    $this->explode_radius[$j]=0;
 452
 453		$la = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
 454
 455		$xcm = $xc + $this->explode_radius[$j]*cos($la)*$expscale;
 456		$ycm = $yc - $this->explode_radius[$j]*sin($la)*$expscale;
 457		
 458		$xcm += $this->ishadowdrop*$expscale;
 459		$ycm += $this->ishadowdrop*$expscale;
 460
 461		$img->CakeSlice($xcm,$ycm,$radius,$radius,
 462				$angle1*180/M_PI,$angle2*180/M_PI,$this->ishadowcolor);
 463		
 464	    }
 465	}
 466
 467	$accsum=0;
 468	$angle2 = $this->startangle;
 469	$img->SetColor($this->color);
 470	for($i=0; $sum>0 && $i < $n; ++$i) {
 471	    $j = $n-$i-1;
 472	    if( empty($this->explode_radius[$j]) )
 473		$this->explode_radius[$j]=0;
 474	    $d = $this->data[$i];
 475	    $angle1 = $angle2;
 476	    $accsum += $d;
 477	    $angle2 = $this->startangle+2*M_PI*$accsum/$sum;
 478	    $this->la[$i] = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
 479
 480	    if( $d == 0 ) continue;
 481
 482	    if( $this->setslicecolors==null )
 483		$slicecolor=$colors[$ta[$i%$numcolors]];
 484	    else
 485		$slicecolor=$this->setslicecolors[$i%$numcolors];
 486
 487	    if( $this->pie_interior_border && $aaoption===0 )
 488		$img->SetColor($this->color);
 489	    else
 490		$img->SetColor($slicecolor);
 491
 492	    $arccolor = $this->pie_border && $aaoption===0 ? $this->color : "";
 493
 494	    $xcm = $xc + $this->explode_radius[$j]*cos($this->la[$i])*$expscale;
 495	    $ycm = $yc - $this->explode_radius[$j]*sin($this->la[$i])*$expscale;
 496
 497	    if( $aaoption !== 2 ) {
 498		$img->CakeSlice($xcm,$ycm,$radius-1,$radius-1,
 499				$angle1*180/M_PI,$angle2*180/M_PI,$slicecolor,$arccolor);
 500	    }
 501
 502	    if( $this->csimtargets && $aaoption !== 1 ) {
 503		$this->AddSliceToCSIM($i,$xcm,$ycm,$radius,$angle1,$angle2);
 504	    }
 505	}
 506
 507	// Format the titles for each slice
 508	if( $aaoption!==2) {
 509	    for( $i=0; $i < $n; ++$i) {
 510		if( $this->labeltype==0 ) {
 511		    if( $sum != 0 )
 512			$l = 100.0*$this->data[$i]/$sum;
 513		    else
 514			$l = 0.0;
 515		}
 516		elseif( $this->labeltype==1 ) {
 517		    $l = $this->data[$i]*1.0;
 518		}
 519		else {
 520		    $l = $this->adjusted_data[$i];
 521		}
 522		if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
 523		    $this->labels[$i]=sprintf($this->labels[$i],$l);
 524		else
 525		    $this->labels[$i]=$l;
 526	    }
 527	}
 528
 529	If( $this->value->show && $aaoption !== 1 ) {
 530	    $this->StrokeAllLabels($img,$xc,$yc,$radius);
 531	}
 532
 533	// Adjust title position
 534	if( $aaoption !== 1 ) {
 535	    $this->title->Pos($xc,
 536			  $yc-$this->title->GetFontHeight($img)-$radius-$this->title->margin,
 537			  "center","bottom");
 538	    $this->title->Stroke($img);
 539	}
 540
 541    }
 542
 543//---------------
 544// PRIVATE METHODS	
 545
 546    function NormAngle($a) {
 547	while( $a < 0 ) $a += 2*M_PI;
 548	while( $a > 2*M_PI ) $a -= 2*M_PI;
 549	return $a;
 550    }
 551
 552    function Quadrant($a) {
 553	$a=$this->NormAngle($a);
 554	if( $a > 0 && $a <= M_PI/2 )
 555	    return 0;
 556	if( $a > M_PI/2 && $a <= M_PI )
 557	    return 1;
 558	if( $a > M_PI && $a <= 1.5*M_PI )
 559	    return 2;
 560	if( $a > 1.5*M_PI )
 561	    return 3;
 562    }
 563
 564    function StrokeGuideLabels($img,$xc,$yc,$radius) {
 565	$n = count($this->labels);
 566
 567	//-----------------------------------------------------------------------
 568	// Step 1 of the algorithm is to construct a number of clusters
 569	// a cluster is defined as all slices within the same quadrant (almost)
 570	// that has an angular distance less than the threshold
 571	//-----------------------------------------------------------------------
 572	$tresh_hold=25 * M_PI/180; // 25 degrees difference to be in a cluster
 573	$incluster=false;	// flag if we are currently in a cluster or not
 574	$clusters = array();	// array of clusters
 575	$cidx=-1;		// running cluster index
 576
 577	// Go through all the labels and construct a number of clusters
 578	for($i=0; $i < $n-1; ++$i) {
 579	    // Calc the angle distance between two consecutive slices
 580	    $a1=$this->la[$i];
 581	    $a2=$this->la[$i+1];
 582	    $q1 = $this->Quadrant($a1);
 583	    $q2 = $this->Quadrant($a2);
 584	    $diff = abs($a1-$a2);
 585	    if( $diff < $tresh_hold ) {
 586		if( $incluster ) {
 587		    $clusters[$cidx][1]++;
 588		    // Each cluster can only cover one quadrant
 589		    // Do we cross a quadrant ( and must break the cluster)
 590		    if( $q1 !=  $q2 ) {
 591			// If we cross a quadrant boundary we normally start a 
 592			// new cluster. However we need to take the 12'a clock
 593			// and 6'a clock positions into a special consideration.
 594			// Case 1: WE go from q=1 to q=2 if the last slice on
 595			// the cluster for q=1 is close to 12'a clock and the 
 596			// first slice in q=0 is small we extend the previous
 597			// cluster
 598			if( $q1 == 1 && $q2 == 0 && $a2 > (90-15)*M_PI/180 ) {
 599			    if( $i < $n-2 ) {
 600				$a3 = $this->la[$i+2];
 601				// If there isn't a cluster coming up with the next-next slice
 602				// we extend the previous cluster to cover this slice as well
 603				if( abs($a3-$a2) >= $tresh_hold ) {
 604				    $clusters[$cidx][1]++;
 605				    $i++;
 606				}
 607			    }
 608			}
 609			elseif( $q1 == 3 && $q2 == 2 && $a2 > (270-15)*M_PI/180 ) {
 610			    if( $i < $n-2 ) {
 611				$a3 = $this->la[$i+2];
 612				// If there isn't a cluster coming up with the next-next slice
 613				// we extend the previous cluster to cover this slice as well
 614				if( abs($a3-$a2) >= $tresh_hold ) {
 615				    $clusters[$cidx][1]++;
 616				    $i++;
 617				}
 618			    }
 619			}
 620
 621			if( $q1==2 && $q2==1 && $a2 > (180-15)*M_PI/180 ) {
 622			    $clusters[$cidx][1]++;
 623			    $i++;			    
 624			}
 625			
 626			$incluster = false;
 627		    }
 628		}
 629		elseif( $q1 == $q2)  {
 630		    $incluster = true;
 631		    // Now we have a special case for quadrant 0. If we previously
 632		    // have a cluster of one in quadrant 0 we just extend that
 633		    // cluster. If we don't do this then we risk that the label
 634		    // for the cluster of one will cross the guide-line
 635		    if( $q1 == 0 && $cidx > -1 && 
 636			$clusters[$cidx][1] == 1 && 
 637			$this->Quadrant($this->la[$clusters[$cidx][0]]) == 0 ) {
 638			$clusters[$cidx][1]++;
 639		    }
 640		    else {
 641			$cidx++;
 642			$clusters[$cidx][0] = $i;
 643			$clusters[$cidx][1] = 1;
 644		    }
 645		}
 646		else {  
 647		    // Create a "cluster" of one since we are just crossing
 648		    // a quadrant
 649		    $cidx++;
 650		    $clusters[$cidx][0] = $i;
 651		    $clusters[$cidx][1] = 1;	    
 652		}
 653	    }
 654	    else {
 655		if( $incluster ) {
 656		    // Add the last slice
 657		    $clusters[$cidx][1]++;
 658		    $incluster = false;
 659		}
 660		else { // Create a "cluster" of one
 661		    $cidx++;
 662		    $clusters[$cidx][0] = $i;
 663		    $clusters[$cidx][1] = 1;	    
 664		}
 665	    }
 666	}
 667	// Handle the very last slice
 668	if( $incluster ) {
 669	    $clusters[$cidx][1]++;
 670	}
 671	else { // Create a "cluster" of one
 672	    $cidx++;
 673	    $clusters[$cidx][0] = $i;
 674	    $clusters[$cidx][1] = 1;	    
 675	}
 676
 677	/*
 678	if( true ) { 
 679	    // Debug printout in labels
 680	    for( $i=0; $i <= $cidx; ++$i ) {
 681		for( $j=0; $j < $clusters[$i][1]; ++$j ) {
 682		    $a = $this->la[$clusters[$i][0]+$j];
 683		    $aa = round($a*180/M_PI);
 684		    $q = $this->Quadrant($a);
 685		    $this->labels[$clusters[$i][0]+$j]="[$q:$aa] $i:$j";
 686		}
 687	    }
 688	}
 689	*/
 690
 691	//-----------------------------------------------------------------------
 692	// Step 2 of the algorithm is use the clusters and draw the labels
 693	// and guidelines
 694	//-----------------------------------------------------------------------
 695
 696	// We use the font height as the base factor for how far we need to
 697	// spread the labels in the Y-direction.
 698	$img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
 699	$fh = $img->GetFontHeight();
 700	$origvstep=$fh*$this->iGuideVFactor;
 701	$this->value->SetMargin(0);
 702
 703	// Number of clusters found
 704	$nc = count($clusters);
 705
 706	// Walk through all the clusters
 707	for($i=0; $i < $nc; ++$i) {
 708
 709	    // Start angle and number of slices in this cluster
 710	    $csize = $clusters[$i][1];
 711	    $a = $this->la[$clusters[$i][0]];
 712	    $q = $this->Quadrant($a);
 713
 714	    // Now set up the start and end conditions to make sure that
 715	    // in each cluster we walk through the all the slices starting with the slice
 716	    // closest to the equator. Since all slices are numbered clockwise from "3'a clock"
 717	    // we have different conditions depending on in which quadrant the slice lies within.
 718	    if( $q == 0 ) {
 719		$start = $csize-1; $idx = $start; $step = -1; $vstep = -$origvstep;
 720	    }
 721	    elseif( $q == 1 ) {
 722		$start = 0; $idx = $start; $step = 1; $vstep = -$origvstep;
 723	    }
 724	    elseif( $q == 2 ) {
 725		$start = $csize-1; $idx = $start; $step = -1; $vstep = $origvstep;
 726	    }
 727	    elseif( $q == 3 ) {
 728		$start = 0; $idx = $start; $step = 1; $vstep = $origvstep;
 729	    }
 730
 731	    // Walk through all slices within this cluster
 732	    for($j=0; $j < $csize; ++$j) {   
 733		// Now adjust the position of the labels in each cluster starting
 734		// with the slice that is closest to the equator of the pie
 735		$a = $this->la[$clusters[$i][0]+$idx];
 736		    
 737		// Guide line start in the center of the arc of the slice
 738		$r = $radius+$this->explode_radius[$n-1-($clusters[$i][0]+$idx)];
 739		$x = round($r*cos($a)+$xc);
 740		$y = round($yc-$r*sin($a));
 741		
 742		// The distance from the arc depends on chosen font and the "R-Factor"
 743		$r += $fh*$this->iGuideLineRFactor;
 744
 745		// Should the labels be placed curved along the pie or in straight columns
 746		// outside the pie?
 747		if( $this->iGuideLineCurve )
 748		    $xt=round($r*cos($a)+$xc);
 749
 750		// If this is the first slice in the cluster we need some first time
 751		// proessing
 752		if( $idx == $start ) {
 753		    if( ! $this->iGuideLineCurve )
 754			$xt=round($r*cos($a)+$xc);
 755		    $yt=round($yc-$r*sin($a));
 756
 757		    // Some special consideration in case this cluster starts
 758		    // in quadrant 1 or 3 very close to the "equator" (< 20 degrees) 
 759		    // and the previous clusters last slice is within the tolerance. 
 760		    // In that case we add a font height to this labels Y-position 
 761		    // so it doesn't collide with
 762		    // the slice in the previous cluster
 763		    $prevcluster = ($i + ($nc-1) ) % $nc;
 764		    $previdx=$clusters[$prevcluster][0]+$clusters[$prevcluster][1]-1;
 765		    if( $q == 1 && $a > 160*M_PI/180 ) {
 766			// Get the angle for the previous clusters last slice
 767			$diff = abs($a-$this->la[$previdx]);
 768			 if( $diff < $tresh_hold ) {
 769			     $yt -= $fh;
 770			 }
 771		    }
 772		    elseif( $q == 3 && $a > 340*M_PI/180 ) {
 773			// We need to subtract 360 to compare angle distance between
 774			// q=0 and q=3
 775			$diff = abs($a-$this->la[$previdx]-360*M_PI/180);
 776			if( $diff < $tresh_hold ) {
 777			     $yt += $fh;
 778			}
 779		    }
 780
 781		}
 782		else {
 783		    // The step is at minimum $vstep but if the slices are relatively large
 784		    // we make sure that we add at least a step that corresponds to the vertical
 785		    // distance between the centers at the arc on the slice
 786		    $prev_a = $this->la[$clusters[$i][0]+($idx-$step)];
 787		    $dy = abs($radius*(sin($a)-sin($prev_a))*1.2);
 788		    if( $vstep > 0 )
 789			$yt += max($vstep,$dy);
 790		    else
 791			$yt += min($vstep,-$dy);
 792		}
 793
 794		$label = $this->labels[$clusters[$i][0]+$idx];
 795
 796		if( $csize == 1 ) {
 797		    // A "meta" cluster with only one slice
 798		    $r = $radius+$this->explode_radius[$n-1-($clusters[$i][0]+$idx)];
 799		    $rr = $r+$img->GetFontHeight()/2;
 800		    $xt=round($rr*cos($a)+$xc);
 801		    $yt=round($yc-$rr*sin($a));
 802		    $this->StrokeLabel($label,$img,$xc,$yc,$a,$r); 
 803		    if( $this->iShowGuideLineForSingle ) 
 804			$this->guideline->Stroke($img,$x,$y,$xt,$yt);
 805		}
 806		else {
 807		    $this->guideline->Stroke($img,$x,$y,$xt,$yt);
 808		    if( $q==1 || $q==2 ) {
 809			// Left side of Pie
 810			$this->guideline->Stroke($img,$xt,$yt,$xt-$this->guidelinemargin,$yt);
 811			$lbladj = -$this->guidelinemargin-5;
 812			$this->value->halign = "right";
 813			$this->value->valign = "center";
 814		    }
 815		    else {
 816			// Right side of pie
 817			$this->guideline->Stroke($img,$xt,$yt,$xt+$this->guidelinemargin,$yt);
 818			$lbladj = $this->guidelinemargin+5;
 819			$this->value->halign = "left";
 820			$this->value->valign = "center";
 821		    }
 822		    $this->value->Stroke($img,$label,$xt+$lbladj,$yt);
 823		}
 824
 825		// Udate idx to point to next slice in the cluster to process
 826		$idx += $step;
 827	    }
 828	}
 829    }
 830
 831    function StrokeAllLabels($img,$xc,$yc,$radius) {
 832	// First normalize all angles for labels
 833	$n = count($this->la);
 834	for($i=0; $i < $n; ++$i) {
 835	    $this->la[$i] = $this->NormAngle($this->la[$i]);
 836	}
 837	if( $this->guideline->iShow ) {
 838	    $this->StrokeGuideLabels($img,$xc,$yc,$radius);
 839	}
 840	else {
 841	    $n = count($this->labels);
 842	    for($i=0; $i < $n; ++$i) {
 843		$this->StrokeLabel($this->labels[$i],$img,$xc,$yc,
 844				   $this->la[$i],
 845				   $radius + $this->explode_radius[$n-1-$i]); 
 846	    }
 847	}
 848    }
 849
 850    // Position the labels of each slice
 851    function StrokeLabel($label,$img,$xc,$yc,$a,$radius) {
 852
 853	// Default value
 854	if( $this->ilabelposadj === 'auto' )
 855	    $this->ilabelposadj = 0.65;
 856	$r = $radius;
 857
 858	// We position the values diferently depending on if they are inside
 859	// or outside the pie
 860	if( $this->ilabelposadj < 1.0 ) {
 861
 862	    $this->value->SetAlign('center','center');
 863	    $this->value->margin = 0;
 864	    
 865	    $xt=round($this->ilabelposadj*$r*cos($a)+$xc);
 866	    $yt=round($yc-$this->ilabelposadj*$r*sin($a));
 867	    
 868	    $this->value->Stroke($img,$label,$xt,$yt);
 869	}
 870	else {
 871
 872	    $this->value->halign = "left";
 873	    $this->value->valign = "top";
 874	    $this->value->margin = 0;
 875	    	    
 876	    // Position the axis title. 
 877	    // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
 878	    // that intersects with the extension of the corresponding axis. The code looks a little
 879	    // bit messy but this is really the only way of having a reasonable position of the
 880	    // axis titles.
 881	    $img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
 882	    $h=$img->GetTextHeight($label);
 883	    // For numeric values the format of the display value
 884	    // must be taken into account
 885	    if( is_numeric($label) ) {
 886		if( $label > 0 )
 887		    $w=$img->GetTextWidth(sprintf($this->value->format,$label));
 888		else
 889		    $w=$img->GetTextWidth(sprintf($this->value->negformat,$label));
 890	    }
 891	    else
 892		$w=$img->GetTextWidth($label);
 893	    if( $this->ilabelposadj > 1.0 && $this->ilabelposadj < 5.0) {
 894		$r *= $this->ilabelposadj;
 895	    }
 896	    
 897	    $r += $img->GetFontHeight()/1.5;
 898
 899	    $xt=round($r*cos($a)+$xc);
 900	    $yt=round($yc-$r*sin($a));
 901
 902	    // Normalize angle
 903	    while( $a < 0 ) $a += 2*M_PI;
 904	    while( $a > 2*M_PI ) $a -= 2*M_PI;
 905		
 906	    if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
 907	    if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI; 
 908	    if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
 909	    if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
 910	    
 911	    if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
 912	    if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
 913	    if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
 914	    if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
 915	    if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
 916	    
 917	    $this->value->Stroke($img,$label,$xt-$dx*$w,$yt-$dy*$h);
 918	}
 919    }	
 920} // Class
 921
 922
 923//===================================================
 924// CLASS PiePlotC
 925// Description: Same as a normal pie plot but with a 
 926// filled circle in the center
 927//===================================================
 928class PiePlotC extends PiePlot {
 929    var $imidsize=0.5;		// Fraction of total width
 930    var $imidcolor='white';
 931    var $midtitle='';
 932    var $middlecsimtarget="",$middlecsimalt="";
 933
 934    function PiePlotC($data,$aCenterTitle='') {
 935	parent::PiePlot($data);
 936	$this->midtitle = new Text();
 937	$this->midtitle->ParagraphAlign('center');
 938    }
 939
 940    function SetMid($aTitle,$aColor='white',$aSize=0.5) {
 941	$this->midtitle->Set($aTitle);
 942
 943	$this->imidsize = $aSize ; 
 944	$this->imidcolor = $aColor ; 
 945    }
 946
 947    function SetMidTitle($aTitle) {
 948	$this->midtitle->Set($aTitle);
 949    }
 950
 951    function SetMidSize($aSize) {
 952	$this->imidsize = $aSize ; 
 953    }
 954
 955    function SetMidColor($aColor) {
 956	$this->imidcolor = $aColor ; 
 957    }
 958
 959    function SetMidCSIM($aTarget,$aAlt) {
 960	$this->middlecsimtarget = $aTarget;
 961	$this->middlecsimalt = $aAlt;
 962    }
 963
 964    function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {  
 965
 966        //Slice number, ellipse centre (x,y), radius, start angle, end angle
 967	while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
 968	while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
 969
 970	$sa = 2*M_PI - $sa;
 971	$ea = 2*M_PI - $ea;
 972
 973	// Special case when we have only one slice since then both start and end
 974	// angle will be == 0
 975	if( abs($sa - $ea) < 0.0001 ) {
 976	    $sa=2*M_PI; $ea=0;
 977	}
 978
 979	// Add inner circle first point
 980	$xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
 981	$yp = floor($yc-($this->imidsize*$radius*sin($ea)));
 982	$coords = "$xp, $yp";
 983	
 984	//add coordinates every 0.25 radians
 985	$a=$ea+0.25;
 986
 987	// If we cross the 260-limit with a slice we need to handle
 988	// the fact that end angle is smaller than start
 989	if( $sa < $ea ) {
 990	    while ($a <= 2*M_PI) {
 991		$xp = floor($radius*cos($a)+$xc);
 992		$yp = floor($yc-$radius*sin($a));
 993		$coords.= ", $xp, $yp";
 994		$a += 0.25;
 995	    }
 996	    $a -= 2*M_PI;
 997	}
 998
 999	while ($a < $sa) {
1000	    $xp = floor(($this->imidsize*$radius*cos($a)+$xc));
1001	    $yp = floor($yc-($this->imidsize*$radius*sin($a)));
1002	    $coords.= ", $xp, $yp";
1003	    $a += 0.25;
1004	}
1005
1006	// Make sure we end at the last point
1007	$xp = floor(($this->imidsize*$radius*cos($sa)+$xc));
1008	$yp = floor($yc-($this->imidsize*$radius*sin($sa)));
1009	$coords.= ", $xp, $yp";
1010
1011	// Straight line to outer circle
1012	$xp = floor($radius*cos($sa)+$xc);
1013	$yp = floor($yc-$radius*sin($sa));
1014	$coords.= ", $xp, $yp";	
1015
1016	//add coordinates every 0.25 radians
1017	$a=$sa - 0.25;
1018	while ($a > $ea) {
1019	    $xp = floor($radius*cos($a)+$xc);
1020	    $yp = floor($yc-$radius*sin($a));
1021	    $coords.= ", $xp, $yp";
1022	    $a -= 0.25;
1023	}
1024		
1025	//Add the last point on the arc
1026	$xp = floor($radius*cos($ea)+$xc);
1027	$yp = floor($yc-$radius*sin($ea));
1028	$coords.= ", $xp, $yp";
1029
1030	// Close the arc
1031	$xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
1032	$yp = floor($yc-($this->imidsize*$radius*sin($ea)));
1033	$coords .= ", $xp, $yp";
1034
1035	if( !empty($this->csimtargets[$i]) ) {
1036	    $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
1037		$this->csimtargets[$i]."\"";
1038	    if( !empty($this->csimalts[$i]) ) {
1039		$tmp=sprintf($this->csimalts[$i],$this->data[$i]);
1040		$this->csimareas .= " title=\"$tmp\"";
1041	    }
1042	    $this->csimareas .= " alt=\"$tmp\" />\n";
1043	}
1044    }
1045
1046
1047    function Stroke($img,$aaoption=0) {
1048
1049	// Stroke the pie but don't stroke values
1050	$tmp =  $this->value->show;
1051	$this->value->show = false;
1052	parent::Stroke($img,$aaoption);
1053	$this->value->show = $tmp;
1054
1055 	$xc = round($this->posx*$img->width);
1056	$yc = round($this->posy*$img->height);
1057
1058	$radius = floor($this->radius * min($img->width,$img->height)) ;
1059
1060
1061	if( $this->imidsize > 0 && $aaoption !== 2 ) {
1062
1063	    if( $this->ishadowcolor != "" ) {
1064		$img->SetColor($this->ishadowcolor);
1065		$img->FilledCircle($xc+$this->ishadowdrop,$yc+$this->ishadowdrop,
1066				   round($radius*$this->imidsize));
1067	    }
1068
1069	    $img->SetColor($this->imidcolor);
1070	    $img->FilledCircle($xc,$yc,round($radius*$this->imidsize));
1071
1072	    if(  $this->pie_border && $aaoption === 0 ) {
1073		$img->SetColor($this->color);
1074		$img->Circle($xc,$yc,round($radius*$this->imidsize));
1075	    }
1076
1077	    if( !empty($this->middlecsimtarget) )
1078		$this->AddMiddleCSIM($xc,$yc,round($radius*$this->imidsize));
1079
1080	}
1081
1082	if( $this->value->show && $aaoption !== 1) {
1083	    $this->StrokeAllLabels($img,$xc,$yc,$radius);
1084	    $this->midtitle->Pos($xc,$yc,'center','center');
1085	    $this->midtitle->Stroke($img);
1086	}
1087
1088    }
1089
1090    function AddMiddleCSIM($xc,$yc,$r) {
1091	$xc=round($xc);$yc=round($yc);$r=round($r);
1092	$this->csimareas .= "<area shape=\"circle\" coords=\"$xc,$yc,$r\" href=\"".
1093	    $this->middlecsimtarget."\"";
1094	if( !empty($this->middlecsimalt) ) {
1095	    $tmp = $this->middlecsimalt;
1096	    $this->csimareas .= " title=\"$tmp\"";
1097	}
1098	$this->csimareas .= " alt=\"$tmp\" />\n";
1099    }
1100
1101    function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
1102
1103	if( $this->ilabelposadj === 'auto' )
1104	    $this->ilabelposadj = (1-$this->imidsize)/2+$this->imidsize;
1105
1106	parent::StrokeLabel($label,$img,$xc,$yc,$a,$r);
1107
1108    }
1109
1110}
1111
1112
1113//===================================================
1114// CLASS PieGraph
1115// Description: 
1116//===================================================
1117class PieGraph extends Graph {
1118    var $posx, $posy, $radius;		
1119    var $legends=array();	
1120    var $plots=array();
1121    var $pieaa = false ;
1122//---------------
1123// CONSTRUCTOR
1124    function PieGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
1125	$this->Graph($width,$height,$cachedName,$timeout,$inline);
1126	$this->posx=$width/2;
1127	$this->posy=$height/2;
1128	$this->SetColor(array(255,255,255));		
1129    }
1130
1131//---------------
1132// PUBLIC METHODS	
1133    function Add($aObj) {
1134
1135	if( is_array($aObj) && count($aObj) > 0 )
1136	    $cl = $aObj[0];
1137	else
1138	    $cl = $aObj;
1139
1140	if( is_a($cl,'Text') ) 
1141	    $this->AddText($aObj);
1142	elseif( is_a($cl,'IconPlot') ) 
1143	    $this->AddIcon($aObj);
1144	else {
1145	    if( is_array($aObj) ) {
1146		$n = count($aObj);
1147		for($i=0; $i < $n; ++$i ) {
1148		    $this->plots[] = $aObj[$i];
1149		}
1150	    }
1151	    else {
1152		$this->plots[] = $aObj;
1153	    }
1154	}
1155    }
1156
1157    function SetAntiAliasing($aFlg=true) {
1158	$this->pieaa = $aFlg;
1159    }
1160	
1161    function SetColor($c) {
1162	$this->SetMarginColor($c);
1163    }
1164
1165    function DisplayCSIMAreas() {
1166	    $csim="";
1167	    foreach($this->plots as $p ) {
1168		$csim .= $p->GetCSIMareas();
1169	    }
1170	    //$csim.= $this->legend->GetCSIMareas();
1171	    if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
1172		$this->img->SetColor($this->csimcolor);
1173		$n = count($coords[0]);
1174		for ($i=0; $i < $n; $i++) {
1175		    if ($coords[1][$i]=="poly") {
1176			preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
1177			$this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
1178			$m = count($pts[0]);
1179			for ($j=0; $j < $m; $j++) {
1180			    $this->img->LineTo($pts[1][$j],$pts[2][$j]);
1181			}
1182		    } else if ($coords[1][$i]=="rect") {
1183			$pts = preg_split('/,/', $coords[2][$i]);
1184			$this->img->SetStartPoint($pts[0],$pts[1]);
1185			$this->img->LineTo($pts[2],$pts[1]);
1186			$this->img->LineTo($pts[2],$pts[3]);
1187			$this->img->LineTo($pts[0],$pts[3]);
1188			$this->img->LineTo($pts[0],$pts[1]);
1189						
1190		    }
1191		}
1192	    }
1193    }
1194
1195    // Method description
1196    function Stroke($aStrokeFileName="") {
1197	// If the filename is the predefined value = '_csim_special_'
1198	// we assume that the call to stroke only needs to do enough
1199	// to correctly generate the CSIM maps.
1200	// We use this variable to skip things we don't strictly need
1201	// to do to generate the image map to improve performance
1202	// a best we can. Therefor you will see a lot of tests !$_csim in the
1203	// code below.
1204	$_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
1205
1206	// We need to know if we have stroked the plot in the
1207	// GetCSIMareas. Otherwise the CSIM hasn't been generated
1208	// and in the case of GetCSIM called before stroke to generate
1209	// CSIM without storing an image to disk GetCSIM must call Stroke.
1210	$this->iHasStroked = true;
1211		
1212	$n = count($this->plots);
1213
1214	if( $this->pieaa ) {
1215
1216	    if( !$_csim ) {
1217		if( $this->background_image != "" ) {
1218		    $this->StrokeFrameBackground();		
1219		}
1220		else {
1221		    $this->StrokeFrame();		
1222		}
1223	    }
1224
1225
1226	    $w = $this->img->width;
1227	    $h = $this->img->height;
1228	    $oldimg = $this->img->img;
1229
1230	    $this->img->CreateImgCanvas(2*$w,2*$h);
1231	    
1232	    $this->img->SetColor( $this->margin_color );
1233	    $this->img->FilledRectangle(0,0,2*$w-1,2*$h-1);
1234
1235	    // Make all icons *2 i size since we will be scaling down the
1236	    // image to do the anti aliasing
1237	    $ni = count($this->iIcons);
1238	    for($i=0; $i < $ni; ++$i) {
1239		$this->iIcons[$i]->iScale *= 2 ;
1240	    }
1241	    $this->StrokeIcons();
1242
1243	    for($i=0; $i < $n; ++$i) {
1244		if( $this->plots[$i]->posx > 1 ) 
1245		    $this->plots[$i]->posx *= 2 ;
1246		if( $this->plots[$i]->posy > 1 ) 
1247		    $this->plots[$i]->posy *= 2 ;
1248
1249		$this->plots[$i]->Stroke($this->img,1);
1250
1251		if( $this->plots[$i]->posx > 1 ) 
1252		    $this->plots[$i]->posx /= 2 ;
1253		if( $this->plots[$i]->posy > 1 ) 
1254		    $this->plots[$i]->posy /= 2 ;
1255	    }
1256
1257	    $indent = $this->doframe ? ($this->frame_weight + ($this->doshadow ? $this->shadow_width : 0 )) : 0 ;
1258	    $indent += $this->framebevel ? $this->framebeveldepth + 1 : 0 ;
1259	    $this->img->CopyCanvasH($oldimg,$this->img->img,$indent,$indent,$indent,$indent,
1260				    $w-2*$indent,$h-2*$indent,2*($w-$indent),2*($h-$indent));
1261
1262	    $this->img->img = $oldimg ;
1263	    $this->img->width = $w ;
1264	    $this->img->height = $h ;
1265
1266	    for($i=0; $i < $n; ++$i) {
1267		$this->plots[$i]->Stroke($this->img,2); // Stroke labels
1268		$this->plots[$i]->Legend($this);
1269	    }
1270
1271	}
1272	else {
1273	    // No antialias
1274	    if( !$_csim ) {
1275		if( $this->background_image != "" ) {
1276		    $this->StrokeFrameBackground();		
1277		}
1278		else {
1279		    $this->StrokeFrame();
1280		    $this->StrokeBackgroundGrad();		
1281		}
1282	    }
1283
1284	    $this->StrokeIcons();
1285
1286	    for($i=0; $i < $n; ++$i) {
1287		$this->plots[$i]->Stroke($this->img);
1288		$this->plots[$i]->Legend($this);
1289	    }
1290	}
1291
1292	$this->legend->Stroke($this->img);
1293	$this->footer->Stroke($this->img);
1294	$this->StrokeTitles();
1295
1296	if( !$_csim ) {	
1297
1298	    // Stroke texts
1299	    if( $this->texts != null ) {
1300		$n = count($this->texts);
1301		for($i=0; $i < $n; ++$i ) {
1302		    $this->texts[$i]->Stroke($this->img);
1303		}
1304	    }
1305
1306	    if( _JPG_DEBUG ) {
1307		$this->DisplayCSIMAreas();
1308	    }
1309
1310	    // Should we do any final image transformation
1311	    if( $this->iImgTrans ) {
1312		if( !class_exists('ImgTrans') ) {
1313		    require_once('jpgraph_imgtrans.php');
1314		    //JpGraphError::Raise('In order to use image transformation you must include the file jpgraph_imgtrans.php in your script.');
1315		}
1316	       
1317		$tform = new ImgTrans($this->img->img);
1318		$this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
1319						 $this->iImgTransDirection,$this->iImgTransHighQ,
1320						 $this->iImgTransMinSize,$this->iImgTransFillColor,
1321						 $this->iImgTransBorder);
1322	    }
1323
1324
1325	    // If the filename is given as the special "__handle"
1326	    // then the image handler is returned and the image is NOT
1327	    // streamed back
1328	    if( $aStrokeFileName == _IMG_HANDLER ) {
1329		return $this->img->img;
1330	    }
1331	    else {
1332		// Finally stream the generated picture					
1333		$this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
1334					   $aStrokeFileName);		
1335	    }
1336	}
1337    }
1338} // Class
1339
1340/* EOF */
1341?>