source: trunk/web/addons/job_monarch/lib/pchart/pChart/pChart.class @ 619

Last change on this file since 619 was 619, checked in by ramonb, 15 years ago

lib/:

  • added new AJAX dependancies: ExtJS, pChart, Lightbox2
File size: 136.4 KB
Line 
1<?php
2 /*
3     pChart - a PHP class to build charts!
4     Copyright (C) 2008 Jean-Damien POGOLOTTI
5     Version  1.27d last updated on 09/30/08
6
7     http://pchart.sourceforge.net
8
9     This program is free software: you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation, either version 1,2,3 of the License, or
12     (at your option) any later version.
13
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18
19     You should have received a copy of the GNU General Public License
20     along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22     Class initialisation :
23      pChart($XSize,$YSize)
24     Draw methods :
25      drawBackground($R,$G,$B)
26      drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B)
27      drawFilledRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B,$DrawBorder=TRUE,$Alpha=100)
28      drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
29      drawFilledRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
30      drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
31      drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
32      drawEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
33      drawFilledEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
34      drawLine($X1,$Y1,$X2,$Y2,$R,$G,$B,$GraphFunction=FALSE)
35      drawDottedLine($X1,$Y1,$X2,$Y2,$DotSize,$R,$G,$B)
36      drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B)
37      drawFromPNG($FileName,$X,$Y,$Alpha=100)
38      drawFromGIF($FileName,$X,$Y,$Alpha=100)
39      drawFromJPG($FileName,$X,$Y,$Alpha=100)
40     Graph setup methods :
41      addBorder($Width=3,$R=0,$G=0,$B=0)
42      clearScale()
43      clearShadow()
44      createColorGradientPalette($R1,$G1,$B1,$R2,$G2,$B2,$Shades)
45      drawGraphArea($R,$G,$B,$Stripe=FALSE)
46      drawScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1,$RightScale=FALSE)
47      drawRightScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1)
48      drawXYScale($Data,$DataDescription,$YSerieName,$XSerieName,$R,$G,$B,$WithMargin=0,$Angle=0,$Decimals=1)
49      drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=100)
50      drawLegend($XPos,$YPos,$DataDescription,$R,$G,$B,$Rs=-1,$Gs=-1,$Bs=-1,$Rt=0,$Gt=0,$Bt=0,$Border=FALSE)
51      drawPieLegend($XPos,$YPos,$Data,$DataDescription,$R,$G,$B)
52      drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2=-1,$YPos2=-1,$Shadow=FALSE)
53      drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4,$FreeText=NULL)
54      drawArea($Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50)
55      drawRadarAxis($Data,$DataDescription,$Mosaic=TRUE,$BorderOffset=10,$A_R=60,$A_G=60,$A_B=60,$S_R=200,$S_G=200,$S_B=200,$MaxValue=-1)
56      drawGraphAreaGradient($R,$G,$B,$Decay,$Target=TARGET_GRAPHAREA)
57      drawTextBox($X1,$Y1,$X2,$Y2,$Text,$Angle=0,$R=255,$G=255,$B=255,$Align=ALIGN_LEFT,$Shadow=TRUE,$BgR=-1,$BgG=-1,$BgB=-1,$Alpha=100)
58      getLegendBoxSize($DataDescription)
59      loadColorPalette($FileName,$Delimiter=",")
60      reportWarnings($Interface="CLI")
61      setGraphArea($X1,$Y1,$X2,$Y2)
62      setLabel($Data,$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210)
63      setColorPalette($ID,$R,$G,$B)
64      setCurrency($Currency)
65      setDateFormat($Format)
66      setFontProperties($FontName,$FontSize)
67      setLineStyle($Width=1,$DotSize=0)
68      setFixedScale($VMin,$VMax,$Divisions=5,$VXMin=0,$VXMin=0,$XDivisions=5)
69      setShadowProperties($XDistance=1,$YDistance=1,$R=60,$G=60,$B=60,$Alpha)
70      writeValues($Data,$DataDescription,$Series)
71    Graphs methods :
72      drawPlotGraph($Data,$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=FALSE)
73      drawXYPlotGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1)
74      drawLineGraph($Data,$DataDescription,$SerieName="")
75      drawXYGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0)
76      drawFilledLineGraph($Data,$DataDescription,$Alpha=100,$AroundZero=FALSE)
77      drawCubicCurve($Data,$DataDescription,$Accuracy=.1,$SerieName="")
78      drawFilledCubicCurve($Data,$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE)
79      drawOverlayBarGraph($Data,$DataDescription,$Alpha=50)
80      drawBarGraph($Data,$DataDescription,$Shadow=FALSE)
81      drawStackedBarGraph($Data,$DataDescription,$Alpha=50,$Contiguous=FALSE)
82      drawLimitsGraph($Data,$DataDescription,$R=0,$G=0,$B=0)
83      drawRadar($Data,$DataDescription,$BorderOffset=10,$MaxValue=-1)
84      drawFilledRadar($Data,$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1)
85      drawBasicPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0)
86      drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals = 0)
87      drawFlatPieGraphWithShadow($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals = 0)
88      drawPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$EnhanceColors=TRUE,$Skew=60,$SpliceHeight=20,$SpliceDistance=0,$Decimals=0)
89     Other methods :
90      setImageMap($Mode=TRUE,$GraphID="MyGraph")
91      getImageMap($MapName,$Flush=TRUE)
92      Render($FileName)
93      Stroke()
94 */
95 
96 /* Declare some script wide constants */
97 define("SCALE_NORMAL",1);
98 define("SCALE_ADDALL",2);
99 define("SCALE_START0",3);
100 define("SCALE_ADDALLSTART0",4);
101 define("PIE_PERCENTAGE", 1);
102 define("PIE_LABELS",2);
103 define("PIE_NOLABEL",3);
104 define("PIE_PERCENTAGE_LABEL", 4);
105 define("TARGET_GRAPHAREA",1);
106 define("TARGET_BACKGROUND",2);
107 define("ALIGN_TOP_LEFT",1);
108 define("ALIGN_TOP_CENTER",2);
109 define("ALIGN_TOP_RIGHT",3);
110 define("ALIGN_LEFT",4);
111 define("ALIGN_CENTER",5);
112 define("ALIGN_RIGHT",6);
113 define("ALIGN_BOTTOM_LEFT",7);
114 define("ALIGN_BOTTOM_CENTER",8);
115 define("ALIGN_BOTTOM_RIGHT",9);
116
117 /* pChart class definition */
118 class pChart
119  {
120   /* Palettes definition */
121   var $Palette = array("0"=>array("R"=>188,"G"=>224,"B"=>46),
122                        "1"=>array("R"=>224,"G"=>100,"B"=>46),
123                        "2"=>array("R"=>224,"G"=>214,"B"=>46),
124                        "3"=>array("R"=>46,"G"=>151,"B"=>224),
125                        "4"=>array("R"=>176,"G"=>46,"B"=>224),
126                        "5"=>array("R"=>224,"G"=>46,"B"=>117),
127                        "6"=>array("R"=>92,"G"=>224,"B"=>46),
128                        "7"=>array("R"=>224,"G"=>176,"B"=>46));
129
130   /* Some static vars used in the class */
131   var $XSize          = NULL;
132   var $YSize          = NULL;
133   var $Picture        = NULL;
134   var $ImageMap       = NULL;
135
136   /* Error management */
137   var $ErrorReporting = FALSE;
138   var $ErrorInterface = "CLI";
139   var $Errors         = NULL;
140   var $ErrorFontName  = "Fonts/pf_arma_five.ttf";
141   var $ErrorFontSize  = 6;
142
143   /* vars related to the graphing area */
144   var $GArea_X1        = NULL;
145   var $GArea_Y1        = NULL;
146   var $GArea_X2        = NULL;
147   var $GArea_Y2        = NULL;
148   var $GAreaXOffset    = NULL;
149   var $VMax            = NULL;
150   var $VMin            = NULL;
151   var $VXMax           = NULL;
152   var $VXMin           = NULL;
153   var $Divisions       = NULL;
154   var $XDivisions      = NULL;
155   var $DivisionHeight  = NULL;
156   var $XDivisionHeight = NULL;
157   var $DivisionCount   = NULL;
158   var $XDivisionCount  = NULL;
159   var $DivisionRatio   = NULL;
160   var $XDivisionRatio  = NULL;
161   var $DivisionWidth   = NULL;
162   var $DataCount       = NULL;
163   var $Currency        = "\$";
164
165   /* Text format related vars */
166   var $FontName       = NULL;
167   var $FontSize       = NULL;
168   var $DateFormat     = "d/m/Y";
169
170   /* Lines format related vars */
171   var $LineWidth      = 1;
172   var $LineDotSize    = 0;
173
174   /* Layer related vars */
175   var $Layers         = NULL;
176
177   /* Set antialias quality : 0 is maximum, 100 minimum*/
178   var $AntialiasQuality = 0;
179
180   /* Shadow settings */
181   var $ShadowActive    = FALSE;
182   var $ShadowXDistance = 1;
183   var $ShadowYDistance = 1;
184   var $ShadowRColor    = 60;
185   var $ShadowGColor    = 60;
186   var $ShadowBColor    = 60;
187   var $ShadowAlpha     = 50;
188   var $ShadowBlur      = 0;
189
190   /* Image Map settings */
191   var $BuildMap         = FALSE;
192   var $MapFunction      = NULL;
193   var $tmpFolder        = "tmp/";
194   var $MapID            = NULL;
195
196   /* This function create the background picture */
197   function pChart($XSize,$YSize)
198    {
199     $this->XSize   = $XSize;
200     $this->YSize   = $YSize;
201     $this->Picture = imagecreatetruecolor($XSize,$YSize);
202
203     $C_White =$this->AllocateColor($this->Picture,255,255,255);
204     imagefilledrectangle($this->Picture,0,0,$XSize,$YSize,$C_White);
205     imagecolortransparent($this->Picture,$C_White);
206
207     $this->setFontProperties("tahoma.ttf",8);
208    }
209
210  /* Set if warnings should be reported */
211  function reportWarnings($Interface="CLI")
212   {
213    $this->ErrorReporting = TRUE;
214    $this->ErrorInterface = $Interface;
215    }
216
217   /* Set the font properties */
218   function setFontProperties($FontName,$FontSize)
219    {
220     $this->FontName = $FontName;
221     $this->FontSize = $FontSize;
222    }
223
224   /* Set the shadow properties */
225   function setShadowProperties($XDistance=1,$YDistance=1,$R=60,$G=60,$B=60,$Alpha=50,$Blur=0)
226    {
227     $this->ShadowActive    = TRUE;
228     $this->ShadowXDistance = $XDistance;
229     $this->ShadowYDistance = $YDistance;
230     $this->ShadowRColor    = $R;
231     $this->ShadowGColor    = $G;
232     $this->ShadowBColor    = $B;
233     $this->ShadowAlpha     = $Alpha;
234     $this->ShadowBlur      = $Blur;
235    }
236
237   /* Remove shadow option */
238   function clearShadow()
239    {
240     $this->ShadowActive = FALSE;
241    }
242 
243   /* Set Palette color */
244   function setColorPalette($ID,$R,$G,$B)
245    {
246     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
247     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
248     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
249
250     $this->Palette[$ID]["R"] = $R;
251     $this->Palette[$ID]["G"] = $G;
252     $this->Palette[$ID]["B"] = $B;
253    }
254
255   /* Create a color palette shading from one color to another */
256   function createColorGradientPalette($R1,$G1,$B1,$R2,$G2,$B2,$Shades)
257    {
258     $RFactor = ($R2-$R1)/$Shades;
259     $GFactor = ($G2-$G1)/$Shades;
260     $BFactor = ($B2-$B1)/$Shades;
261
262     for($i=0;$i<=$Shades-1;$i++)
263      {
264       $this->Palette[$i]["R"] = $R1+$RFactor*$i;
265       $this->Palette[$i]["G"] = $G1+$GFactor*$i;
266       $this->Palette[$i]["B"] = $B1+$BFactor*$i;
267      }
268    }
269
270   /* Load Color Palette from file */
271   function loadColorPalette($FileName,$Delimiter=",")
272    {
273     $handle  = @fopen($FileName,"r");
274     $ColorID = 0;
275     if ($handle)
276      {
277       while (!feof($handle))
278        {
279         $buffer = fgets($handle, 4096);
280         $buffer = str_replace(chr(10),"",$buffer);
281         $buffer = str_replace(chr(13),"",$buffer);
282         $Values = split($Delimiter,$buffer);
283         if ( count($Values) == 3 )
284          {
285           $this->Palette[$ColorID]["R"] = $Values[0];
286           $this->Palette[$ColorID]["G"] = $Values[1];
287           $this->Palette[$ColorID]["B"] = $Values[2];
288           $ColorID++;
289          }
290        }
291      }
292    }
293
294   /* Set line style */
295  function setLineStyle($Width=1,$DotSize=0)
296   {
297    $this->LineWidth   = $Width;
298    $this->LineDotSize = $DotSize;
299   }
300
301   /* Set currency symbol */
302   function setCurrency($Currency)
303    {
304     $this->Currency = $Currency;
305    }
306
307   /* Set the graph area location */
308   function setGraphArea($X1,$Y1,$X2,$Y2)
309    {
310     $this->GArea_X1 = $X1;
311     $this->GArea_Y1 = $Y1;
312     $this->GArea_X2 = $X2;
313     $this->GArea_Y2 = $Y2;
314    }
315
316   /* Prepare the graph area */
317   function drawGraphArea($R,$G,$B,$Stripe=FALSE)
318    {
319     $this->drawFilledRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B,FALSE);
320     $this->drawRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R-40,$G-40,$B-40);
321
322     if ( $Stripe )
323      {
324       $R2 = $R-15; if ( $R2 < 0 ) { $R2 = 0; }
325       $G2 = $R-15; if ( $G2 < 0 ) { $G2 = 0; }
326       $B2 = $R-15; if ( $B2 < 0 ) { $B2 = 0; }
327
328       $LineColor =$this->AllocateColor($this->Picture,$R2,$G2,$B2);
329       $SkewWidth = $this->GArea_Y2-$this->GArea_Y1-1;
330
331       for($i=$this->GArea_X1-$SkewWidth;$i<=$this->GArea_X2;$i=$i+4)
332        {
333         $X1 = $i;            $Y1 = $this->GArea_Y2;
334         $X2 = $i+$SkewWidth; $Y2 = $this->GArea_Y1;
335
336
337         if ( $X1 < $this->GArea_X1 )
338          { $X1 = $this->GArea_X1; $Y1 = $this->GArea_Y1 + $X2 - $this->GArea_X1 + 1; }
339
340         if ( $X2 >= $this->GArea_X2 )
341          { $Y2 = $this->GArea_Y1 + $X2 - $this->GArea_X2 +1; $X2 = $this->GArea_X2 - 1; }
342// * Fixed in 1.27 *         { $X2 = $this->GArea_X2 - 1; $Y2 = $this->GArea_Y2 - ($this->GArea_X2 - $X1); }
343
344         imageline($this->Picture,$X1,$Y1,$X2,$Y2+1,$LineColor);
345        }
346      }
347    }
348
349   /* Allow you to clear the scale : used if drawing multiple charts */
350   function clearScale()
351    {
352     $this->VMin       = NULL;
353     $this->VMax       = NULL;
354     $this->VXMin      = NULL;
355     $this->VXMax      = NULL;
356     $this->Divisions  = NULL;
357     $this->XDivisions = NULL;    }
358
359   /* Allow you to fix the scale, use this to bypass the automatic scaling */
360   function setFixedScale($VMin,$VMax,$Divisions=5,$VXMin=0,$VXMax=0,$XDivisions=5)
361    {
362     $this->VMin      = $VMin;
363     $this->VMax      = $VMax;
364     $this->Divisions = $Divisions;
365
366     if ( !$VXMin == 0 )
367      {
368       $this->VXMin      = $VXMin;
369       $this->VXMax      = $VXMax;
370       $this->XDivisions = $XDivisions;
371      }
372    }
373
374   /* Wrapper to the drawScale() function allowing a second scale to be drawn */
375   function drawRightScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1)
376    {
377     $this->drawScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks,$Angle,$Decimals,$WithMargin,$SkipLabels,TRUE);
378    }
379
380   /* Compute and draw the scale */
381   function drawScale($Data,$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1,$RightScale=FALSE)
382    {
383     /* Validate the Data and DataDescription array */
384     $this->validateData("drawScale",$Data);
385
386     $C_TextColor         =$this->AllocateColor($this->Picture,$R,$G,$B);
387
388     $this->drawLine($this->GArea_X1,$this->GArea_Y1,$this->GArea_X1,$this->GArea_Y2,$R,$G,$B);
389     $this->drawLine($this->GArea_X1,$this->GArea_Y2,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B);
390
391     if ( $this->VMin == NULL && $this->VMax == NULL)
392      {
393       if (isset($DataDescription["Values"][0]))
394        {
395         $this->VMin = $Data[0][$DataDescription["Values"][0]];
396         $this->VMax = $Data[0][$DataDescription["Values"][0]];
397        }
398       else { $this->VMin = 2147483647; $this->VMax = -2147483647; }
399
400       /* Compute Min and Max values */
401       if ( $ScaleMode == SCALE_NORMAL || $ScaleMode == SCALE_START0 )
402        {
403         if ( $ScaleMode == SCALE_START0 ) { $this->VMin = 0; }
404
405         foreach ( $Data as $Key => $Values )
406          {
407           foreach ( $DataDescription["Values"] as $Key2 => $ColName )
408            {
409             if (isset($Data[$Key][$ColName]))
410              {
411               $Value = $Data[$Key][$ColName];
412
413               if ( is_numeric($Value) )
414                {
415                 if ( $this->VMax < $Value) { $this->VMax = $Value; }
416                 if ( $this->VMin > $Value) { $this->VMin = $Value; }
417                }
418              }
419            }
420          }
421        }
422       elseif ( $ScaleMode == SCALE_ADDALL || $ScaleMode == SCALE_ADDALLSTART0 ) /* Experimental */
423        {
424         if ( $ScaleMode == SCALE_ADDALLSTART0 ) { $this->VMin = 0; }
425
426         foreach ( $Data as $Key => $Values )
427          {
428           $Sum = 0;
429           foreach ( $DataDescription["Values"] as $Key2 => $ColName )
430            {
431             if (isset($Data[$Key][$ColName]))
432              {
433               $Value = $Data[$Key][$ColName];
434               if ( is_numeric($Value) )
435                $Sum  += $Value;
436              }
437            }
438           if ( $this->VMax < $Sum) { $this->VMax = $Sum; }
439           if ( $this->VMin > $Sum) { $this->VMin = $Sum; }
440          }
441        }
442
443       if ( $this->VMax > preg_replace('/\.[0-9]+/','',$this->VMax) )
444        $this->VMax = preg_replace('/\.[0-9]+/','',$this->VMax)+1;
445
446       /* If all values are the same */
447       if ( $this->VMax == $this->VMin )
448        {
449         if ( $this->VMax >= 0 ) { $this->VMax++; }
450         else { $this->VMin--; }
451        }
452
453       $DataRange = $this->VMax - $this->VMin;
454       if ( $DataRange == 0 ) { $DataRange = .1; }
455
456       /* Compute automatic scaling */
457       $ScaleOk = FALSE; $Factor = 1;
458       $MinDivHeight = 25; $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight;
459
460       if ( $this->VMin == 0 && $this->VMax == 0 )
461        { $this->VMin = 0; $this->VMax = 2; $Scale = 1; $Divisions = 2;}
462       elseif ($MaxDivs > 1)
463        {
464         while(!$ScaleOk)
465          {
466           $Scale1 = ( $this->VMax - $this->VMin ) / $Factor;
467           $Scale2 = ( $this->VMax - $this->VMin ) / $Factor / 2;
468           $Scale4 = ( $this->VMax - $this->VMin ) / $Factor / 4;
469
470           if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale1); $Scale = 1;}
471           if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale2); $Scale = 2;}
472           if (!$ScaleOk)
473            {
474             if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
475             if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
476            }
477          }
478
479         if ( floor($this->VMax / $Scale / $Factor) != $this->VMax / $Scale / $Factor)
480          {
481           $GridID     = floor ( $this->VMax / $Scale / $Factor) + 1;
482           $this->VMax = $GridID * $Scale * $Factor;
483           $Divisions++;
484          }
485
486         if ( floor($this->VMin / $Scale / $Factor) != $this->VMin / $Scale / $Factor)
487          {
488           $GridID     = floor( $this->VMin / $Scale / $Factor);
489           $this->VMin = $GridID * $Scale * $Factor;
490           $Divisions++;
491          }
492        }
493       else /* Can occurs for small graphs */
494        $Scale = 1;
495
496       if ( !isset($Divisions) )
497        $Divisions = 2;
498
499       if ($Scale == 1 && $Divisions%2 == 1)
500        $Divisions--;
501      }
502     else
503      $Divisions = $this->Divisions;
504
505     $this->DivisionCount = $Divisions;
506
507     $DataRange = $this->VMax - $this->VMin;
508     if ( $DataRange == 0 ) { $DataRange = .1; }
509
510     $this->DivisionHeight = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $Divisions;
511     $this->DivisionRatio  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $DataRange;
512
513     $this->GAreaXOffset  = 0;
514     if ( count($Data) > 1 )
515      {
516       if ( $WithMargin == FALSE )
517        $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data)-1);
518       else
519        {
520         $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data));
521         $this->GAreaXOffset  = $this->DivisionWidth / 2;
522        }
523      }
524     else
525      {
526       $this->DivisionWidth = $this->GArea_X2 - $this->GArea_X1;
527       $this->GAreaXOffset  = $this->DivisionWidth / 2;
528      }
529
530     $this->DataCount = count($Data);
531
532     if ( $DrawTicks == FALSE )
533      return(0);
534
535     $YPos = $this->GArea_Y2; $XMin = NULL;
536     for($i=1;$i<=$Divisions+1;$i++)
537      {
538       if ( $RightScale )
539        $this->drawLine($this->GArea_X2,$YPos,$this->GArea_X2+5,$YPos,$R,$G,$B);
540       else
541        $this->drawLine($this->GArea_X1,$YPos,$this->GArea_X1-5,$YPos,$R,$G,$B);
542
543       $Value     = $this->VMin + ($i-1) * (( $this->VMax - $this->VMin ) / $Divisions);
544       $Value     = round($Value * pow(10,$Decimals)) / pow(10,$Decimals);
545       if ( $DataDescription["Format"]["Y"] == "number" )
546        $Value = $Value.$DataDescription["Unit"]["Y"];
547       if ( $DataDescription["Format"]["Y"] == "time" )
548        $Value = $this->ToTime($Value);       
549       if ( $DataDescription["Format"]["Y"] == "date" )
550        $Value = $this->ToDate($Value);       
551       if ( $DataDescription["Format"]["Y"] == "metric" )
552        $Value = $this->ToMetric($Value);       
553       if ( $DataDescription["Format"]["Y"] == "currency" )
554        $Value = $this->ToCurrency($Value);       
555
556       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
557       $TextWidth = $Position[2]-$Position[0];
558
559       if ( $RightScale )
560        {
561         imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+10,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
562         if ( $XMin < $this->GArea_X2+15+$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X2+15+$TextWidth; }
563        }
564       else
565        {
566         imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1-10-$TextWidth,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
567         if ( $XMin > $this->GArea_X1-10-$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X1-10-$TextWidth; }
568        }
569
570       $YPos = $YPos - $this->DivisionHeight;
571      }
572
573     /* Write the Y Axis caption if set */
574     if ( isset($DataDescription["Axis"]["Y"]) )
575      {
576       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["Y"]);
577       $TextHeight = abs($Position[1])+abs($Position[3]);
578       $TextTop    = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight/2);
579
580       if ( $RightScale )
581        imagettftext($this->Picture,$this->FontSize,90,$XMin+$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
582       else
583        imagettftext($this->Picture,$this->FontSize,90,$XMin-$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
584      }
585
586     /* Horizontal Axis */
587     $XPos = $this->GArea_X1 + $this->GAreaXOffset;
588     $ID = 1; $YMax = NULL;
589     foreach ( $Data as $Key => $Values )
590      {
591       if ( $ID % $SkipLabels == 0 )
592        {
593         $this->drawLine(floor($XPos),$this->GArea_Y2,floor($XPos),$this->GArea_Y2+5,$R,$G,$B);
594         $Value      = $Data[$Key][$DataDescription["Position"]];
595         if ( $DataDescription["Format"]["X"] == "number" )
596          $Value = $Value.$DataDescription["Unit"]["X"];
597         if ( $DataDescription["Format"]["X"] == "time" )
598          $Value = $this->ToTime($Value);       
599         if ( $DataDescription["Format"]["X"] == "date" )
600          $Value = $this->ToDate($Value);       
601         if ( $DataDescription["Format"]["X"] == "metric" )
602          $Value = $this->ToMetric($Value);       
603         if ( $DataDescription["Format"]["X"] == "currency" )
604          $Value = $this->ToCurrency($Value);       
605
606         $Position   = imageftbbox($this->FontSize,$Angle,$this->FontName,$Value);
607         $TextWidth  = abs($Position[2])+abs($Position[0]);
608         $TextHeight = abs($Position[1])+abs($Position[3]);
609
610         if ( $Angle == 0 )
611          {
612           $YPos = $this->GArea_Y2+18;
613           imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-floor($TextWidth/2),$YPos,$C_TextColor,$this->FontName,$Value);
614          }
615         else
616          {
617           $YPos = $this->GArea_Y2+10+$TextHeight;
618           if ( $Angle <= 90 )
619            imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
620           else
621            imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)+$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
622          }
623         if ( $YMax < $YPos || $YMax == NULL ) { $YMax = $YPos; }
624        }
625
626       $XPos = $XPos + $this->DivisionWidth;
627       $ID++;
628      }
629
630    /* Write the X Axis caption if set */
631    if ( isset($DataDescription["Axis"]["X"]) )
632      {
633       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["X"]);
634       $TextWidth  = abs($Position[2])+abs($Position[0]);
635       $TextLeft   = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth/2);
636       imagettftext($this->Picture,$this->FontSize,0,$TextLeft,$YMax+$this->FontSize+5,$C_TextColor,$this->FontName,$DataDescription["Axis"]["X"]);
637      }
638    }
639
640   /* Compute and draw the scale for X/Y charts */
641   function drawXYScale($Data,$DataDescription,$YSerieName,$XSerieName,$R,$G,$B,$WithMargin=0,$Angle=0,$Decimals=1)
642    {
643     /* Validate the Data and DataDescription array */
644     $this->validateData("drawScale",$Data);
645
646     $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
647
648     $this->drawLine($this->GArea_X1,$this->GArea_Y1,$this->GArea_X1,$this->GArea_Y2,$R,$G,$B);
649     $this->drawLine($this->GArea_X1,$this->GArea_Y2,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B);
650
651     /* Process Y scale */
652     if ( $this->VMin == NULL && $this->VMax == NULL)
653      {
654       $this->VMin = $Data[0][$YSerieName];
655       $this->VMax = $Data[0][$YSerieName];
656
657       foreach ( $Data as $Key => $Values )
658        {
659         if (isset($Data[$Key][$YSerieName]))
660          {
661           $Value = $Data[$Key][$YSerieName];
662           if ( $this->VMax < $Value) { $this->VMax = $Value; }
663           if ( $this->VMin > $Value) { $this->VMin = $Value; }
664          }
665        }
666
667       if ( $this->VMax > preg_replace('/\.[0-9]+/','',$this->VMax) )
668        $this->VMax = preg_replace('/\.[0-9]+/','',$this->VMax)+1;
669
670       $DataRange = $this->VMax - $this->VMin;
671       if ( $DataRange == 0 ) { $DataRange = .1; }
672
673       /* Compute automatic scaling */
674       $ScaleOk = FALSE; $Factor = 1;
675       $MinDivHeight = 25; $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight;
676
677       if ( $this->VMin == 0 && $this->VMax == 0 )
678        { $this->VMin = 0; $this->VMax = 2; $Scale = 1; $Divisions = 2;}
679       elseif ($MaxDivs > 1)
680        {
681         while(!$ScaleOk)
682          {
683           $Scale1 = ( $this->VMax - $this->VMin ) / $Factor;
684           $Scale2 = ( $this->VMax - $this->VMin ) / $Factor / 2;
685           $Scale4 = ( $this->VMax - $this->VMin ) / $Factor / 4;
686
687           if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale1); $Scale = 1;}
688           if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale2); $Scale = 2;}
689           if (!$ScaleOk)
690            {
691             if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
692             if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
693            }
694          }
695
696         if ( floor($this->VMax / $Scale / $Factor) != $this->VMax / $Scale / $Factor)
697          {
698           $GridID     = floor ( $this->VMax / $Scale / $Factor) + 1;
699           $this->VMax = $GridID * $Scale * $Factor;
700           $Divisions++;
701          }
702
703         if ( floor($this->VMin / $Scale / $Factor) != $this->VMin / $Scale / $Factor)
704          {
705           $GridID     = floor( $this->VMin / $Scale / $Factor);
706           $this->VMin = $GridID * $Scale * $Factor;
707           $Divisions++;
708          }
709        }
710       else /* Can occurs for small graphs */
711        $Scale = 1;
712
713       if ( !isset($Divisions) )
714        $Divisions = 2;
715
716       if ( $this->isRealInt(($this->VMax-$this->VMin)/($Divisions-1)))
717        $Divisions--;
718       elseif ( $this->isRealInt(($this->VMax-$this->VMin)/($Divisions+1)))
719        $Divisions++;
720      }
721     else
722      $Divisions = $this->Divisions;
723
724     $this->DivisionCount = $Divisions;
725
726     $DataRange = $this->VMax - $this->VMin;
727     if ( $DataRange == 0 ) { $DataRange = .1; }
728
729     $this->DivisionHeight = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $Divisions;
730     $this->DivisionRatio  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $DataRange;
731
732     $YPos = $this->GArea_Y2; $XMin = NULL;
733     for($i=1;$i<=$Divisions+1;$i++)
734      {
735       $this->drawLine($this->GArea_X1,$YPos,$this->GArea_X1-5,$YPos,$R,$G,$B);
736       $Value     = $this->VMin + ($i-1) * (( $this->VMax - $this->VMin ) / $Divisions);
737       $Value     = round($Value * pow(10,$Decimals)) / pow(10,$Decimals);
738       if ( $DataDescription["Format"]["Y"] == "number" )
739        $Value = $Value.$DataDescription["Unit"]["Y"];
740       if ( $DataDescription["Format"]["Y"] == "time" )
741        $Value = $this->ToTime($Value);       
742       if ( $DataDescription["Format"]["Y"] == "date" )
743        $Value = $this->ToDate($Value);       
744       if ( $DataDescription["Format"]["Y"] == "metric" )
745        $Value = $this->ToMetric($Value);       
746       if ( $DataDescription["Format"]["Y"] == "currency" )
747        $Value = $this->ToCurrency($Value);       
748
749       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
750       $TextWidth = $Position[2]-$Position[0];
751       imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1-10-$TextWidth,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
752
753       if ( $XMin > $this->GArea_X1-10-$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X1-10-$TextWidth; }
754
755       $YPos = $YPos - $this->DivisionHeight;
756      }
757
758     /* Process X scale */
759     if ( $this->VXMin == NULL && $this->VXMax == NULL)
760      {
761       $this->VXMin = $Data[0][$XSerieName];
762       $this->VXMax = $Data[0][$XSerieName];
763
764       foreach ( $Data as $Key => $Values )
765        {
766         if (isset($Data[$Key][$XSerieName]))
767          {
768           $Value = $Data[$Key][$XSerieName];
769           if ( $this->VXMax < $Value) { $this->VXMax = $Value; }
770           if ( $this->VXMin > $Value) { $this->VXMin = $Value; }
771          }
772        }
773
774       if ( $this->VXMax > preg_replace('/\.[0-9]+/','',$this->VXMax) )
775        $this->VXMax = preg_replace('/\.[0-9]+/','',$this->VXMax)+1;
776
777       $DataRange = $this->VMax - $this->VMin;
778       if ( $DataRange == 0 ) { $DataRange = .1; }
779
780       /* Compute automatic scaling */
781       $ScaleOk = FALSE; $Factor = 1;
782       $MinDivWidth = 25; $MaxDivs = ($this->GArea_X2 - $this->GArea_X1) / $MinDivWidth;
783
784       if ( $this->VXMin == 0 && $this->VXMax == 0 )
785        { $this->VXMin = 0; $this->VXMax = 2; $Scale = 1; $XDivisions = 2;}
786       elseif ($MaxDivs > 1)
787        {
788         while(!$ScaleOk)
789          {
790           $Scale1 = ( $this->VXMax - $this->VXMin ) / $Factor;
791           $Scale2 = ( $this->VXMax - $this->VXMin ) / $Factor / 2;
792           $Scale4 = ( $this->VXMax - $this->VXMin ) / $Factor / 4;
793
794           if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $XDivisions = floor($Scale1); $Scale = 1;}
795           if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $XDivisions = floor($Scale2); $Scale = 2;}
796           if (!$ScaleOk)
797            {
798             if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
799             if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
800            }
801          }
802
803         if ( floor($this->VXMax / $Scale / $Factor) != $this->VXMax / $Scale / $Factor)
804          {
805           $GridID     = floor ( $this->VXMax / $Scale / $Factor) + 1;
806           $this->VXMax = $GridID * $Scale * $Factor;
807           $XDivisions++;
808          }
809
810         if ( floor($this->VXMin / $Scale / $Factor) != $this->VXMin / $Scale / $Factor)
811          {
812           $GridID     = floor( $this->VXMin / $Scale / $Factor);
813           $this->VXMin = $GridID * $Scale * $Factor;
814           $XDivisions++;
815          }
816        }
817       else /* Can occurs for small graphs */
818        $Scale = 1;
819
820       if ( !isset($XDivisions) )
821        $XDivisions = 2;
822
823       if ( $this->isRealInt(($this->VXMax-$this->VXMin)/($XDivisions-1)))
824        $XDivisions--;
825       elseif ( $this->isRealInt(($this->VXMax-$this->VXMin)/($XDivisions+1)))
826        $XDivisions++;
827      }
828     else
829      $XDivisions = $this->XDivisions;
830
831     $this->XDivisionCount = $Divisions;
832     $this->DataCount      = $Divisions + 2;
833
834     $XDataRange = $this->VXMax - $this->VXMin;
835     if ( $XDataRange == 0 ) { $XDataRange = .1; }
836
837     $this->DivisionWidth   = ( $this->GArea_X2 - $this->GArea_X1 ) / $XDivisions;
838     $this->XDivisionRatio  = ( $this->GArea_X2 - $this->GArea_X1 ) / $XDataRange;
839
840     $XPos = $this->GArea_X1; $YMax = NULL;
841     for($i=1;$i<=$XDivisions+1;$i++)
842      {
843       $this->drawLine($XPos,$this->GArea_Y2,$XPos,$this->GArea_Y2+5,$R,$G,$B);
844
845       $Value     = $this->VXMin + ($i-1) * (( $this->VXMax - $this->VXMin ) / $XDivisions);
846       $Value     = round($Value * pow(10,$Decimals)) / pow(10,$Decimals);
847       if ( $DataDescription["Format"]["Y"] == "number" )
848        $Value = $Value.$DataDescription["Unit"]["Y"];
849       if ( $DataDescription["Format"]["Y"] == "time" )
850        $Value = $this->ToTime($Value);       
851       if ( $DataDescription["Format"]["Y"] == "date" )
852        $Value = $this->ToDate($Value);       
853       if ( $DataDescription["Format"]["Y"] == "metric" )
854        $Value = $this->ToMetric($Value);       
855       if ( $DataDescription["Format"]["Y"] == "currency" )
856        $Value = $this->ToCurrency($Value);       
857
858       $Position   = imageftbbox($this->FontSize,$Angle,$this->FontName,$Value);
859       $TextWidth  = abs($Position[2])+abs($Position[0]);
860       $TextHeight = abs($Position[1])+abs($Position[3]);
861
862       if ( $Angle == 0 )
863        {
864         $YPos = $this->GArea_Y2+18;
865         imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-floor($TextWidth/2),$YPos,$C_TextColor,$this->FontName,$Value);
866        }
867       else
868        {
869         $YPos = $this->GArea_Y2+10+$TextHeight;
870         if ( $Angle <= 90 )
871          imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
872         else
873          imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)+$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
874         }
875
876       if ( $YMax < $YPos || $YMax == NULL ) { $YMax = $YPos; }
877
878       $XPos = $XPos + $this->DivisionWidth;
879      }
880
881     /* Write the Y Axis caption if set */
882     if ( isset($DataDescription["Axis"]["Y"]) )
883      {
884       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["Y"]);
885       $TextHeight = abs($Position[1])+abs($Position[3]);
886       $TextTop    = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight/2);
887       imagettftext($this->Picture,$this->FontSize,90,$XMin-$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
888      }
889
890     /* Write the X Axis caption if set */
891     if ( isset($DataDescription["Axis"]["X"]) )
892      {
893       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["X"]);
894       $TextWidth  = abs($Position[2])+abs($Position[0]);
895       $TextLeft   = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth/2);
896       imagettftext($this->Picture,$this->FontSize,0,$TextLeft,$YMax+$this->FontSize+5,$C_TextColor,$this->FontName,$DataDescription["Axis"]["X"]);
897      }
898    }
899
900   /* Compute and draw the scale */
901   function drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=100)
902    {
903     /* Draw mosaic */
904     if ( $Mosaic )
905      {
906       $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
907       $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
908
909       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
910       $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
911       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
912       imagecolortransparent($this->Layers[0],$C_White);
913
914       $C_Rectangle =$this->AllocateColor($this->Layers[0],250,250,250);
915
916       $YPos  = $LayerHeight; //$this->GArea_Y2-1;
917       $LastY = $YPos;
918       for($i=0;$i<=$this->DivisionCount;$i++)
919        {
920         $LastY = $YPos;
921         $YPos  = $YPos - $this->DivisionHeight;
922
923         if ( $YPos <= 0 ) { $YPos = 1; }
924
925         if ( $i % 2 == 0 )
926          {
927           imagefilledrectangle($this->Layers[0],1,$YPos,$LayerWidth-1,$LastY,$C_Rectangle);
928          }
929        }
930       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
931       imagedestroy($this->Layers[0]);
932      }
933
934     /* Horizontal lines */
935     $YPos = $this->GArea_Y2 - $this->DivisionHeight;
936     for($i=1;$i<=$this->DivisionCount;$i++)
937      {
938       if ( $YPos > $this->GArea_Y1 && $YPos < $this->GArea_Y2 )
939        $this->drawDottedLine($this->GArea_X1,$YPos,$this->GArea_X2,$YPos,$LineWidth,$R,$G,$B);
940       
941       $YPos = $YPos - $this->DivisionHeight;
942      }
943
944     /* Vertical lines */
945     if ( $this->GAreaXOffset == 0 )
946      { $XPos = $this->GArea_X1 + $this->DivisionWidth + $this->GAreaXOffset; $ColCount = $this->DataCount-2; }
947     else
948      { $XPos = $this->GArea_X1 + $this->GAreaXOffset; $ColCount = floor( ($this->GArea_X2 - $this->GArea_X1) / $this->DivisionWidth ); }
949
950     for($i=1;$i<=$ColCount;$i++)
951      {
952       if ( $XPos > $this->GArea_X1 && $XPos < $this->GArea_X2 )
953        $this->drawDottedLine(floor($XPos),$this->GArea_Y1,floor($XPos),$this->GArea_Y2,$LineWidth,$R,$G,$B);
954       $XPos = $XPos + $this->DivisionWidth;
955      }
956    }
957
958   /* retrieve the legends size */
959   function getLegendBoxSize($DataDescription)
960    {
961     if ( !isset($DataDescription["Description"]) )
962      return(-1);
963
964     /* <-10->[8]<-4->Text<-10-> */
965     $MaxWidth = 0; $MaxHeight = 8;
966     foreach($DataDescription["Description"] as $Key => $Value)
967      {
968       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
969       $TextWidth  = $Position[2]-$Position[0];
970       $TextHeight = $Position[1]-$Position[7];
971       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
972       $MaxHeight = $MaxHeight + $TextHeight + 4;
973      }
974     $MaxHeight = $MaxHeight - 3;
975     $MaxWidth  = $MaxWidth + 32;
976
977     return(array($MaxWidth,$MaxHeight));
978    }
979
980   /* Draw the data legends */
981   function drawLegend($XPos,$YPos,$DataDescription,$R,$G,$B,$Rs=-1,$Gs=-1,$Bs=-1,$Rt=0,$Gt=0,$Bt=0,$Border=TRUE)
982    {
983     /* Validate the Data and DataDescription array */
984     $this->validateDataDescription("drawLegend",$DataDescription);
985
986     if ( !isset($DataDescription["Description"]) )
987      return(-1);
988
989     $C_TextColor =$this->AllocateColor($this->Picture,$Rt,$Gt,$Bt);
990
991     /* <-10->[8]<-4->Text<-10-> */
992     $MaxWidth = 0; $MaxHeight = 8;
993     foreach($DataDescription["Description"] as $Key => $Value)
994      {
995       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
996       $TextWidth  = $Position[2]-$Position[0];
997       $TextHeight = $Position[1]-$Position[7];
998       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
999       $MaxHeight = $MaxHeight + $TextHeight + 4;
1000      }
1001     $MaxHeight = $MaxHeight - 5;
1002     $MaxWidth  = $MaxWidth + 32;
1003
1004     if ( $Rs == -1 || $Gs == -1 || $Bs == -1 )
1005      { $Rs = $R-30; $Gs = $G-30; $Bs = $B-30; }
1006
1007     if ( $Border )
1008      {
1009       $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$Rs,$Gs,$Bs);
1010       $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1011      }
1012
1013     $YOffset = 4 + $this->FontSize; $ID = 0;
1014     foreach($DataDescription["Description"] as $Key => $Value)
1015      {
1016       $this->drawFilledRoundedRectangle($XPos+10,$YPos+$YOffset-4,$XPos+14,$YPos+$YOffset-4,2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]);
1017       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value);
1018
1019       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1020       $TextHeight = $Position[1]-$Position[7];
1021
1022       $YOffset = $YOffset + $TextHeight + 4;
1023       $ID++;
1024      }
1025    }
1026
1027   /* Draw the data legends */
1028   function drawPieLegend($XPos,$YPos,$Data,$DataDescription,$R,$G,$B)
1029    {
1030     /* Validate the Data and DataDescription array */
1031     $this->validateDataDescription("drawPieLegend",$DataDescription,FALSE);
1032     $this->validateData("drawPieLegend",$Data);
1033
1034     if ( !isset($DataDescription["Position"]) )
1035      return(-1);
1036
1037     $C_TextColor =$this->AllocateColor($this->Picture,0,0,0);
1038
1039     /* <-10->[8]<-4->Text<-10-> */
1040     $MaxWidth = 0; $MaxHeight = 8;
1041     foreach($Data as $Key => $Value)
1042      {
1043       $Value2 = $Value[$DataDescription["Position"]];
1044       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1045       $TextWidth = $Position[2]-$Position[0];
1046       $TextHeight = $Position[1]-$Position[7];
1047       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
1048
1049       $MaxHeight = $MaxHeight + $TextHeight + 4;
1050      }
1051     $MaxHeight = $MaxHeight - 3;
1052     $MaxWidth  = $MaxWidth + 32;
1053
1054     $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$R-30,$G-30,$B-30);
1055     $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1056
1057     $YOffset = 4 + $this->FontSize; $ID = 0;
1058     foreach($Data as $Key => $Value)
1059      {
1060       $Value2     = $Value[$DataDescription["Position"]];
1061       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1062       $TextHeight = $Position[1]-$Position[7];
1063       $this->drawFilledRectangle($XPos+10,$YPos+$YOffset-6,$XPos+14,$YPos+$YOffset-2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]);
1064
1065       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value2);
1066       $YOffset = $YOffset + $TextHeight + 4;
1067       $ID++;
1068      }
1069    }
1070
1071   /* Draw the graph title */
1072   function drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2=-1,$YPos2=-1,$Shadow=FALSE)
1073    {
1074     $C_TextColor = $this->AllocateColor($this->Picture,$R,$G,$B);
1075
1076     if ( $XPos2 != -1 )
1077      {
1078       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1079       $TextWidth = $Position[2]-$Position[0];
1080       $XPos      = floor(( $XPos2 - $XPos - $TextWidth ) / 2 ) + $XPos;
1081      }
1082
1083     if ( $YPos2 != -1 )
1084      {
1085       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1086       $TextHeight = $Position[5]-$Position[3];
1087       $YPos       = floor(( $YPos2 - $YPos - $TextHeight ) / 2 ) + $YPos;
1088      }
1089
1090     if ( $Shadow )
1091      {
1092       $C_ShadowColor = $this->AllocateColor($this->Picture,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor);
1093       imagettftext($this->Picture,$this->FontSize,0,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$C_ShadowColor,$this->FontName,$Value);     
1094      }
1095
1096     imagettftext($this->Picture,$this->FontSize,0,$XPos,$YPos,$C_TextColor,$this->FontName,$Value);     
1097    }
1098
1099   /* Draw a text box with text align & alpha properties */
1100   function drawTextBox($X1,$Y1,$X2,$Y2,$Text,$Angle=0,$R=255,$G=255,$B=255,$Align=ALIGN_LEFT,$Shadow=TRUE,$BgR=-1,$BgG=-1,$BgB=-1,$Alpha=100)
1101    {
1102     $Position   = imageftbbox($this->FontSize,$Angle,$this->FontName,$Text);
1103     $TextWidth  = $Position[2]-$Position[0];
1104     $TextHeight = $Position[5]-$Position[3];
1105     $AreaWidth  = $X2 - $X1;
1106     $AreaHeight = $Y2 - $Y1;
1107
1108     if ( $BgR != -1 && $BgG != -1 && $BgB != -1 )
1109      $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$BgR,$BgG,$BgB,FALSE,$Alpha);
1110
1111     if ( $Align == ALIGN_TOP_LEFT )      { $X = $X1+1; $Y = $Y1+$this->FontSize+1; }
1112     if ( $Align == ALIGN_TOP_CENTER )    { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+$this->FontSize+1; }
1113     if ( $Align == ALIGN_TOP_RIGHT )     { $X = $X2-$TextWidth-1; $Y = $Y1+$this->FontSize+1; }
1114     if ( $Align == ALIGN_LEFT )          { $X = $X1+1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1115     if ( $Align == ALIGN_CENTER )        { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1116     if ( $Align == ALIGN_RIGHT )         { $X = $X2-$TextWidth-1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1117     if ( $Align == ALIGN_BOTTOM_LEFT )   { $X = $X1+1; $Y = $Y2-1; }
1118     if ( $Align == ALIGN_BOTTOM_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y2-1; }
1119     if ( $Align == ALIGN_BOTTOM_RIGHT )  { $X = $X2-$TextWidth-1; $Y = $Y2-1; }
1120
1121     $C_TextColor   =$this->AllocateColor($this->Picture,$R,$G,$B);
1122     $C_ShadowColor =$this->AllocateColor($this->Picture,0,0,0);
1123     if ( $Shadow )
1124      imagettftext($this->Picture,$this->FontSize,$Angle,$X+1,$Y+1,$C_ShadowColor,$this->FontName,$Text);     
1125
1126     imagettftext($this->Picture,$this->FontSize,$Angle,$X,$Y,$C_TextColor,$this->FontName,$Text);     
1127    }
1128
1129   /* Compute and draw the scale */
1130   function drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4,$FreeText=NULL)
1131    {
1132     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
1133     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
1134     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
1135
1136     $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
1137     $Y = $this->GArea_Y2 - ($Value - $this->VMin) * $this->DivisionRatio;
1138
1139     if ( $Y <= $this->GArea_Y1 || $Y >= $this->GArea_Y2 )
1140      return(-1);
1141
1142     if ( $TickWidth == 0 )
1143      $this->drawLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$R,$G,$B);
1144     else
1145      $this->drawDottedLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$TickWidth,$R,$G,$B);
1146
1147     if ( $ShowLabel )
1148      {
1149       if ( $FreeText == NULL )
1150        { $Label = $Value; } else { $Label = $FreeText; }
1151
1152       if ( $ShowOnRight )
1153        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+2,$Y+($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1154       else
1155        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1+2,$Y-($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1156      }
1157    }
1158
1159   /* This function put a label on a specific point */
1160   function setLabel($Data,$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210)
1161    {
1162     /* Validate the Data and DataDescription array */
1163     $this->validateDataDescription("setLabel",$DataDescription);
1164     $this->validateData("setLabel",$Data);
1165     $ShadowFactor = 100;
1166     $C_Label      =$this->AllocateColor($this->Picture,$R,$G,$B);
1167     $C_Shadow     =$this->AllocateColor($this->Picture,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1168     $C_TextColor  =$this->AllocateColor($this->Picture,0,0,0);
1169
1170     $Cp = 0; $Found = FALSE;
1171     foreach ( $Data as $Key => $Value )
1172      {
1173       if ( $Data[$Key][$DataDescription["Position"]] == $ValueName )
1174        { $NumericalValue = $Data[$Key][$SerieName]; $Found = TRUE; }
1175       if ( !$Found )
1176        $Cp++;
1177      }
1178
1179     $XPos = $this->GArea_X1 + $this->GAreaXOffset + ( $this->DivisionWidth * $Cp ) + 2;
1180     $YPos = $this->GArea_Y2 - ($NumericalValue - $this->VMin) * $this->DivisionRatio;
1181
1182     $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
1183     $TextHeight = $Position[3] - $Position[5];
1184     $TextWidth  = $Position[2]-$Position[0] + 2;
1185     $TextOffset = floor($TextHeight/2);
1186
1187     // Shadow
1188     $Poly = array($XPos+1,$YPos+1,$XPos + 9,$YPos - $TextOffset,$XPos + 8,$YPos + $TextOffset + 2);
1189     imagefilledpolygon($this->Picture,$Poly,3,$C_Shadow);
1190     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos - $TextOffset - .2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1191     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1192     $this->drawFilledRectangle($XPos + 9,$YPos - $TextOffset-.2,$XPos + 13 + $TextWidth,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1193
1194     // Label background
1195     $Poly = array($XPos,$YPos,$XPos + 8,$YPos - $TextOffset - 1,$XPos + 8,$YPos + $TextOffset + 1);
1196     imagefilledpolygon($this->Picture,$Poly,3,$C_Label);
1197     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos - $TextOffset - 1.2,$R,$G,$B);
1198     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos + $TextOffset + 1.2,$R,$G,$B);
1199     $this->drawFilledRectangle($XPos + 8,$YPos - $TextOffset - 1.2,$XPos + 12 + $TextWidth,$YPos + $TextOffset + 1.2,$R,$G,$B);
1200
1201     imagettftext($this->Picture,$this->FontSize,0,$XPos + 10,$YPos + $TextOffset,$C_TextColor,$this->FontName,$Caption);
1202    }
1203
1204   /* This function draw a plot graph */
1205   function drawPlotGraph($Data,$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=FALSE)
1206    {
1207     /* Validate the Data and DataDescription array */
1208     $this->validateDataDescription("drawPlotGraph",$DataDescription);
1209     $this->validateData("drawPlotGraph",$Data);
1210
1211     $GraphID = 0;
1212     $Ro = $R2; $Go = $G2; $Bo = $B2;
1213
1214     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1215      {
1216       $ID = 0;
1217       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1218        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1219
1220       $R = $this->Palette[$ColorID]["R"];
1221       $G = $this->Palette[$ColorID]["G"];
1222       $B = $this->Palette[$ColorID]["B"];
1223       $R2 = $Ro; $G2 = $Go; $B2 = $Bo;
1224
1225       if ( isset($DataDescription["Symbol"][$ColName]) )
1226        {
1227         $Is_Alpha = ((ord ( file_get_contents ($DataDescription["Symbol"][$ColName], false, null, 25, 1)) & 6) & 4) == 4;
1228
1229         $Infos       = getimagesize($DataDescription["Symbol"][$ColName]);
1230         $ImageWidth  = $Infos[0];
1231         $ImageHeight = $Infos[1];
1232         $Symbol      = imagecreatefromgif($DataDescription["Symbol"][$ColName]);
1233        }
1234
1235       $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1236       $Hsize = round($BigRadius/2);
1237       $R3 = -1; $G3 = -1; $B3 = -1;
1238       foreach ( $Data as $Key => $Values )
1239        {
1240         $Value = $Data[$Key][$ColName];
1241         $YPos  = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1242
1243         /* Save point into the image map if option activated */
1244         if ( $this->BuildMap )
1245          $this->addToImageMap($XPos-$Hsize,$YPos-$Hsize,$XPos+1+$Hsize,$YPos+$Hsize+1,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Plot");
1246
1247         if ( is_numeric($Value) )
1248          {
1249           if ( !isset($DataDescription["Symbol"][$ColName]) )
1250            {
1251
1252             if ( $Shadow )
1253              {
1254               if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1255                $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1256               else
1257                {
1258                 $R3 = $this->Palette[$ColorID]["R"]-20; if ( $R3 < 0 ) { $R3 = 0; }
1259                 $G3 = $this->Palette[$ColorID]["G"]-20; if ( $G3 < 0 ) { $G3 = 0; }
1260                 $B3 = $this->Palette[$ColorID]["B"]-20; if ( $B3 < 0 ) { $B3 = 0; }
1261                 $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1262                }
1263              }
1264
1265             $this->drawFilledCircle($XPos+1,$YPos+1,$BigRadius,$R,$G,$B);
1266
1267             if ( $SmallRadius != 0 )
1268              {
1269               if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1270                $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1271               else
1272                {
1273                 $R2 = $this->Palette[$ColorID]["R"]-15; if ( $R2 < 0 ) { $R2 = 0; }
1274                 $G2 = $this->Palette[$ColorID]["G"]-15; if ( $G2 < 0 ) { $G2 = 0; }
1275                 $B2 = $this->Palette[$ColorID]["B"]-15; if ( $B2 < 0 ) { $B2 = 0; }
1276
1277                 $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1278                }
1279              }
1280            }
1281           else
1282            {
1283             imagecopymerge($this->Picture,$Symbol,$XPos+1-$ImageWidth/2,$YPos+1-$ImageHeight/2,0,0,$ImageWidth,$ImageHeight,100);
1284            }
1285          }
1286
1287         $XPos = $XPos + $this->DivisionWidth;
1288        }
1289       $GraphID++;
1290      }
1291    }
1292
1293   /* This function draw a plot graph in an X/Y space */
1294   function drawXYPlotGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=TRUE)
1295    {
1296     $R = $this->Palette[$PaletteID]["R"];
1297     $G = $this->Palette[$PaletteID]["G"];
1298     $B = $this->Palette[$PaletteID]["B"];
1299     $R3 = -1; $G3 = -1; $B3 = -1;
1300
1301     $YLast = -1; $XLast = -1;
1302     foreach ( $Data as $Key => $Values )
1303      {
1304       if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1305        {
1306         $X = $Data[$Key][$XSerieName];
1307         $Y = $Data[$Key][$YSerieName];
1308
1309         $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1310         $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1311
1312
1313         if ( $Shadow )
1314          {
1315           if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1316            $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1317           else
1318            {
1319             $R3 = $this->Palette[$PaletteID]["R"]-20; if ( $R < 0 ) { $R = 0; }
1320             $G3 = $this->Palette[$PaletteID]["G"]-20; if ( $G < 0 ) { $G = 0; }
1321             $B3 = $this->Palette[$PaletteID]["B"]-20; if ( $B < 0 ) { $B = 0; }
1322             $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1323            }
1324          }
1325
1326         $this->drawFilledCircle($X+1,$Y+1,$BigRadius,$R,$G,$B);
1327
1328         if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1329          $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1330         else
1331          {
1332           $R2 = $this->Palette[$PaletteID]["R"]+20; if ( $R > 255 ) { $R = 255; }
1333           $G2 = $this->Palette[$PaletteID]["G"]+20; if ( $G > 255 ) { $G = 255; }
1334           $B2 = $this->Palette[$PaletteID]["B"]+20; if ( $B > 255 ) { $B = 255; }
1335           $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1336          }
1337        }
1338      }
1339
1340    }
1341
1342   /* This function draw an area between two series */
1343   function drawArea($Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50)
1344    {
1345     /* Validate the Data and DataDescription array */
1346     $this->validateData("drawArea",$Data);
1347
1348     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1349     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1350
1351     $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1352     $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
1353     imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1354     imagecolortransparent($this->Layers[0],$C_White);
1355
1356     $C_Graph =$this->AllocateColor($this->Layers[0],$R,$G,$B);
1357
1358     $XPos     = $this->GAreaXOffset;
1359     $LastXPos = -1;
1360     foreach ( $Data as $Key => $Values )
1361      {
1362       $Value1 = $Data[$Key][$Serie1];
1363       $Value2 = $Data[$Key][$Serie2];
1364       $YPos1  = $LayerHeight - (($Value1-$this->VMin) * $this->DivisionRatio);
1365       $YPos2  = $LayerHeight - (($Value2-$this->VMin) * $this->DivisionRatio);
1366
1367       if ( $LastXPos != -1 )
1368        {
1369         $Points   = "";
1370         $Points[] = $LastXPos; $Points[] = $LastYPos1;
1371         $Points[] = $LastXPos; $Points[] = $LastYPos2;
1372         $Points[] = $XPos; $Points[] = $YPos2;
1373         $Points[] = $XPos; $Points[] = $YPos1;
1374
1375         imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1376        }
1377
1378       $LastYPos1 = $YPos1;
1379       $LastYPos2 = $YPos2;
1380       $LastXPos  = $XPos;
1381
1382       $XPos = $XPos + $this->DivisionWidth;
1383      }
1384
1385     imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1386     imagedestroy($this->Layers[0]);
1387    }
1388
1389
1390   /* This function write the values of the specified series */
1391   function writeValues($Data,$DataDescription,$Series)
1392    {
1393     /* Validate the Data and DataDescription array */
1394     $this->validateDataDescription("writeValues",$DataDescription);
1395     $this->validateData("writeValues",$Data);
1396
1397     if ( !is_array($Series) ) { $Series = array($Series); }     
1398
1399     foreach($Series as $Key => $Serie)
1400      {
1401       $ID = 0;
1402       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1403        { if ( $keyI == $Serie ) { $ColorID = $ID; }; $ID++; }
1404
1405       $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1406       $XLast = -1;
1407       foreach ( $Data as $Key => $Values )
1408        {
1409         if ( isset($Data[$Key][$Serie]) && is_numeric($Data[$Key][$Serie]))
1410          {
1411           $Value = $Data[$Key][$Serie];
1412           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1413
1414           $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Value);
1415           $Width  = $Positions[2] - $Positions[6]; $XOffset = $XPos - ($Width/2);
1416           $Height = $Positions[3] - $Positions[7]; $YOffset = $YPos - 4;
1417
1418           $C_TextColor =$this->AllocateColor($this->Picture,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1419           imagettftext($this->Picture,$this->FontSize,0,$XOffset,$YOffset,$C_TextColor,$this->FontName,$Value);
1420          }
1421         $XPos = $XPos + $this->DivisionWidth;
1422        }
1423
1424      }
1425    }
1426
1427   /* This function draw a line graph */
1428   function drawLineGraph($Data,$DataDescription,$SerieName="")
1429    {
1430     /* Validate the Data and DataDescription array */
1431     $this->validateDataDescription("drawLineGraph",$DataDescription);
1432     $this->validateData("drawLineGraph",$Data);
1433
1434     $GraphID = 0;
1435     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1436      {
1437       $ID = 0;
1438       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1439        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1440
1441       if ( $SerieName == "" || $SerieName == $ColName )
1442        {
1443         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1444         $XLast = -1;
1445         foreach ( $Data as $Key => $Values )
1446          {
1447           if ( isset($Data[$Key][$ColName]))
1448            {
1449             $Value = $Data[$Key][$ColName];
1450             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1451
1452             /* Save point into the image map if option activated */
1453             if ( $this->BuildMap )
1454              $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Line");
1455
1456             if (!is_numeric($Value)) { $XLast = -1; }
1457             if ( $XLast != -1 )
1458              $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1459
1460             $XLast = $XPos;
1461             $YLast = $YPos;
1462             if (!is_numeric($Value)) { $XLast = -1; }
1463            }
1464           $XPos = $XPos + $this->DivisionWidth;
1465          }
1466         $GraphID++;
1467        }
1468      }
1469    }
1470
1471   /* This function draw a line graph */
1472   function drawXYGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0)
1473    {
1474     $YLast = -1; $XLast = -1;
1475     foreach ( $Data as $Key => $Values )
1476      {
1477       if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1478        {
1479         $X = $Data[$Key][$XSerieName];
1480         $Y = $Data[$Key][$YSerieName];
1481
1482         $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1483         $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1484
1485         if ($XLast != -1 && $YLast != -1)
1486          {
1487           $this->drawLine($XLast,$YLast,$X,$Y,$this->Palette[$PaletteID]["R"],$this->Palette[$PaletteID]["G"],$this->Palette[$PaletteID]["B"],TRUE);
1488          }
1489
1490         $XLast = $X;
1491         $YLast = $Y;
1492        }
1493      }
1494    }
1495
1496   /* This function draw a cubic curve */
1497   function drawCubicCurve($Data,$DataDescription,$Accuracy=.1,$SerieName="")
1498    {
1499     /* Validate the Data and DataDescription array */
1500     $this->validateDataDescription("drawCubicCurve",$DataDescription);
1501     $this->validateData("drawCubicCurve",$Data);
1502
1503     $GraphID = 0;
1504     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1505      {
1506       if ( $SerieName == "" || $SerieName == $ColName )
1507        {
1508         $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1509         $XIn[0] = 0; $YIn[0] = 0;
1510
1511         $ID = 0;
1512         foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1513          { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1514
1515         $Index = 1;
1516         $XLast = -1; $Missing = "";
1517         foreach ( $Data as $Key => $Values )
1518          {
1519           if ( isset($Data[$Key][$ColName]) )
1520            {
1521             $Value = $Data[$Key][$ColName];
1522             $XIn[$Index] = $Index;
1523             $YIn[$Index] = $Value;
1524             if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1525             $Index++;
1526            }
1527          }
1528         $Index--;
1529
1530         $Yt[0] = 0;
1531         $Yt[1] = 0;
1532         $U[1]  = 0;
1533         for($i=2;$i<=$Index-1;$i++)
1534          {
1535           $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1536           $p      = $Sig * $Yt[$i-1] + 2;
1537           $Yt[$i] = ($Sig - 1) / $p;
1538           $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1539           $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1540          }
1541
1542         $qn = 0;
1543         $un = 0;
1544         $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1545
1546         for($k=$Index-1;$k>=1;$k--)
1547          $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1548
1549         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1550         for($X=1;$X<=$Index;$X=$X+$Accuracy)
1551          {
1552           $klo = 1;
1553           $khi = $Index;
1554           $k   = $khi - $klo;
1555           while($k > 1)
1556            {
1557             $k = $khi - $klo;
1558             If ( $XIn[$k] >= $X )
1559              $khi = $k;
1560             else
1561              $klo = $k;
1562            }
1563           $klo = $khi - 1;
1564
1565           $h     = $XIn[$khi] - $XIn[$klo];
1566           $a     = ($XIn[$khi] - $X) / $h;
1567           $b     = ($X - $XIn[$klo]) / $h;
1568           $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1569
1570           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1571
1572           if ( $XLast != -1 && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]) )
1573            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1574
1575           $XLast = $XPos;
1576           $YLast = $YPos;
1577           $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
1578          }
1579
1580         // Add potentialy missing values
1581         $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1582         if ( $XPos < ($this->GArea_X2 - $this->GAreaXOffset) )
1583          {
1584           $YPos = $this->GArea_Y2 - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1585           $this->drawLine($XLast,$YLast,$this->GArea_X2-$this->GAreaXOffset,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1586          }
1587
1588         $GraphID++;
1589        }
1590      }
1591    }
1592
1593   /* This function draw a filled cubic curve */
1594   function drawFilledCubicCurve($Data,$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE)
1595    {
1596     /* Validate the Data and DataDescription array */
1597     $this->validateDataDescription("drawFilledCubicCurve",$DataDescription);
1598     $this->validateData("drawFilledCubicCurve",$Data);
1599
1600     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1601     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1602     $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1603     if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1604
1605     $GraphID = 0;
1606     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1607      {
1608       $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1609       $XIn[0] = 0; $YIn[0] = 0;
1610
1611       $ID = 0;
1612       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1613        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1614
1615       $Index = 1;
1616       $XLast = -1; $Missing = "";
1617       foreach ( $Data as $Key => $Values )
1618        {
1619         $Value = $Data[$Key][$ColName];
1620         $XIn[$Index] = $Index;
1621         $YIn[$Index] = $Value;
1622         if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1623         $Index++;
1624        }
1625       $Index--;
1626 
1627       $Yt[0] = 0;
1628       $Yt[1] = 0;
1629       $U[1]  = 0;
1630       for($i=2;$i<=$Index-1;$i++)
1631        {
1632         $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1633         $p      = $Sig * $Yt[$i-1] + 2;
1634         $Yt[$i] = ($Sig - 1) / $p;
1635         $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1636         $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1637        }
1638
1639       $qn = 0;
1640       $un = 0;
1641       $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1642
1643       for($k=$Index-1;$k>=1;$k--)
1644        $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1645
1646       $Points   = "";
1647       $Points[] = $this->GAreaXOffset;
1648       $Points[] = $LayerHeight;
1649
1650       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1651       $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
1652       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1653       imagecolortransparent($this->Layers[0],$C_White);
1654
1655       $YLast = NULL;
1656       $XPos  = $this->GAreaXOffset; $PointsCount = 2;
1657       for($X=1;$X<=$Index;$X=$X+$Accuracy)
1658        {
1659         $klo = 1;
1660         $khi = $Index;
1661         $k   = $khi - $klo;
1662         while($k > 1)
1663          {
1664           $k = $khi - $klo;
1665           If ( $XIn[$k] >= $X )
1666            $khi = $k;
1667           else
1668            $klo = $k;
1669          }
1670         $klo = $khi - 1;
1671
1672         $h     = $XIn[$khi] - $XIn[$klo];
1673         $a     = ($XIn[$khi] - $X) / $h;
1674         $b     = ($X - $XIn[$klo]) / $h;
1675         $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1676
1677         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1678
1679         if ( $YLast != NULL && $AroundZero && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]))
1680          {
1681           $aPoints   = "";
1682           $aPoints[] = $XLast;
1683           $aPoints[] = $YLast;
1684           $aPoints[] = $XPos;
1685           $aPoints[] = $YPos;
1686           $aPoints[] = $XPos;
1687           $aPoints[] = $YZero;
1688           $aPoints[] = $XLast;
1689           $aPoints[] = $YZero;
1690
1691           $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1692           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1693          }
1694
1695         if ( !isset($Missing[floor($X)]) || $YLast == NULL )
1696          {
1697           $PointsCount++;
1698           $Points[] = $XPos;
1699           $Points[] = $YPos;
1700          }
1701         else
1702          {
1703           $PointsCount++; $Points[] = $XLast; $Points[] = $LayerHeight;
1704          }
1705
1706         $YLast = $YPos; $XLast = $XPos;
1707         $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
1708        }
1709
1710       // Add potentialy missing values
1711       $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1712       if ( $XPos < ($LayerWidth-$this->GAreaXOffset) )
1713        {
1714         $YPos = $LayerHeight - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1715
1716         if ( $YLast != NULL && $AroundZero )
1717          {
1718           $aPoints   = "";
1719           $aPoints[] = $XLast;
1720           $aPoints[] = $YLast;
1721           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1722           $aPoints[] = $YPos;
1723           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1724           $aPoints[] = $YZero;
1725           $aPoints[] = $XLast;
1726           $aPoints[] = $YZero;
1727
1728           $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1729           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1730          }
1731
1732         if ( $YIn[$klo] != "" && $YIn[$khi] != "" || $YLast == NULL )
1733          {
1734           $PointsCount++;
1735           $Points[] = $LayerWidth-$this->GAreaXOffset;
1736           $Points[] = $YPos;
1737          }
1738        }
1739
1740       $Points[] = $LayerWidth-$this->GAreaXOffset;
1741       $Points[] = $LayerHeight;
1742
1743       if ( !$AroundZero )
1744        {
1745         $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1746         imagefilledpolygon($this->Layers[0],$Points,$PointsCount,$C_Graph);
1747        }
1748
1749       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1750       imagedestroy($this->Layers[0]);
1751
1752       $this->drawCubicCurve($Data,$DataDescription,$Accuracy,$ColName);
1753
1754       $GraphID++;
1755      }
1756    }
1757
1758   /* This function draw a filled line graph */
1759   function drawFilledLineGraph($Data,$DataDescription,$Alpha=100,$AroundZero=FALSE)
1760    {
1761     $Empty = -2147483647;
1762
1763     /* Validate the Data and DataDescription array */
1764     $this->validateDataDescription("drawFilledLineGraph",$DataDescription);
1765     $this->validateData("drawFilledLineGraph",$Data);
1766
1767     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1768     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1769
1770     $GraphID = 0;
1771     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1772      {
1773       $ID = 0;
1774       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1775        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1776
1777       $aPoints   = "";
1778       $aPoints[] = $this->GAreaXOffset;
1779       $aPoints[] = $LayerHeight;
1780
1781       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1782       $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
1783       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1784       imagecolortransparent($this->Layers[0],$C_White);
1785
1786       $XPos  = $this->GAreaXOffset;
1787       $XLast = -1; $PointsCount = 2;
1788       $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1789       if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1790
1791       $YLast = $Empty;
1792       foreach ( $Data as $Key => $Values )
1793        {
1794         $Value = $Data[$Key][$ColName];
1795         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1796
1797         /* Save point into the image map if option activated */
1798         if ( $this->BuildMap )
1799          $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"FLine");
1800
1801         if ( !is_numeric($Value) )
1802          {
1803           $PointsCount++;
1804           $aPoints[] = $XLast;
1805           $aPoints[] = $LayerHeight;
1806
1807           $YLast = $Empty;
1808          }
1809         else
1810          {
1811           $PointsCount++;
1812           if ( $YLast <> $Empty )
1813            { $aPoints[] = $XPos; $aPoints[] = $YPos; }
1814           else
1815            { $PointsCount++; $aPoints[] = $XPos; $aPoints[] = $LayerHeight; $aPoints[] = $XPos; $aPoints[] = $YPos; }
1816
1817           if ($YLast <> $Empty && $AroundZero)
1818            {
1819             $Points   = "";
1820             $Points[] = $XLast; $Points[] = $YLast;
1821             $Points[] = $XPos;
1822             $Points[] = $YPos;
1823             $Points[] = $XPos;
1824             $Points[] = $YZero;
1825             $Points[] = $XLast;
1826             $Points[] = $YZero;
1827
1828             $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1829             imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1830            }
1831           $YLast = $YPos;
1832          }
1833
1834         $XLast = $XPos;
1835         $XPos  = $XPos + $this->DivisionWidth;
1836        }
1837       $aPoints[] = $LayerWidth - $this->GAreaXOffset;
1838       $aPoints[] = $LayerHeight;
1839
1840       if ( $AroundZero == FALSE )
1841        {
1842         $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1843         imagefilledpolygon($this->Layers[0],$aPoints,$PointsCount,$C_Graph);
1844        }
1845
1846       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1847       imagedestroy($this->Layers[0]);
1848       $GraphID++;
1849       $this->drawLineGraph($Data,$DataDescription,$ColName);
1850      }
1851    }
1852
1853   /* This function draw a bar graph */
1854   function drawOverlayBarGraph($Data,$DataDescription,$Alpha=50)
1855    {
1856     /* Validate the Data and DataDescription array */
1857     $this->validateDataDescription("drawOverlayBarGraph",$DataDescription);
1858     $this->validateData("drawOverlayBarGraph",$Data);
1859
1860     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1861     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1862
1863     $GraphID = 0;
1864     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1865      {
1866       $ID = 0;
1867       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1868        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1869
1870       $this->Layers[$GraphID] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1871       $C_White                = $this->AllocateColor($this->Layers[$GraphID],255,255,255);
1872       $C_Graph                = $this->AllocateColor($this->Layers[$GraphID],$this->Palette[$GraphID]["R"],$this->Palette[$GraphID]["G"],$this->Palette[$GraphID]["B"]);
1873       imagefilledrectangle($this->Layers[$GraphID],0,0,$LayerWidth,$LayerHeight,$C_White);
1874       imagecolortransparent($this->Layers[$GraphID],$C_White);
1875
1876       $XWidth = $this->DivisionWidth / 4;
1877       $XPos   = $this->GAreaXOffset;
1878       $YZero  = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1879       $XLast  = -1; $PointsCount = 2;
1880       foreach ( $Data as $Key => $Values )
1881        {
1882         if ( isset($Data[$Key][$ColName]) )
1883          {
1884           $Value = $Data[$Key][$ColName];
1885           if ( is_numeric($Value) )
1886            {
1887             $YPos  = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1888
1889             imagefilledrectangle($this->Layers[$GraphID],$XPos-$XWidth,$YPos,$XPos+$XWidth,$YZero,$C_Graph);
1890
1891             $X1 = floor($XPos - $XWidth + $this->GArea_X1); $Y1 = floor($YPos+$this->GArea_Y1) + .2;
1892             $X2 = floor($XPos + $XWidth + $this->GArea_X1); $Y2 = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1893             if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
1894             if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
1895
1896             /* Save point into the image map if option activated */
1897             if ( $this->BuildMap )
1898              $this->addToImageMap($X1,min($Y1,$Y2),$X2,max($Y1,$Y2),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"oBar");
1899
1900             $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1901            }
1902          }
1903         $XPos = $XPos + $this->DivisionWidth;
1904        }
1905
1906       $GraphID++;
1907      }
1908
1909     for($i=0;$i<=($GraphID-1);$i++)
1910      {
1911       imagecopymerge($this->Picture,$this->Layers[$i],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1912       imagedestroy($this->Layers[$i]);
1913      }
1914    }
1915
1916   /* This function draw a bar graph */
1917   function drawBarGraph($Data,$DataDescription,$Shadow=FALSE,$Alpha=100)
1918    {
1919     /* Validate the Data and DataDescription array */
1920     $this->validateDataDescription("drawBarGraph",$DataDescription);
1921     $this->validateData("drawBarGraph",$Data);
1922
1923     $GraphID      = 0;
1924     $Series       = count($DataDescription["Values"]);
1925     $SeriesWidth  = $this->DivisionWidth / ($Series+1);
1926     $SerieXOffset = $this->DivisionWidth / 2 - $SeriesWidth / 2;
1927
1928     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1929     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
1930
1931     $SerieID = 0;
1932     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1933      {
1934       $ID = 0;
1935       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1936        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1937
1938       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SerieXOffset + $SeriesWidth * $SerieID;
1939       $XLast = -1;
1940       foreach ( $Data as $Key => $Values )
1941        {
1942         if ( isset($Data[$Key][$ColName]))
1943          {
1944           if ( is_numeric($Data[$Key][$ColName]) )
1945            {
1946             $Value = $Data[$Key][$ColName];
1947             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1948
1949             /* Save point into the image map if option activated */
1950             if ( $this->BuildMap )
1951              {
1952               $this->addToImageMap($XPos+1,min($YZero,$YPos),$XPos+$SeriesWidth-1,max($YZero,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Bar");
1953              }
1954           
1955             if ( $Shadow && $Alpha == 100 )
1956              $this->drawRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,25,25,25,TRUE,$Alpha);
1957
1958             $this->drawFilledRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
1959            }
1960          }
1961         $XPos = $XPos + $this->DivisionWidth;
1962        }
1963       $SerieID++;
1964      }
1965    }
1966
1967   /* This function draw a stacked bar graph */
1968   function drawStackedBarGraph($Data,$DataDescription,$Alpha=50,$Contiguous=FALSE)
1969    {
1970     /* Validate the Data and DataDescription array */
1971     $this->validateDataDescription("drawBarGraph",$DataDescription);
1972     $this->validateData("drawBarGraph",$Data);
1973
1974     $GraphID      = 0;
1975     $Series       = count($DataDescription["Values"]);
1976     if ( $Contiguous )
1977      $SeriesWidth  = $this->DivisionWidth;
1978     else
1979      $SeriesWidth  = $this->DivisionWidth * .8;
1980
1981     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1982     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
1983
1984     $SerieID = 0; $LastValue = "";
1985     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1986      {
1987       $ID = 0;
1988       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1989        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1990
1991       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SeriesWidth / 2;
1992       $XLast = -1;
1993       foreach ( $Data as $Key => $Values )
1994        {
1995         if ( isset($Data[$Key][$ColName]))
1996          {
1997           if ( is_numeric($Data[$Key][$ColName]) )
1998            {
1999             $Value = $Data[$Key][$ColName];
2000
2001             if ( isset($LastValue[$Key]) )
2002              {
2003               $YPos    = $this->GArea_Y2 - ((($Value+$LastValue[$Key])-$this->VMin) * $this->DivisionRatio);
2004               $YBottom = $this->GArea_Y2 - (($LastValue[$Key]-$this->VMin) * $this->DivisionRatio);
2005               $LastValue[$Key] += $Value;
2006              }
2007             else
2008              {
2009               $YPos    = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
2010               $YBottom = $YZero;
2011               $LastValue[$Key] = $Value;
2012              }
2013
2014             /* Save point into the image map if option activated */
2015             if ( $this->BuildMap )
2016              $this->addToImageMap($XPos+1,min($YBottom,$YPos),$XPos+$SeriesWidth-1,max($YBottom,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"sBar");
2017
2018             $this->drawFilledRectangle($XPos+1,$YBottom,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
2019            }
2020          }
2021         $XPos = $XPos + $this->DivisionWidth;
2022        }
2023       $SerieID++;
2024      }
2025    }
2026
2027   /* This function draw a limits bar graphs */
2028   function drawLimitsGraph($Data,$DataDescription,$R=0,$G=0,$B=0)
2029    {
2030     /* Validate the Data and DataDescription array */
2031     $this->validateDataDescription("drawLimitsGraph",$DataDescription);
2032     $this->validateData("drawLimitsGraph",$Data);
2033
2034     $XWidth = $this->DivisionWidth / 4;
2035     $XPos   = $this->GArea_X1 + $this->GAreaXOffset;
2036
2037     foreach ( $Data as $Key => $Values )
2038      {
2039       $Min     = $Data[$Key][$DataDescription["Values"][0]];
2040       $Max     = $Data[$Key][$DataDescription["Values"][0]];
2041       $GraphID = 0; $MaxID = 0; $MinID = 0;
2042       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2043        {
2044         if ( isset($Data[$Key][$ColName]) )
2045          {
2046           if ( $Data[$Key][$ColName] > $Max && is_numeric($Data[$Key][$ColName]))
2047            { $Max = $Data[$Key][$ColName]; $MaxID = $GraphID; }
2048          }
2049         if ( isset($Data[$Key][$ColName]) && is_numeric($Data[$Key][$ColName]))
2050          {
2051           if ( $Data[$Key][$ColName] < $Min )
2052            { $Min = $Data[$Key][$ColName]; $MinID = $GraphID; }
2053           $GraphID++;
2054          }
2055        }
2056
2057       $YPos = $this->GArea_Y2 - (($Max-$this->VMin) * $this->DivisionRatio);
2058       $X1 = floor($XPos - $XWidth); $Y1 = floor($YPos) - .2;
2059       $X2 = floor($XPos + $XWidth);
2060       if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
2061       if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
2062
2063       $YPos = $this->GArea_Y2 - (($Min-$this->VMin) * $this->DivisionRatio);
2064       $Y2 = floor($YPos) + .2;
2065
2066       $this->drawLine(floor($XPos)-.2,$Y1+1,floor($XPos)-.2,$Y2-1,$R,$G,$B,TRUE);
2067       $this->drawLine(floor($XPos)+.2,$Y1+1,floor($XPos)+.2,$Y2-1,$R,$G,$B,TRUE);
2068       $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$MaxID]["R"],$this->Palette[$MaxID]["G"],$this->Palette[$MaxID]["B"],FALSE);
2069       $this->drawLine($X1,$Y2,$X2,$Y2,$this->Palette[$MinID]["R"],$this->Palette[$MinID]["G"],$this->Palette[$MinID]["B"],FALSE);
2070
2071       $XPos = $XPos + $this->DivisionWidth;
2072      }
2073    }
2074
2075   /* This function draw radar axis centered on the graph area */
2076   function drawRadarAxis($Data,$DataDescription,$Mosaic=TRUE,$BorderOffset=10,$A_R=60,$A_G=60,$A_B=60,$S_R=200,$S_G=200,$S_B=200,$MaxValue=-1)
2077    {
2078     /* Validate the Data and DataDescription array */
2079     $this->validateDataDescription("drawRadarAxis",$DataDescription);
2080     $this->validateData("drawRadarAxis",$Data);
2081
2082     $C_TextColor = $this->AllocateColor($this->Picture,$A_R,$A_G,$A_B);
2083
2084     /* Draw radar axis */
2085     $Points  = count($Data);
2086     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2087     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2088     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2089
2090     /* Search for the max value */
2091     if ( $MaxValue == -1 )
2092      {
2093       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2094        {
2095         foreach ( $Data as $Key => $Values )
2096          {
2097           if ( isset($Data[$Key][$ColName]))
2098            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2099          }
2100        }
2101      }
2102
2103     /* Draw the mosaic */
2104     if ( $Mosaic )
2105      {
2106       $RadiusScale = $Radius / $MaxValue;
2107       for ( $t=1; $t<=$MaxValue-1; $t++)
2108        {
2109         $TRadius  = $RadiusScale * $t;
2110         $LastX1   = -1;
2111
2112         for ( $i=0; $i<=$Points; $i++)
2113          {
2114           $Angle = -90 + $i * 360/$Points;
2115           $X1 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2116           $Y1 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2117           $X2 = cos($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $XCenter;
2118           $Y2 = sin($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $YCenter;
2119
2120           if ( $t % 2 == 1 && $LastX1 != -1)
2121            {
2122             $Plots   = "";
2123             $Plots[] = $X1; $Plots[] = $Y1;
2124             $Plots[] = $X2; $Plots[] = $Y2;
2125             $Plots[] = $LastX2; $Plots[] = $LastY2;
2126             $Plots[] = $LastX1; $Plots[] = $LastY1;
2127
2128             $C_Graph = $this->AllocateColor($this->Picture,250,250,250);
2129             imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_Graph);
2130            }
2131
2132           $LastX1 = $X1; $LastY1= $Y1;
2133           $LastX2 = $X2; $LastY2= $Y2;
2134          }
2135        }
2136      }
2137
2138
2139     /* Draw the spider web */
2140     for ( $t=1; $t<=$MaxValue; $t++)
2141      {
2142       $TRadius = ( $Radius / $MaxValue ) * $t;
2143       $LastX   = -1;
2144
2145       for ( $i=0; $i<=$Points; $i++)
2146        {
2147         $Angle = -90 + $i * 360/$Points;
2148         $X = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2149         $Y = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2150
2151         if ( $LastX != -1 )
2152          $this->drawDottedLine($LastX,$LastY,$X,$Y,4,$S_R,$S_G,$S_B);
2153
2154         $LastX = $X; $LastY= $Y;
2155        }
2156      }
2157
2158     /* Draw the axis */
2159     for ( $i=0; $i<=$Points; $i++)
2160      {
2161       $Angle = -90 + $i * 360/$Points;
2162       $X = cos($Angle * 3.1418 / 180 ) * $Radius + $XCenter;
2163       $Y = sin($Angle * 3.1418 / 180 ) * $Radius + $YCenter;
2164
2165       $this->drawLine($XCenter,$YCenter,$X,$Y,$A_R,$A_G,$A_B);
2166
2167       $XOffset = 0; $YOffset = 0;
2168       if (isset($Data[$i][$DataDescription["Position"]]))
2169        {
2170         $Label = $Data[$i][$DataDescription["Position"]];
2171
2172         $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Label);
2173         $Width  = $Positions[2] - $Positions[6];
2174         $Height = $Positions[3] - $Positions[7];
2175
2176         if ( $Angle >= 0 && $Angle <= 90 )
2177          $YOffset = $Height;
2178
2179         if ( $Angle > 90 && $Angle <= 180 )
2180          { $YOffset = $Height; $XOffset = -$Width; }
2181
2182         if ( $Angle > 180 && $Angle <= 270 )
2183          { $XOffset = -$Width; }
2184
2185         imagettftext($this->Picture,$this->FontSize,0,$X+$XOffset,$Y+$YOffset,$C_TextColor,$this->FontName,$Label);
2186        }
2187      }
2188
2189     /* Write the values */
2190     for ( $t=1; $t<=$MaxValue; $t++)
2191      {
2192       $TRadius = ( $Radius / $MaxValue ) * $t;
2193
2194       $Angle = -90 + 360 / $Points;
2195       $X1 = $XCenter;
2196       $Y1 = $YCenter - $TRadius;
2197       $X2 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2198       $Y2 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2199
2200       $XPos = floor(($X2-$X1)/2) + $X1;
2201       $YPos = floor(($Y2-$Y1)/2) + $Y1;
2202
2203       $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$t);
2204       $X = $XPos - ( $X+$Positions[2] - $X+$Positions[6] ) / 2;
2205       $Y = $YPos + $this->FontSize;
2206
2207       $this->drawFilledRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,240,240,240);
2208       $this->drawRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,220,220,220);
2209       imagettftext($this->Picture,$this->FontSize,0,$X,$Y,$C_TextColor,$this->FontName,$t);
2210      }
2211    }
2212
2213   /* This function draw a radar graph centered on the graph area */
2214   function drawRadar($Data,$DataDescription,$BorderOffset=10,$MaxValue=-1)
2215    {
2216     /* Validate the Data and DataDescription array */
2217     $this->validateDataDescription("drawRadar",$DataDescription);
2218     $this->validateData("drawRadar",$Data);
2219
2220     $Points  = count($Data);
2221     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2222     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2223     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2224
2225     /* Search for the max value */
2226     if ( $MaxValue == -1 )
2227      {
2228       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2229        {
2230         foreach ( $Data as $Key => $Values )
2231          {
2232           if ( isset($Data[$Key][$ColName]))
2233            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2234          }
2235        }
2236      }
2237
2238     $GraphID = 0;
2239     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2240      {
2241       $ID = 0;
2242       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2243        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2244
2245       $Angle = -90;
2246       $XLast = -1;
2247       foreach ( $Data as $Key => $Values )
2248        {
2249         if ( isset($Data[$Key][$ColName]))
2250          {
2251           $Value    = $Data[$Key][$ColName];
2252           $Strength = ( $Radius / $MaxValue ) * $Value;
2253
2254           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2255           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2256
2257           if ( $XLast != -1 )
2258            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2259
2260           if ( $XLast == -1 )
2261            { $FirstX = $XPos; $FirstY = $YPos; }
2262
2263           $Angle = $Angle + (360/$Points);
2264           $XLast = $XPos;
2265           $YLast = $YPos;
2266          }
2267        }
2268       $this->drawLine($XPos,$YPos,$FirstX,$FirstY,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2269       $GraphID++;
2270      }
2271    }
2272
2273   /* This function draw a radar graph centered on the graph area */
2274   function drawFilledRadar($Data,$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1)
2275    {
2276     /* Validate the Data and DataDescription array */
2277     $this->validateDataDescription("drawFilledRadar",$DataDescription);
2278     $this->validateData("drawFilledRadar",$Data);
2279
2280     $Points      = count($Data);
2281     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
2282     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
2283     $Radius      = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2284     $XCenter     = ( $this->GArea_X2 - $this->GArea_X1 ) / 2;
2285     $YCenter     = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2;
2286
2287     /* Search for the max value */
2288     if ( $MaxValue == -1 )
2289      {
2290       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2291        {
2292         foreach ( $Data as $Key => $Values )
2293          {
2294           if ( isset($Data[$Key][$ColName]))
2295            if ( $Data[$Key][$ColName] > $MaxValue && is_numeric($Data[$Key][$ColName])) { $MaxValue = $Data[$Key][$ColName]; }
2296          }
2297        }
2298      }
2299
2300     $GraphID = 0;
2301     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2302      {
2303       $ID = 0;
2304       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2305        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2306
2307       $Angle = -90;
2308       $XLast = -1;
2309       $Plots = "";
2310       foreach ( $Data as $Key => $Values )
2311        {
2312         if ( isset($Data[$Key][$ColName]))
2313          {
2314           $Value    = $Data[$Key][$ColName];
2315           if ( !is_numeric($Value) ) { $Value = 0; }
2316           $Strength = ( $Radius / $MaxValue ) * $Value;
2317
2318           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2319           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2320
2321           $Plots[] = $XPos;
2322           $Plots[] = $YPos;
2323
2324           $Angle = $Angle + (360/$Points);
2325           $XLast = $XPos;
2326           $YLast = $YPos;
2327          }
2328        }
2329
2330       if (isset($Plots[0]))
2331        {
2332         $Plots[] = $Plots[0];
2333         $Plots[] = $Plots[1];
2334
2335         $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
2336         $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
2337         imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
2338         imagecolortransparent($this->Layers[0],$C_White);
2339
2340         $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2341         imagefilledpolygon($this->Layers[0],$Plots,(count($Plots)+1)/2,$C_Graph);
2342
2343         imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
2344         imagedestroy($this->Layers[0]);
2345
2346         for($i=0;$i<=count($Plots)-4;$i=$i+2)
2347          $this->drawLine($Plots[$i]+$this->GArea_X1,$Plots[$i+1]+$this->GArea_Y1,$Plots[$i+2]+$this->GArea_X1,$Plots[$i+3]+$this->GArea_Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2348        }
2349
2350       $GraphID++;
2351      }
2352    }
2353
2354   /* This function draw a flat pie chart */
2355   function drawBasicPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0)
2356    {
2357     /* Validate the Data and DataDescription array */
2358     $this->validateDataDescription("drawBasicPieGraph",$DataDescription,FALSE);
2359     $this->validateData("drawBasicPieGraph",$Data);
2360
2361     /* Determine pie sum */
2362     $Series = 0; $PieSum = 0;
2363     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2364      {
2365       if ( $ColName != $DataDescription["Position"] )
2366        {
2367         $Series++;
2368         foreach ( $Data as $Key => $Values )
2369          {
2370           if ( isset($Data[$Key][$ColName]))
2371            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2372          }
2373        }
2374      }
2375
2376     /* Validate serie */
2377     if ( $Series != 1 )
2378      RaiseFatal("Pie chart can only accept one serie of data.");
2379
2380     $SpliceRatio         = 360 / $PieSum;
2381     $SplicePercent       = 100 / $PieSum;
2382
2383     /* Calculate all polygons */
2384     $Angle    = 0; $TopPlots = "";
2385     foreach($iValues as $Key => $Value)
2386      {
2387       $TopPlots[$Key][] = $XPos;
2388       $TopPlots[$Key][] = $YPos;
2389
2390       /* Process labels position & size */
2391       $Caption = "";
2392       if ( !($DrawLabels == PIE_NOLABEL) )
2393        {
2394         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2395         if ($DrawLabels == PIE_PERCENTAGE)
2396          $Caption  = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2397         elseif ($DrawLabels == PIE_LABELS)
2398          $Caption  = $iLabels[$Key];
2399         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2400          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2401         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2402          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2403
2404         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2405         $TextWidth  = $Position[2]-$Position[0];
2406         $TextHeight = abs($Position[1])+abs($Position[3]);
2407
2408         $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $XPos;
2409
2410         if ( $TAngle > 0 && $TAngle < 180 )
2411          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $YPos + 4;
2412         else
2413          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+4) + $YPos - ($TextHeight/2);
2414
2415         if ( $TAngle > 90 && $TAngle < 270 )
2416          $TX = $TX - $TextWidth;
2417
2418         $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2419         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2420        }
2421
2422       /* Process pie slices */
2423       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2424        {
2425         $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
2426         $TopY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos;
2427
2428         $TopPlots[$Key][] = $TopX;
2429         $TopPlots[$Key][] = $TopY;
2430        }
2431
2432       $TopPlots[$Key][] = $XPos;
2433       $TopPlots[$Key][] = $YPos;
2434
2435       $Angle = $iAngle;
2436      }
2437     $PolyPlots = $TopPlots;
2438
2439     /* Set array values type to float --- PHP Bug with imagefilledpolygon casting to integer */
2440     foreach ($TopPlots as $Key => $Value)
2441      { foreach ($TopPlots[$Key] as $Key2 => $Value2) { settype($TopPlots[$Key][$Key2],"float"); } }
2442
2443     /* Draw Top polygons */
2444     foreach ($PolyPlots as $Key => $Value)
2445      {
2446       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2447       imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
2448      }
2449
2450     $this->drawCircle($XPos-.5,$YPos-.5,$Radius,$R,$G,$B);
2451     $this->drawCircle($XPos-.5,$YPos-.5,$Radius+.5,$R,$G,$B);
2452
2453     /* Draw Top polygons */
2454     foreach ($TopPlots as $Key => $Value)
2455      {
2456       for($j=0;$j<=count($TopPlots[$Key])-4;$j=$j+2)
2457        $this->drawLine($TopPlots[$Key][$j],$TopPlots[$Key][$j+1],$TopPlots[$Key][$j+2],$TopPlots[$Key][$j+3],$R,$G,$B);
2458      }
2459    }
2460
2461   function drawFlatPieGraphWithShadow($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0)
2462    {
2463     $this->drawFlatPieGraph($Data,$DataDescription,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$Radius,PIE_NOLABEL,$SpliceDistance,$Decimals,TRUE);
2464     $this->drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius,$DrawLabels,$SpliceDistance,$Decimals,FALSE);
2465    }
2466
2467   /* This function draw a flat pie chart */
2468   function drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0,$AllBlack=FALSE)
2469    {
2470     /* Validate the Data and DataDescription array */
2471     $this->validateDataDescription("drawFlatPieGraph",$DataDescription,FALSE);
2472     $this->validateData("drawFlatPieGraph",$Data);
2473
2474     $ShadowStatus = $this->ShadowActive ; $this->ShadowActive = FALSE;
2475
2476     /* Determine pie sum */
2477     $Series = 0; $PieSum = 0;
2478     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2479      {
2480       if ( $ColName != $DataDescription["Position"] )
2481        {
2482         $Series++;
2483         foreach ( $Data as $Key => $Values )
2484          {
2485           if ( isset($Data[$Key][$ColName]))
2486            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2487          }
2488        }
2489      }
2490
2491     /* Validate serie */
2492     if ( $Series != 1 )
2493      {
2494       RaiseFatal("Pie chart can only accept one serie of data.");
2495       return(0);
2496      }
2497
2498     $SpliceRatio   = 360 / $PieSum;
2499     $SplicePercent = 100 / $PieSum;
2500
2501     /* Calculate all polygons */
2502     $Angle = 0; $TopPlots = "";
2503     foreach($iValues as $Key => $Value)
2504      {
2505       $XOffset = cos(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2506       $YOffset = sin(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2507
2508       $TopPlots[$Key][] = round($XPos + $XOffset);
2509       $TopPlots[$Key][] = round($YPos + $YOffset);
2510
2511       if ( $AllBlack )
2512        { $Rc = $this->ShadowRColor; $Gc = $this->ShadowGColor; $Bc = $this->ShadowBColor; }
2513       else
2514        { $Rc = $this->Palette[$Key]["R"]; $Gc = $this->Palette[$Key]["G"]; $Bc = $this->Palette[$Key]["B"]; }
2515
2516       $XLineLast = ""; $YLineLast = "";
2517
2518       /* Process labels position & size */
2519       $Caption = "";
2520       if ( !($DrawLabels == PIE_NOLABEL) )
2521        {
2522         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2523         if ($DrawLabels == PIE_PERCENTAGE)
2524          $Caption  = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2525         elseif ($DrawLabels == PIE_LABELS)
2526          $Caption  = $iLabels[$Key];
2527         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2528          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2529         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2530          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2531
2532         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2533         $TextWidth  = $Position[2]-$Position[0];
2534         $TextHeight = abs($Position[1])+abs($Position[3]);
2535
2536         $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $XPos;
2537
2538         if ( $TAngle > 0 && $TAngle < 180 )
2539          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $YPos + 4;
2540         else
2541          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+$SpliceDistance+4) + $YPos - ($TextHeight/2);
2542
2543         if ( $TAngle > 90 && $TAngle < 270 )
2544          $TX = $TX - $TextWidth;
2545
2546         $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2547         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2548        }
2549
2550       /* Process pie slices */
2551       if ( !$AllBlack )
2552        $LineColor = $this->AllocateColor($this->Picture,$Rc,$Gc,$Bc);
2553       else
2554        $LineColor = $this->AllocateColor($this->Picture,$Rc,$Gc,$Bc);
2555
2556       $XLineLast = ""; $YLineLast = "";
2557       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2558        {
2559         $PosX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos + $XOffset;
2560         $PosY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos + $YOffset;
2561
2562         $TopPlots[$Key][] = round($PosX); $TopPlots[$Key][] = round($PosY);
2563
2564         if ( $iAngle == $Angle || $iAngle == $Angle+$Value*$SpliceRatio || $iAngle +.5 > $Angle+$Value*$SpliceRatio)
2565          $this->drawLine($XPos+$XOffset,$YPos+$YOffset,$PosX,$PosY,$Rc,$Gc,$Bc);
2566
2567         if ( $XLineLast != "" )
2568          $this->drawLine($XLineLast,$YLineLast,$PosX,$PosY,$Rc,$Gc,$Bc);
2569
2570         $XLineLast = $PosX; $YLineLast = $PosY;
2571        }
2572
2573       $TopPlots[$Key][] = round($XPos + $XOffset);  $TopPlots[$Key][] = round($YPos + $YOffset);
2574
2575       $Angle = $iAngle;
2576      }
2577     $PolyPlots = $TopPlots;
2578
2579     /* Draw Top polygons */
2580     foreach ($PolyPlots as $Key => $Value)
2581      {
2582       if ( !$AllBlack )
2583        $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2584       else
2585        $C_GraphLo = $this->AllocateColor($this->Picture,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor);
2586
2587       imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
2588      }
2589     $this->ShadowActive = $ShadowStatus;
2590    }
2591
2592   /* This function draw a pseudo-3D pie chart */
2593   function drawPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$EnhanceColors=TRUE,$Skew=60,$SpliceHeight=20,$SpliceDistance=0,$Decimals=0)
2594    {
2595     /* Validate the Data and DataDescription array */
2596     $this->validateDataDescription("drawPieGraph",$DataDescription,FALSE);
2597     $this->validateData("drawPieGraph",$Data);
2598
2599     /* Determine pie sum */
2600     $Series = 0; $PieSum = 0; $rPieSum = 0;
2601     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2602      {
2603       if ( $ColName != $DataDescription["Position"] )
2604        {
2605         $Series++;
2606         foreach ( $Data as $Key => $Values )
2607          if ( isset($Data[$Key][$ColName]))
2608           {
2609            if ( $Data[$Key][$ColName] == 0 )
2610             { $iValues[] = 0; $rValues[] = 0; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; }
2611              // Removed : $PieSum++; $rValues[] = 1;
2612            else
2613             { $PieSum += $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; $rValues[] = $Data[$Key][$ColName]; $rPieSum += $Data[$Key][$ColName];}
2614           }
2615        }
2616      }
2617
2618     /* Validate serie */
2619     if ( $Series != 1 )
2620      RaiseFatal("Pie chart can only accept one serie of data.");
2621
2622     $SpliceDistanceRatio = $SpliceDistance;
2623     $SkewHeight          = ($Radius * $Skew) / 100;
2624     $SpliceRatio         = (360 - $SpliceDistanceRatio * count($iValues) ) / $PieSum;
2625     $SplicePercent       = 100 / $PieSum;
2626     $rSplicePercent      = 100 / $rPieSum;
2627
2628     /* Calculate all polygons */
2629     $Angle    = 0; $CDev = 5;
2630     $TopPlots = ""; $BotPlots = "";
2631     $aTopPlots = ""; $aBotPlots = "";
2632     foreach($iValues as $Key => $Value)
2633      {
2634       $XCenterPos = cos(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos;
2635       $YCenterPos = sin(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos;
2636       $XCenterPos2 = cos(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos;
2637       $YCenterPos2 = sin(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos;
2638
2639       $TopPlots[$Key][] = round($XCenterPos); $BotPlots[$Key][] = round($XCenterPos);
2640       $TopPlots[$Key][] = round($YCenterPos); $BotPlots[$Key][] = round($YCenterPos + $SpliceHeight);
2641       $aTopPlots[$Key][] = $XCenterPos; $aBotPlots[$Key][] = $XCenterPos;
2642       $aTopPlots[$Key][] = $YCenterPos; $aBotPlots[$Key][] = $YCenterPos + $SpliceHeight;
2643
2644       /* Process labels position & size */
2645       $Caption = "";
2646       if ( !($DrawLabels == PIE_NOLABEL) )
2647        {
2648         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2649         if ($DrawLabels == PIE_PERCENTAGE)
2650          $Caption  = (round($rValues[$Key] * pow(10,$Decimals) * $rSplicePercent)/pow(10,$Decimals))."%";
2651         elseif ($DrawLabels == PIE_LABELS)
2652          $Caption  = $iLabels[$Key];
2653         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2654          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2655
2656         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2657         $TextWidth  = $Position[2]-$Position[0];
2658         $TextHeight = abs($Position[1])+abs($Position[3]);
2659
2660         $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius + 10)+ $XPos;
2661
2662         if ( $TAngle > 0 && $TAngle < 180 )
2663          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 10) + $YPos + $SpliceHeight + 4;
2664         else
2665          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 4) + $YPos - ($TextHeight/2);
2666
2667         if ( $TAngle > 90 && $TAngle < 270 )
2668          $TX = $TX - $TextWidth;
2669
2670         $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2671         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2672        }
2673
2674       /* Process pie slices */
2675       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2676        {
2677         $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
2678         $TopY = sin($iAngle * 3.1418 / 180 ) * $SkewHeight + $YPos;
2679
2680         $TopPlots[$Key][] = round($TopX); $BotPlots[$Key][] = round($TopX);
2681         $TopPlots[$Key][] = round($TopY); $BotPlots[$Key][] = round($TopY + $SpliceHeight);
2682         $aTopPlots[$Key][] = $TopX; $aBotPlots[$Key][] = $TopX;
2683         $aTopPlots[$Key][] = $TopY; $aBotPlots[$Key][] = $TopY + $SpliceHeight;
2684        }
2685
2686       $TopPlots[$Key][] = round($XCenterPos2); $BotPlots[$Key][] = round($XCenterPos2);
2687       $TopPlots[$Key][] = round($YCenterPos2); $BotPlots[$Key][] = round($YCenterPos2 + $SpliceHeight);
2688       $aTopPlots[$Key][] = $XCenterPos2; $aBotPlots[$Key][] = $XCenterPos2;
2689       $aTopPlots[$Key][] = $YCenterPos2; $aBotPlots[$Key][] = $YCenterPos2 + $SpliceHeight;
2690
2691       $Angle = $iAngle + $SpliceDistanceRatio;
2692      }
2693
2694     /* Draw Bottom polygons */
2695     foreach($iValues as $Key => $Value)
2696      {
2697       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-20);
2698       imagefilledpolygon($this->Picture,$BotPlots[$Key],(count($BotPlots[$Key])+1)/2,$C_GraphLo);
2699
2700       if ( $EnhanceColors ) { $En = -10; } else { $En = 0; }
2701 
2702       for($j=0;$j<=count($aBotPlots[$Key])-4;$j=$j+2)
2703        $this->drawLine($aBotPlots[$Key][$j],$aBotPlots[$Key][$j+1],$aBotPlots[$Key][$j+2],$aBotPlots[$Key][$j+3],$this->Palette[$Key]["R"]+$En,$this->Palette[$Key]["G"]+$En,$this->Palette[$Key]["B"]+$En);
2704      }
2705
2706     /* Draw pie layers */
2707     if ( $EnhanceColors ) { $ColorRatio = 30 / $SpliceHeight; } else { $ColorRatio = 25 / $SpliceHeight; }
2708     for($i=$SpliceHeight-1;$i>=1;$i--)
2709      {
2710       foreach($iValues as $Key => $Value)
2711        {
2712         $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-10);
2713         $Plots = ""; $Plot = 0;
2714         foreach($TopPlots[$Key] as $Key2 => $Value2)
2715          {
2716           $Plot++;
2717           if ( $Plot % 2 == 1 )
2718            $Plots[] = $Value2;
2719           else
2720            $Plots[] = $Value2+$i;
2721          }
2722         imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_GraphLo);
2723
2724         $Index       = count($Plots);
2725         if ($EnhanceColors ) {$ColorFactor = -20 + ($SpliceHeight - $i) * $ColorRatio; } else { $ColorFactor = 0; }
2726
2727         $this->drawAntialiasPixel($Plots[0],$Plots[1],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2728         $this->drawAntialiasPixel($Plots[2],$Plots[3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2729         $this->drawAntialiasPixel($Plots[$Index-4],$Plots[$Index-3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2730        }
2731      }
2732
2733     /* Draw Top polygons */
2734     for($Key=count($iValues)-1;$Key>=0;$Key--)
2735      {
2736       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2737       imagefilledpolygon($this->Picture,$TopPlots[$Key],(count($TopPlots[$Key])+1)/2,$C_GraphLo);
2738
2739       if ( $EnhanceColors ) { $En = 10; } else { $En = 0; }
2740       for($j=0;$j<=count($aTopPlots[$Key])-4;$j=$j+2)
2741        $this->drawLine($aTopPlots[$Key][$j],$aTopPlots[$Key][$j+1],$aTopPlots[$Key][$j+2],$aTopPlots[$Key][$j+3],$this->Palette[$Key]["R"]+$En,$this->Palette[$Key]["G"]+$En,$this->Palette[$Key]["B"]+$En);
2742      }
2743    }
2744
2745   /* This function can be used to set the background color */
2746   function drawBackground($R,$G,$B)
2747    {
2748     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2749     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2750     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2751
2752     $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B);
2753     imagefilledrectangle($this->Picture,0,0,$this->XSize,$this->YSize,$C_Background);
2754    }
2755
2756   /* This function can be used to set the background color */
2757   function drawGraphAreaGradient($R,$G,$B,$Decay,$Target=TARGET_GRAPHAREA)
2758    {
2759     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2760     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2761     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2762
2763     if ( $Target == TARGET_GRAPHAREA )  { $X1 = $this->GArea_X1+1; $X2 = $this->GArea_X2-1; $Y1 = $this->GArea_Y1+1; $Y2 = $this->GArea_Y2; }
2764     if ( $Target == TARGET_BACKGROUND ) { $X1 = 0; $X2 = $this->XSize; $Y1 = 0; $Y2 = $this->YSize; }
2765
2766     /* Positive gradient */
2767     if ( $Decay > 0 )
2768      {
2769       $YStep = ($Y2 - $Y1 - 2) / $Decay;
2770       for($i=0;$i<=$Decay;$i++)
2771        {
2772         $R-=1;$G-=1;$B-=1;
2773         $Yi1 = $Y1 + ( $i * $YStep );
2774         $Yi2 = ceil( $Yi1 + ( $i * $YStep ) + $YStep );
2775         if ( $Yi2 >= $Yi2 ) { $Yi2 = $Y2-1; }
2776
2777         $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B);
2778         imagefilledrectangle($this->Picture,$X1,$Yi1,$X2,$Yi2,$C_Background);
2779        }
2780      }
2781
2782     /* Negative gradient */
2783     if ( $Decay < 0 )
2784      {
2785       $YStep = ($Y2 - $Y1 - 2) / -$Decay;
2786       $Yi1   = $Y1; $Yi2   = $Y1+$YStep;
2787       for($i=-$Decay;$i>=0;$i--)
2788        {
2789         $R+=1;$G+=1;$B+=1;
2790         $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B);
2791         imagefilledrectangle($this->Picture,$X1,$Yi1,$X2,$Yi2,$C_Background);
2792
2793         $Yi1+= $YStep;
2794         $Yi2+= $YStep;
2795         if ( $Yi2 >= $Yi2 ) { $Yi2 = $Y2-1; }
2796        }
2797      }
2798    }
2799
2800   /* This function create a rectangle with antialias */
2801   function drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B)
2802    {
2803     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2804     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2805     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2806
2807     $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2808
2809     $X1=$X1-.2;$Y1=$Y1-.2;
2810     $X2=$X2+.2;$Y2=$Y2+.2;
2811     $this->drawLine($X1,$Y1,$X2,$Y1,$R,$G,$B);
2812     $this->drawLine($X2,$Y1,$X2,$Y2,$R,$G,$B);
2813     $this->drawLine($X2,$Y2,$X1,$Y2,$R,$G,$B);
2814     $this->drawLine($X1,$Y2,$X1,$Y1,$R,$G,$B);
2815    }
2816
2817   /* This function create a filled rectangle with antialias */
2818   function drawFilledRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B,$DrawBorder=TRUE,$Alpha=100,$NoFallBack=FALSE)
2819    {
2820     if ( $X2 < $X1 ) { list($X1, $X2) = array($X2, $X1); }
2821     if ( $Y2 < $Y1 ) { list($Y1, $Y2) = array($Y2, $Y1); }
2822
2823     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2824     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2825     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2826
2827     if ( $Alpha == 100 )
2828      {
2829       /* Process shadows */
2830       if ( $this->ShadowActive && !$NoFallBack )
2831        {
2832         $this->drawFilledRectangle($X1+$this->ShadowXDistance,$Y1+$this->ShadowYDistance,$X2+$this->ShadowXDistance,$Y2+$this->ShadowYDistance,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,FALSE,$this->ShadowAlpha,TRUE);
2833         if ( $this->ShadowBlur != 0 )
2834          {
2835           $AlphaDecay = ($this->ShadowAlpha / $this->ShadowBlur);
2836
2837           for($i=1; $i<=$this->ShadowBlur; $i++)
2838            $this->drawFilledRectangle($X1+$this->ShadowXDistance-$i/2,$Y1+$this->ShadowYDistance-$i/2,$X2+$this->ShadowXDistance-$i/2,$Y2+$this->ShadowYDistance-$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,FALSE,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
2839           for($i=1; $i<=$this->ShadowBlur; $i++)
2840            $this->drawFilledRectangle($X1+$this->ShadowXDistance+$i/2,$Y1+$this->ShadowYDistance+$i/2,$X2+$this->ShadowXDistance+$i/2,$Y2+$this->ShadowYDistance+$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,FALSE,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
2841          }
2842        }
2843
2844       $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2845       imagefilledrectangle($this->Picture,round($X1),round($Y1),round($X2),round($Y2),$C_Rectangle);
2846      }
2847     else
2848      {
2849       $LayerWidth  = abs($X2-$X1)+2;
2850       $LayerHeight = abs($Y2-$Y1)+2;
2851
2852       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
2853       $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
2854       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
2855       imagecolortransparent($this->Layers[0],$C_White);
2856
2857       $C_Rectangle = $this->AllocateColor($this->Layers[0],$R,$G,$B);
2858       imagefilledrectangle($this->Layers[0],round(1),round(1),round($LayerWidth-1),round($LayerHeight-1),$C_Rectangle);
2859
2860       imagecopymerge($this->Picture,$this->Layers[0],round(min($X1,$X2)-1),round(min($Y1,$Y2)-1),0,0,$LayerWidth,$LayerHeight,$Alpha);
2861       imagedestroy($this->Layers[0]);
2862      }
2863
2864     if ( $DrawBorder )
2865      {
2866       $ShadowSettings = $this->ShadowActive; $this->ShadowActive = FALSE;
2867       $this->drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B);
2868       $this->ShadowActive = $ShadowSettings;
2869      }
2870    }
2871
2872   /* This function create a rectangle with rounded corners and antialias */
2873   function drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
2874    {
2875     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2876     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2877     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2878
2879     $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2880
2881     $Step = 90 / ((3.1418 * $Radius)/2);
2882
2883     for($i=0;$i<=90;$i=$i+$Step)
2884      {
2885       $X = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius;
2886       $Y = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius;
2887       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2888
2889       $X = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius;
2890       $Y = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius;
2891       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2892
2893       $X = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius;
2894       $Y = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius;
2895       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2896
2897       $X = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius;
2898       $Y = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius;
2899       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2900      }
2901
2902     $X1=$X1-.2;$Y1=$Y1-.2;
2903     $X2=$X2+.2;$Y2=$Y2+.2;
2904     $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B);
2905     $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B);
2906     $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B);
2907     $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B);
2908    }
2909
2910   /* This function create a filled rectangle with rounded corners and antialias */
2911   function drawFilledRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
2912    {
2913     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2914     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2915     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2916
2917     $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2918
2919     $Step = 90 / ((3.1418 * $Radius)/2);
2920
2921     for($i=0;$i<=90;$i=$i+$Step)
2922      {
2923       $Xi1 = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius;
2924       $Yi1 = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius;
2925
2926       $Xi2 = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius;
2927       $Yi2 = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius;
2928
2929       $Xi3 = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius;
2930       $Yi3 = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius;
2931
2932       $Xi4 = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius;
2933       $Yi4 = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius;
2934
2935       imageline($this->Picture,$Xi1,$Yi1,$X1+$Radius,$Yi1,$C_Rectangle);
2936       imageline($this->Picture,$X2-$Radius,$Yi2,$Xi2,$Yi2,$C_Rectangle);
2937       imageline($this->Picture,$X2-$Radius,$Yi3,$Xi3,$Yi3,$C_Rectangle);
2938       imageline($this->Picture,$Xi4,$Yi4,$X1+$Radius,$Yi4,$C_Rectangle);
2939
2940       $this->drawAntialiasPixel($Xi1,$Yi1,$R,$G,$B);
2941       $this->drawAntialiasPixel($Xi2,$Yi2,$R,$G,$B);
2942       $this->drawAntialiasPixel($Xi3,$Yi3,$R,$G,$B);
2943       $this->drawAntialiasPixel($Xi4,$Yi4,$R,$G,$B);
2944      }
2945
2946     imagefilledrectangle($this->Picture,$X1,$Y1+$Radius,$X2,$Y2-$Radius,$C_Rectangle);
2947     imagefilledrectangle($this->Picture,$X1+$Radius,$Y1,$X2-$Radius,$Y2,$C_Rectangle);
2948
2949     $X1=$X1-.2;$Y1=$Y1-.2;
2950     $X2=$X2+.2;$Y2=$Y2+.2;
2951     $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B);
2952     $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B);
2953     $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B);
2954     $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B);
2955    }
2956
2957   /* This function create a circle with antialias */
2958   function drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
2959    {
2960     if ( $Width == 0 ) { $Width = $Height; }
2961     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2962     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2963     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2964
2965     $C_Circle = $this->AllocateColor($this->Picture,$R,$G,$B);
2966     $Step     = 360 / (2 * 3.1418 * max($Width,$Height));
2967
2968     for($i=0;$i<=360;$i=$i+$Step)
2969      {
2970       $X = cos($i*3.1418/180) * $Height + $Xc;
2971       $Y = sin($i*3.1418/180) * $Width + $Yc;
2972       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2973      }
2974    }
2975
2976   /* This function create a filled circle/ellipse with antialias */
2977   function drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
2978    {
2979     if ( $Width == 0 ) { $Width = $Height; }
2980     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2981     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2982     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2983
2984     $C_Circle = $this->AllocateColor($this->Picture,$R,$G,$B);
2985     $Step     = 360 / (2 * 3.1418 * max($Width,$Height));
2986
2987     for($i=90;$i<=270;$i=$i+$Step)
2988      {
2989       $X1 = cos($i*3.1418/180) * $Height + $Xc;
2990       $Y1 = sin($i*3.1418/180) * $Width + $Yc;
2991       $X2 = cos((180-$i)*3.1418/180) * $Height + $Xc;
2992       $Y2 = sin((180-$i)*3.1418/180) * $Width + $Yc;
2993
2994       $this->drawAntialiasPixel($X1-1,$Y1-1,$R,$G,$B);
2995       $this->drawAntialiasPixel($X2-1,$Y2-1,$R,$G,$B);
2996
2997       if ( ($Y1-1) > $Yc - max($Width,$Height) )
2998        imageline($this->Picture,$X1,$Y1-1,$X2-1,$Y2-1,$C_Circle);
2999      }
3000    }
3001
3002   /* This function will draw a filled ellipse */
3003   function drawEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
3004    { $this->drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); }
3005
3006   /* This function will draw an ellipse */
3007   function drawFilledEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
3008    { $this->drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); }
3009
3010   /* This function create a line with antialias */
3011   function drawLine($X1,$Y1,$X2,$Y2,$R,$G,$B,$GraphFunction=FALSE)
3012    {
3013     if ( $this->LineDotSize > 1 ) { $this->drawDottedLine($X1,$Y1,$X2,$Y2,$this->LineDotSize,$R,$G,$B,$GraphFunction); return(0); }
3014     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3015     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3016     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3017
3018     $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1)); 
3019     if ( $Distance == 0 )
3020      return(-1);
3021     $XStep = ($X2-$X1) / $Distance;
3022     $YStep = ($Y2-$Y1) / $Distance;
3023
3024     for($i=0;$i<=$Distance;$i++)
3025      {
3026       $X = $i * $XStep + $X1;
3027       $Y = $i * $YStep + $Y1;
3028
3029       if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction )
3030        {
3031         if ( $this->LineWidth == 1 )
3032          $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
3033         else
3034          {
3035           $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2);
3036           for($j=$StartOffset;$j<=$EndOffset;$j++)
3037            $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B);
3038          }
3039        }
3040      }
3041    }
3042
3043   /* This function create a line with antialias */
3044   function drawDottedLine($X1,$Y1,$X2,$Y2,$DotSize,$R,$G,$B,$GraphFunction=FALSE)
3045    {
3046     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3047     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3048     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3049
3050     $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1)); 
3051
3052     $XStep = ($X2-$X1) / $Distance;
3053     $YStep = ($Y2-$Y1) / $Distance;
3054
3055     $DotIndex = 0;
3056     for($i=0;$i<=$Distance;$i++)
3057      {
3058       $X = $i * $XStep + $X1;
3059       $Y = $i * $YStep + $Y1;
3060
3061       if ( $DotIndex <= $DotSize)
3062        {
3063         if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction )
3064          {
3065           if ( $this->LineWidth == 1 )
3066            $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
3067           else
3068            {
3069             $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2);
3070             for($j=$StartOffset;$j<=$EndOffset;$j++)
3071              $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B);
3072            }
3073          }
3074        }
3075
3076       $DotIndex++;
3077       if ( $DotIndex == $DotSize * 2 )
3078        $DotIndex = 0;       
3079      }
3080    }
3081
3082   /* Load a PNG file and draw it over the chart */
3083   function drawFromPNG($FileName,$X,$Y,$Alpha=100)
3084    { $this->drawFromPicture(1,$FileName,$X,$Y,$Alpha); }
3085
3086   /* Load a GIF file and draw it over the chart */
3087   function drawFromGIF($FileName,$X,$Y,$Alpha=100)
3088    { $this->drawFromPicture(2,$FileName,$X,$Y,$Alpha); }
3089
3090   /* Load a JPEG file and draw it over the chart */
3091   function drawFromJPG($FileName,$X,$Y,$Alpha=100)
3092    { $this->drawFromPicture(3,$FileName,$X,$Y,$Alpha); }
3093
3094   /* Generic loader function for external pictures */
3095   function drawFromPicture($PicType,$FileName,$X,$Y,$Alpha=100)
3096    {
3097     if ( file_exists($FileName))
3098      {
3099       $Infos  = getimagesize($FileName);
3100       $Width  = $Infos[0];
3101       $Height = $Infos[1];
3102       if ( $PicType == 1 ) { $Raster = imagecreatefrompng($FileName); }
3103       if ( $PicType == 2 ) { $Raster = imagecreatefromgif($FileName); }
3104       if ( $PicType == 3 ) { $Raster = imagecreatefromjpeg($FileName); }
3105
3106       imagecopymerge($this->Picture,$Raster,$X,$Y,0,0,$Width,$Height,$Alpha);
3107       imagedestroy($Raster);
3108      }
3109    }
3110
3111   /* Draw an alpha pixel */
3112   function drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B)
3113    {
3114     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3115     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3116     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3117
3118     if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize )
3119      return(-1);
3120
3121     $RGB2 = imagecolorat($this->Picture, $X, $Y);
3122     $R2   = ($RGB2 >> 16) & 0xFF;
3123     $G2   = ($RGB2 >> 8) & 0xFF;
3124     $B2   = $RGB2 & 0xFF;
3125
3126     $iAlpha = (100 - $Alpha)/100;
3127     $Alpha  = $Alpha / 100;
3128
3129     $Ra   = floor($R*$Alpha+$R2*$iAlpha);
3130     $Ga   = floor($G*$Alpha+$G2*$iAlpha);
3131     $Ba   = floor($B*$Alpha+$B2*$iAlpha);
3132
3133     $C_Aliased = $this->AllocateColor($this->Picture,$Ra,$Ga,$Ba);
3134     imagesetpixel($this->Picture,$X,$Y,$C_Aliased);
3135    }
3136
3137   /* Color helper */
3138   function AllocateColor($Picture,$R,$G,$B,$Factor=0)
3139    {
3140     $R = $R + $Factor;
3141     $G = $G + $Factor;
3142     $B = $B + $Factor;
3143     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3144     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3145     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3146
3147     return(imagecolorallocate($Picture,$R,$G,$B));
3148    }
3149
3150   /* Add a border to the picture */
3151   function addBorder($Size=3,$R=0,$G=0,$B=0)
3152    {
3153     $Width  = $this->XSize+2*$Size;
3154     $Height = $this->YSize+2*$Size;
3155
3156     $Resampled    = imagecreatetruecolor($Width,$Height);
3157     $C_Background = $this->AllocateColor($Resampled,$R,$G,$B);
3158     imagefilledrectangle($Resampled,0,0,$Width,$Height,$C_Background);
3159
3160     imagecopy($Resampled,$this->Picture,$Size,$Size,0,0,$this->XSize,$this->YSize);
3161     imagedestroy($this->Picture);
3162
3163     $this->XSize = $Width;
3164     $this->YSize = $Height;
3165
3166     $this->Picture = imagecreatetruecolor($this->XSize,$this->YSize);
3167     $C_White = $this->AllocateColor($this->Picture,255,255,255);
3168     imagefilledrectangle($this->Picture,0,0,$this->XSize,$this->YSize,$C_White);
3169     imagecolortransparent($this->Picture,$C_White);
3170     imagecopy($this->Picture,$Resampled,0,0,0,0,$this->XSize,$this->YSize);
3171    }
3172
3173   /* Render the current picture to a file */
3174   function Render($FileName)
3175    {
3176     if ( $this->ErrorReporting )
3177      $this->printErrors($this->ErrorInterface);
3178
3179     /* Save image map if requested */
3180     if ( $this->BuildMap )
3181      $this->SaveImageMap();
3182
3183     imagepng($this->Picture,$FileName);
3184    }
3185
3186   /* Render the current picture to STDOUT */
3187   function Stroke()
3188    {
3189     if ( $this->ErrorReporting )
3190      $this->printErrors("GD");
3191
3192     /* Save image map if requested */
3193     if ( $this->BuildMap )
3194      $this->SaveImageMap();
3195
3196     header('Content-type: image/png');
3197     imagepng($this->Picture);
3198    }
3199
3200   /* Private functions for internal processing */
3201   function drawAntialiasPixel($X,$Y,$R,$G,$B,$Alpha=100,$NoFallBack=FALSE)
3202    {
3203     /* Process shadows */
3204     if ( $this->ShadowActive && !$NoFallBack )
3205      {
3206       $this->drawAntialiasPixel($X+$this->ShadowXDistance,$Y+$this->ShadowYDistance,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,$this->ShadowAlpha,TRUE);
3207       if ( $this->ShadowBlur != 0 )
3208        {
3209         $AlphaDecay = ($this->ShadowAlpha / $this->ShadowBlur);
3210
3211         for($i=1; $i<=$this->ShadowBlur; $i++)
3212          $this->drawAntialiasPixel($X+$this->ShadowXDistance-$i/2,$Y+$this->ShadowYDistance-$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
3213         for($i=1; $i<=$this->ShadowBlur; $i++)
3214          $this->drawAntialiasPixel($X+$this->ShadowXDistance+$i/2,$Y+$this->ShadowYDistance+$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
3215        }
3216      }
3217
3218     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3219     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3220     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3221
3222     $Plot = "";
3223     $Xi   = floor($X);
3224     $Yi   = floor($Y);
3225
3226     if ( $Xi == $X && $Yi == $Y)
3227      {
3228       if ( $Alpha == 100 )
3229        {
3230         $C_Aliased = $this->AllocateColor($this->Picture,$R,$G,$B);
3231         imagesetpixel($this->Picture,$X,$Y,$C_Aliased);
3232        }
3233       else
3234        $this->drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B);
3235      }
3236     else
3237      {
3238       $Alpha1 = (((1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
3239       if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); }
3240
3241       $Alpha2 = ((($X - floor($X)) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
3242       if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); }
3243
3244       $Alpha3 = (((1 - ($X - floor($X))) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
3245       if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); }
3246
3247       $Alpha4 = ((($X - floor($X)) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
3248       if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); }
3249      }
3250    }
3251
3252   /* Validate data contained in the description array */
3253   function validateDataDescription($FunctionName,&$DataDescription,$DescriptionRequired=TRUE)
3254    {
3255     if (!isset($DataDescription["Position"]))
3256      {
3257       $this->Errors[] = "[Warning] ".$FunctionName." - Y Labels are not set.";
3258       $DataDescription["Position"] = "Name";
3259      }
3260
3261     if ( $DescriptionRequired )
3262      {
3263       if (!isset($DataDescription["Description"]))
3264        {
3265         $this->Errors[] = "[Warning] ".$FunctionName." - Series descriptions are not set.";
3266         foreach($DataDescription["Values"] as $key => $Value)
3267          {
3268           $DataDescription["Description"][$Value] = $Value;
3269          }
3270        }
3271
3272       if (count($DataDescription["Description"]) < count($DataDescription["Values"]))
3273        {
3274         $this->Errors[] = "[Warning] ".$FunctionName." - Some series descriptions are not set.";
3275         foreach($DataDescription["Values"] as $key => $Value)
3276          {
3277           if ( !isset($DataDescription["Description"][$Value]))
3278            $DataDescription["Description"][$Value] = $Value;
3279          }
3280        }
3281      }
3282    }
3283
3284   /* Validate data contained in the data array */
3285   function validateData($FunctionName,&$Data)
3286    {
3287     $DataSummary = array();
3288
3289     foreach($Data as $key => $Values)
3290      {
3291       foreach($Values as $key2 => $Value)
3292        {
3293         if (!isset($DataSummary[$key2]))
3294          $DataSummary[$key2] = 1;
3295         else
3296          $DataSummary[$key2]++;
3297        }
3298      }
3299
3300     if ( max($DataSummary) == 0 )
3301      $this->Errors[] = "[Warning] ".$FunctionName." - No data set.";
3302
3303     foreach($DataSummary as $key => $Value)
3304      {
3305       if ($Value < max($DataSummary))
3306        {
3307         $this->Errors[] = "[Warning] ".$FunctionName." - Missing data in serie ".$key.".";
3308        }
3309      }
3310    }
3311
3312   /* Print all error messages on the CLI or graphically */
3313   function printErrors($Mode="CLI")
3314    {
3315     if (count($this->Errors) == 0)
3316      return(0);
3317
3318     if ( $Mode == "CLI" )
3319      {
3320       foreach($this->Errors as $key => $Value)
3321        echo $Value."\r\n";
3322      }
3323     elseif ( $Mode == "GD" )
3324      {
3325       $this->setLineStyle($Width=1);
3326       $MaxWidth = 0;
3327       foreach($this->Errors as $key => $Value)
3328        {
3329         $Position  = imageftbbox($this->ErrorFontSize,0,$this->ErrorFontName,$Value);
3330         $TextWidth = $Position[2]-$Position[0];
3331         if ( $TextWidth > $MaxWidth ) { $MaxWidth = $TextWidth; }
3332        }
3333       $this->drawFilledRoundedRectangle($this->XSize-($MaxWidth+20),$this->YSize-(20+(($this->ErrorFontSize+4)*count($this->Errors))),$this->XSize-10,$this->YSize-10,6,233,185,185);
3334       $this->drawRoundedRectangle($this->XSize-($MaxWidth+20),$this->YSize-(20+(($this->ErrorFontSize+4)*count($this->Errors))),$this->XSize-10,$this->YSize-10,6,193,145,145);
3335
3336       $C_TextColor = $this->AllocateColor($this->Picture,133,85,85);
3337       $YPos        = $this->YSize - (18 + (count($this->Errors)-1) * ($this->ErrorFontSize + 4));
3338       foreach($this->Errors as $key => $Value)
3339        {
3340         imagettftext($this->Picture,$this->ErrorFontSize,0,$this->XSize-($MaxWidth+15),$YPos,$C_TextColor,$this->ErrorFontName,$Value);
3341         $YPos = $YPos + ($this->ErrorFontSize + 4);
3342        }
3343      }
3344    }
3345
3346   /* Activate the image map creation process */
3347   function setImageMap($Mode=TRUE,$GraphID="MyGraph")
3348    {
3349     $this->BuildMap = $Mode;
3350     $this->MapID    = $GraphID;
3351    }
3352
3353   /* Add a box into the image map */
3354   function addToImageMap($X1,$Y1,$X2,$Y2,$SerieName,$Value,$CallerFunction)
3355    {
3356     if ( $this->MapFunction == NULL || $this->MapFunction == $CallerFunction )
3357      {
3358       $this->ImageMap[]  = round($X1).",".round($Y1).",".round($X2).",".round($Y2).",".$SerieName.",".$Value;
3359       $this->MapFunction = $CallerFunction;
3360      }
3361    }
3362
3363   /* Load and cleanup the image map from disk */
3364   function getImageMap($MapName,$Flush=TRUE)
3365    {
3366     /* Strip HTML query strings */
3367     $Values   = $this->tmpFolder.$MapName;
3368     $Value    = split("\?",$Values);
3369     $FileName = $Value[0];
3370
3371     if ( file_exists($FileName) )
3372      {
3373       $Handle     = fopen($FileName, "r");
3374       $MapContent = fread($Handle, filesize($FileName));
3375       fclose($Handle);
3376       echo $MapContent;
3377
3378       if ( $Flush )
3379        unlink($FileName);
3380
3381       exit();
3382      }
3383     else
3384      {
3385       header("HTTP/1.0 404 Not Found");
3386       exit();
3387      }
3388    }
3389
3390   /* Save the image map to the disk */
3391   function SaveImageMap()
3392    {
3393     if ( !$this->BuildMap ) { return(-1); }
3394
3395     if ( $this->ImageMap == NULL )
3396      {
3397       $this->Errors[] = "[Warning] SaveImageMap - Image map is empty.";
3398       return(-1);
3399      }
3400
3401     $Handle = fopen($this->tmpFolder.$this->MapID, 'w');
3402     if ( !$Handle )
3403      {
3404       $this->Errors[] = "[Warning] SaveImageMap - Cannot save the image map.";
3405       return(-1);
3406      }
3407     else
3408      {
3409       foreach($this->ImageMap as $Key => $Value)
3410        fwrite($Handle, htmlentities($Value)."\r");
3411      }
3412     fclose ($Handle);
3413    }
3414
3415   /* Convert seconds to a time format string */
3416   function ToTime($Value)
3417    {
3418     $Hour   = floor($Value/3600);
3419     $Minute = floor(($Value - $Hour*3600)/60);
3420     $Second = floor($Value - $Hour*3600 - $Minute*60);
3421
3422     if (strlen($Hour) == 1 )   { $Hour = "0".$Hour; }
3423     if (strlen($Minute) == 1 ) { $Minute = "0".$Minute; }
3424     if (strlen($Second) == 1 ) { $Second = "0".$Second; }
3425
3426     return($Hour.":".$Minute.":".$Second);
3427    }
3428
3429   /* Convert to metric system */
3430   function ToMetric($Value)
3431    {
3432     $Go = floor($Value/1000000000);
3433     $Mo = floor(($Value - $Go*1000000000)/1000000);
3434     $Ko = floor(($Value - $Go*1000000000 - $Mo*1000000)/1000);
3435     $o  = floor($Value - $Go*1000000000 - $Mo*1000000 - $Ko*1000);
3436
3437     if ($Go != 0)   { return($Go.".".$Mo."g"); }
3438     if ($Mo != 0)   { return($Mo.".".$ko."m"); }
3439     if ($Ko != 0)   { return($Ko.".".$o)."k"; }
3440     return($o);
3441    }
3442
3443   /* Convert to curency */
3444   function ToCurrency($Value)
3445    {
3446     $Go = floor($Value/1000000000);
3447     $Mo = floor(($Value - $Go*1000000000)/1000000);
3448     $Ko = floor(($Value - $Go*1000000000 - $Mo*1000000)/1000);
3449     $o  = floor($Value - $Go*1000000000 - $Mo*1000000 - $Ko*1000);
3450
3451     if ( strlen($o) == 1 ) { $o = "00".$o; }
3452     if ( strlen($o) == 2 ) { $o = "0".$o; }
3453
3454     $ResultString = $o;
3455     if ( $Ko != 0 ) { $ResultString = $Ko.".".$ResultString; }
3456     if ( $Mo != 0 ) { $ResultString = $Mo.".".$ResultString; }
3457     if ( $Go != 0 ) { $ResultString = $Go.".".$ResultString; }
3458
3459     $ResultString = $this->Currency.$ResultString;
3460     return($ResultString);
3461    }
3462
3463   /* Set date format for axis labels */
3464   function setDateFormat($Format)
3465    {
3466     $this->DateFormat = $Format;
3467    }
3468
3469   /* Convert TS to a date format string */
3470   function ToDate($Value)
3471    {
3472     return(date($this->DateFormat,$Value));
3473    }
3474
3475   /* Check if a number is a full integer (for scaling) */
3476   function isRealInt($Value)
3477    {
3478     if ($Value == floor($Value))
3479      return(TRUE);
3480     return(FALSE);
3481    }
3482  }
3483
3484 function RaiseFatal($Message)
3485  {
3486   echo "[FATAL] ".$Message."\r\n";
3487   exit();
3488  }
3489?>
Note: See TracBrowser for help on using the repository browser.