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   /* retrieve the pie legends size */
981   function getPieLegendBoxSize($Data, $DataDescription)
982    {
983     if ( !isset($DataDescription["Position"]) )
984      return(-1);
985
986     /* <-10->[8]<-4->Text<-10-> */
987     $MaxWidth = 0; $MaxHeight = 8;
988     foreach($Data as $DataPoint)
989      {
990       $Value = $DataPoint[$DataDescription["Position"]];
991       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
992       $TextWidth  = $Position[2]-$Position[0];
993       $TextHeight = $Position[1]-$Position[7];
994       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
995       $MaxHeight = $MaxHeight + $TextHeight + 4;
996      }
997
998     $MaxHeight = $MaxHeight - 3;
999     $MaxWidth  = $MaxWidth + 32;
1000
1001     return(array($MaxWidth,$MaxHeight));
1002    }
1003
1004   /* Draw the data legends */
1005   function drawLegend($XPos,$YPos,$DataDescription,$R,$G,$B,$Rs=-1,$Gs=-1,$Bs=-1,$Rt=0,$Gt=0,$Bt=0,$Border=TRUE)
1006    {
1007     /* Validate the Data and DataDescription array */
1008     $this->validateDataDescription("drawLegend",$DataDescription);
1009
1010     if ( !isset($DataDescription["Description"]) )
1011      return(-1);
1012
1013     $C_TextColor =$this->AllocateColor($this->Picture,$Rt,$Gt,$Bt);
1014
1015     /* <-10->[8]<-4->Text<-10-> */
1016     $MaxWidth = 0; $MaxHeight = 8;
1017     foreach($DataDescription["Description"] as $Key => $Value)
1018      {
1019       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1020       $TextWidth  = $Position[2]-$Position[0];
1021       $TextHeight = $Position[1]-$Position[7];
1022       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
1023       $MaxHeight = $MaxHeight + $TextHeight + 4;
1024      }
1025     $MaxHeight = $MaxHeight - 5;
1026     $MaxWidth  = $MaxWidth + 32;
1027
1028     if ( $Rs == -1 || $Gs == -1 || $Bs == -1 )
1029      { $Rs = $R-30; $Gs = $G-30; $Bs = $B-30; }
1030
1031     if ( $Border )
1032      {
1033       $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$Rs,$Gs,$Bs);
1034       $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1035      }
1036
1037     $YOffset = 4 + $this->FontSize; $ID = 0;
1038     foreach($DataDescription["Description"] as $Key => $Value)
1039      {
1040       $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"]);
1041       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value);
1042
1043       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1044       $TextHeight = $Position[1]-$Position[7];
1045
1046       $YOffset = $YOffset + $TextHeight + 4;
1047       $ID++;
1048      }
1049    }
1050
1051   /* Draw the data legends */
1052   function drawPieLegend($XPos,$YPos,$Data,$DataDescription,$R,$G,$B)
1053    {
1054     /* Validate the Data and DataDescription array */
1055     $this->validateDataDescription("drawPieLegend",$DataDescription,FALSE);
1056     $this->validateData("drawPieLegend",$Data);
1057
1058     if ( !isset($DataDescription["Position"]) )
1059      return(-1);
1060
1061     $C_TextColor =$this->AllocateColor($this->Picture,0,0,0);
1062
1063     /* <-10->[8]<-4->Text<-10-> */
1064     $MaxWidth = 0; $MaxHeight = 8;
1065     foreach($Data as $Key => $Value)
1066      {
1067       $Value2 = $Value[$DataDescription["Position"]];
1068       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1069       $TextWidth = $Position[2]-$Position[0];
1070       $TextHeight = $Position[1]-$Position[7];
1071       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
1072
1073       $MaxHeight = $MaxHeight + $TextHeight + 4;
1074      }
1075     $MaxHeight = $MaxHeight - 3;
1076     $MaxWidth  = $MaxWidth + 32;
1077
1078     $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$R-30,$G-30,$B-30);
1079     $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
1080
1081     $YOffset = 4 + $this->FontSize; $ID = 0;
1082     foreach($Data as $Key => $Value)
1083      {
1084       $Value2     = $Value[$DataDescription["Position"]];
1085       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
1086       $TextHeight = $Position[1]-$Position[7];
1087       $this->drawFilledRectangle($XPos+10,$YPos+$YOffset-6,$XPos+14,$YPos+$YOffset-2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]);
1088
1089       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value2);
1090       $YOffset = $YOffset + $TextHeight + 4;
1091       $ID++;
1092      }
1093    }
1094
1095   /* Draw the graph title */
1096   function drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2=-1,$YPos2=-1,$Shadow=FALSE)
1097    {
1098     $C_TextColor = $this->AllocateColor($this->Picture,$R,$G,$B);
1099
1100     if ( $XPos2 != -1 )
1101      {
1102       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1103       $TextWidth = $Position[2]-$Position[0];
1104       $XPos      = floor(( $XPos2 - $XPos - $TextWidth ) / 2 ) + $XPos;
1105      }
1106
1107     if ( $YPos2 != -1 )
1108      {
1109       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
1110       $TextHeight = $Position[5]-$Position[3];
1111       $YPos       = floor(( $YPos2 - $YPos - $TextHeight ) / 2 ) + $YPos;
1112      }
1113
1114     if ( $Shadow )
1115      {
1116       $C_ShadowColor = $this->AllocateColor($this->Picture,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor);
1117       imagettftext($this->Picture,$this->FontSize,0,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$C_ShadowColor,$this->FontName,$Value);
1118      }
1119
1120     imagettftext($this->Picture,$this->FontSize,0,$XPos,$YPos,$C_TextColor,$this->FontName,$Value);
1121    }
1122
1123   /* Draw a text box with text align & alpha properties */
1124   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)
1125    {
1126     $Position   = imageftbbox($this->FontSize,$Angle,$this->FontName,$Text);
1127     $TextWidth  = $Position[2]-$Position[0];
1128     $TextHeight = $Position[5]-$Position[3];
1129     $AreaWidth  = $X2 - $X1;
1130     $AreaHeight = $Y2 - $Y1;
1131
1132     if ( $BgR != -1 && $BgG != -1 && $BgB != -1 )
1133      $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$BgR,$BgG,$BgB,FALSE,$Alpha);
1134
1135     if ( $Align == ALIGN_TOP_LEFT )      { $X = $X1+1; $Y = $Y1+$this->FontSize+1; }
1136     if ( $Align == ALIGN_TOP_CENTER )    { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+$this->FontSize+1; }
1137     if ( $Align == ALIGN_TOP_RIGHT )     { $X = $X2-$TextWidth-1; $Y = $Y1+$this->FontSize+1; }
1138     if ( $Align == ALIGN_LEFT )          { $X = $X1+1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1139     if ( $Align == ALIGN_CENTER )        { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1140     if ( $Align == ALIGN_RIGHT )         { $X = $X2-$TextWidth-1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); }
1141     if ( $Align == ALIGN_BOTTOM_LEFT )   { $X = $X1+1; $Y = $Y2-1; }
1142     if ( $Align == ALIGN_BOTTOM_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y2-1; }
1143     if ( $Align == ALIGN_BOTTOM_RIGHT )  { $X = $X2-$TextWidth-1; $Y = $Y2-1; }
1144
1145     $C_TextColor   =$this->AllocateColor($this->Picture,$R,$G,$B);
1146     $C_ShadowColor =$this->AllocateColor($this->Picture,0,0,0);
1147     if ( $Shadow )
1148      imagettftext($this->Picture,$this->FontSize,$Angle,$X+1,$Y+1,$C_ShadowColor,$this->FontName,$Text);
1149
1150     imagettftext($this->Picture,$this->FontSize,$Angle,$X,$Y,$C_TextColor,$this->FontName,$Text);
1151    }
1152
1153   /* Compute and draw the scale */
1154   function drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4,$FreeText=NULL)
1155    {
1156     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
1157     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
1158     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
1159
1160     $C_TextColor =$this->AllocateColor($this->Picture,$R,$G,$B);
1161     $Y = $this->GArea_Y2 - ($Value - $this->VMin) * $this->DivisionRatio;
1162
1163     if ( $Y <= $this->GArea_Y1 || $Y >= $this->GArea_Y2 )
1164      return(-1);
1165
1166     if ( $TickWidth == 0 )
1167      $this->drawLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$R,$G,$B);
1168     else
1169      $this->drawDottedLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$TickWidth,$R,$G,$B);
1170
1171     if ( $ShowLabel )
1172      {
1173       if ( $FreeText == NULL )
1174        { $Label = $Value; } else { $Label = $FreeText; }
1175
1176       if ( $ShowOnRight )
1177        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+2,$Y+($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1178       else
1179        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1+2,$Y-($this->FontSize/2),$C_TextColor,$this->FontName,$Label);
1180      }
1181    }
1182
1183   /* This function put a label on a specific point */
1184   function setLabel($Data,$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210)
1185    {
1186     /* Validate the Data and DataDescription array */
1187     $this->validateDataDescription("setLabel",$DataDescription);
1188     $this->validateData("setLabel",$Data);
1189     $ShadowFactor = 100;
1190     $C_Label      =$this->AllocateColor($this->Picture,$R,$G,$B);
1191     $C_Shadow     =$this->AllocateColor($this->Picture,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1192     $C_TextColor  =$this->AllocateColor($this->Picture,0,0,0);
1193
1194     $Cp = 0; $Found = FALSE;
1195     foreach ( $Data as $Key => $Value )
1196      {
1197       if ( $Data[$Key][$DataDescription["Position"]] == $ValueName )
1198        { $NumericalValue = $Data[$Key][$SerieName]; $Found = TRUE; }
1199       if ( !$Found )
1200        $Cp++;
1201      }
1202
1203     $XPos = $this->GArea_X1 + $this->GAreaXOffset + ( $this->DivisionWidth * $Cp ) + 2;
1204     $YPos = $this->GArea_Y2 - ($NumericalValue - $this->VMin) * $this->DivisionRatio;
1205
1206     $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
1207     $TextHeight = $Position[3] - $Position[5];
1208     $TextWidth  = $Position[2]-$Position[0] + 2;
1209     $TextOffset = floor($TextHeight/2);
1210
1211     // Shadow
1212     $Poly = array($XPos+1,$YPos+1,$XPos + 9,$YPos - $TextOffset,$XPos + 8,$YPos + $TextOffset + 2);
1213     imagefilledpolygon($this->Picture,$Poly,3,$C_Shadow);
1214     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos - $TextOffset - .2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1215     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1216     $this->drawFilledRectangle($XPos + 9,$YPos - $TextOffset-.2,$XPos + 13 + $TextWidth,$YPos + $TextOffset + 2.2,$R-$ShadowFactor,$G-$ShadowFactor,$B-$ShadowFactor);
1217
1218     // Label background
1219     $Poly = array($XPos,$YPos,$XPos + 8,$YPos - $TextOffset - 1,$XPos + 8,$YPos + $TextOffset + 1);
1220     imagefilledpolygon($this->Picture,$Poly,3,$C_Label);
1221     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos - $TextOffset - 1.2,$R,$G,$B);
1222     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos + $TextOffset + 1.2,$R,$G,$B);
1223     $this->drawFilledRectangle($XPos + 8,$YPos - $TextOffset - 1.2,$XPos + 12 + $TextWidth,$YPos + $TextOffset + 1.2,$R,$G,$B);
1224
1225     imagettftext($this->Picture,$this->FontSize,0,$XPos + 10,$YPos + $TextOffset,$C_TextColor,$this->FontName,$Caption);
1226    }
1227
1228   /* This function draw a plot graph */
1229   function drawPlotGraph($Data,$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=FALSE)
1230    {
1231     /* Validate the Data and DataDescription array */
1232     $this->validateDataDescription("drawPlotGraph",$DataDescription);
1233     $this->validateData("drawPlotGraph",$Data);
1234
1235     $GraphID = 0;
1236     $Ro = $R2; $Go = $G2; $Bo = $B2;
1237
1238     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1239      {
1240       $ID = 0;
1241       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1242        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1243
1244       $R = $this->Palette[$ColorID]["R"];
1245       $G = $this->Palette[$ColorID]["G"];
1246       $B = $this->Palette[$ColorID]["B"];
1247       $R2 = $Ro; $G2 = $Go; $B2 = $Bo;
1248
1249       if ( isset($DataDescription["Symbol"][$ColName]) )
1250        {
1251         $Is_Alpha = ((ord ( file_get_contents ($DataDescription["Symbol"][$ColName], false, null, 25, 1)) & 6) & 4) == 4;
1252
1253         $Infos       = getimagesize($DataDescription["Symbol"][$ColName]);
1254         $ImageWidth  = $Infos[0];
1255         $ImageHeight = $Infos[1];
1256         $Symbol      = imagecreatefromgif($DataDescription["Symbol"][$ColName]);
1257        }
1258
1259       $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1260       $Hsize = round($BigRadius/2);
1261       $R3 = -1; $G3 = -1; $B3 = -1;
1262       foreach ( $Data as $Key => $Values )
1263        {
1264         $Value = $Data[$Key][$ColName];
1265         $YPos  = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1266
1267         /* Save point into the image map if option activated */
1268         if ( $this->BuildMap )
1269          $this->addToImageMap($XPos-$Hsize,$YPos-$Hsize,$XPos+1+$Hsize,$YPos+$Hsize+1,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Plot");
1270
1271         if ( is_numeric($Value) )
1272          {
1273           if ( !isset($DataDescription["Symbol"][$ColName]) )
1274            {
1275
1276             if ( $Shadow )
1277              {
1278               if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1279                $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1280               else
1281                {
1282                 $R3 = $this->Palette[$ColorID]["R"]-20; if ( $R3 < 0 ) { $R3 = 0; }
1283                 $G3 = $this->Palette[$ColorID]["G"]-20; if ( $G3 < 0 ) { $G3 = 0; }
1284                 $B3 = $this->Palette[$ColorID]["B"]-20; if ( $B3 < 0 ) { $B3 = 0; }
1285                 $this->drawFilledCircle($XPos+2,$YPos+2,$BigRadius,$R3,$G3,$B3);
1286                }
1287              }
1288
1289             $this->drawFilledCircle($XPos+1,$YPos+1,$BigRadius,$R,$G,$B);
1290
1291             if ( $SmallRadius != 0 )
1292              {
1293               if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1294                $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1295               else
1296                {
1297                 $R2 = $this->Palette[$ColorID]["R"]-15; if ( $R2 < 0 ) { $R2 = 0; }
1298                 $G2 = $this->Palette[$ColorID]["G"]-15; if ( $G2 < 0 ) { $G2 = 0; }
1299                 $B2 = $this->Palette[$ColorID]["B"]-15; if ( $B2 < 0 ) { $B2 = 0; }
1300
1301                 $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
1302                }
1303              }
1304            }
1305           else
1306            {
1307             imagecopymerge($this->Picture,$Symbol,$XPos+1-$ImageWidth/2,$YPos+1-$ImageHeight/2,0,0,$ImageWidth,$ImageHeight,100);
1308            }
1309          }
1310
1311         $XPos = $XPos + $this->DivisionWidth;
1312        }
1313       $GraphID++;
1314      }
1315    }
1316
1317   /* This function draw a plot graph in an X/Y space */
1318   function drawXYPlotGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1,$Shadow=TRUE)
1319    {
1320     $R = $this->Palette[$PaletteID]["R"];
1321     $G = $this->Palette[$PaletteID]["G"];
1322     $B = $this->Palette[$PaletteID]["B"];
1323     $R3 = -1; $G3 = -1; $B3 = -1;
1324
1325     $YLast = -1; $XLast = -1;
1326     foreach ( $Data as $Key => $Values )
1327      {
1328       if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1329        {
1330         $X = $Data[$Key][$XSerieName];
1331         $Y = $Data[$Key][$YSerieName];
1332
1333         $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1334         $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1335
1336
1337         if ( $Shadow )
1338          {
1339           if ( $R3 !=-1 && $G3 !=-1 && $B3 !=-1 )
1340            $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1341           else
1342            {
1343             $R3 = $this->Palette[$PaletteID]["R"]-20; if ( $R < 0 ) { $R = 0; }
1344             $G3 = $this->Palette[$PaletteID]["G"]-20; if ( $G < 0 ) { $G = 0; }
1345             $B3 = $this->Palette[$PaletteID]["B"]-20; if ( $B < 0 ) { $B = 0; }
1346             $this->drawFilledCircle($X+2,$Y+2,$BigRadius,$R3,$G3,$B3);
1347            }
1348          }
1349
1350         $this->drawFilledCircle($X+1,$Y+1,$BigRadius,$R,$G,$B);
1351
1352         if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
1353          $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1354         else
1355          {
1356           $R2 = $this->Palette[$PaletteID]["R"]+20; if ( $R > 255 ) { $R = 255; }
1357           $G2 = $this->Palette[$PaletteID]["G"]+20; if ( $G > 255 ) { $G = 255; }
1358           $B2 = $this->Palette[$PaletteID]["B"]+20; if ( $B > 255 ) { $B = 255; }
1359           $this->drawFilledCircle($X+1,$Y+1,$SmallRadius,$R2,$G2,$B2);
1360          }
1361        }
1362      }
1363
1364    }
1365
1366   /* This function draw an area between two series */
1367   function drawArea($Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50)
1368    {
1369     /* Validate the Data and DataDescription array */
1370     $this->validateData("drawArea",$Data);
1371
1372     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1373     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1374
1375     $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1376     $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
1377     imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1378     imagecolortransparent($this->Layers[0],$C_White);
1379
1380     $C_Graph =$this->AllocateColor($this->Layers[0],$R,$G,$B);
1381
1382     $XPos     = $this->GAreaXOffset;
1383     $LastXPos = -1;
1384     foreach ( $Data as $Key => $Values )
1385      {
1386       $Value1 = $Data[$Key][$Serie1];
1387       $Value2 = $Data[$Key][$Serie2];
1388       $YPos1  = $LayerHeight - (($Value1-$this->VMin) * $this->DivisionRatio);
1389       $YPos2  = $LayerHeight - (($Value2-$this->VMin) * $this->DivisionRatio);
1390
1391       if ( $LastXPos != -1 )
1392        {
1393         $Points   = "";
1394         $Points[] = $LastXPos; $Points[] = $LastYPos1;
1395         $Points[] = $LastXPos; $Points[] = $LastYPos2;
1396         $Points[] = $XPos; $Points[] = $YPos2;
1397         $Points[] = $XPos; $Points[] = $YPos1;
1398
1399         imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1400        }
1401
1402       $LastYPos1 = $YPos1;
1403       $LastYPos2 = $YPos2;
1404       $LastXPos  = $XPos;
1405
1406       $XPos = $XPos + $this->DivisionWidth;
1407      }
1408
1409     imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1410     imagedestroy($this->Layers[0]);
1411    }
1412
1413
1414   /* This function write the values of the specified series */
1415   function writeValues($Data,$DataDescription,$Series)
1416    {
1417     /* Validate the Data and DataDescription array */
1418     $this->validateDataDescription("writeValues",$DataDescription);
1419     $this->validateData("writeValues",$Data);
1420
1421     if ( !is_array($Series) ) { $Series = array($Series); }
1422
1423     foreach($Series as $Key => $Serie)
1424      {
1425       $ID = 0;
1426       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1427        { if ( $keyI == $Serie ) { $ColorID = $ID; }; $ID++; }
1428
1429       $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1430       $XLast = -1;
1431       foreach ( $Data as $Key => $Values )
1432        {
1433         if ( isset($Data[$Key][$Serie]) && is_numeric($Data[$Key][$Serie]))
1434          {
1435           $Value = $Data[$Key][$Serie];
1436           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1437
1438           $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Value);
1439           $Width  = $Positions[2] - $Positions[6]; $XOffset = $XPos - ($Width/2);
1440           $Height = $Positions[3] - $Positions[7]; $YOffset = $YPos - 4;
1441
1442           $C_TextColor =$this->AllocateColor($this->Picture,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1443           imagettftext($this->Picture,$this->FontSize,0,$XOffset,$YOffset,$C_TextColor,$this->FontName,$Value);
1444          }
1445         $XPos = $XPos + $this->DivisionWidth;
1446        }
1447
1448      }
1449    }
1450
1451   /* This function draw a line graph */
1452   function drawLineGraph($Data,$DataDescription,$SerieName="")
1453    {
1454     /* Validate the Data and DataDescription array */
1455     $this->validateDataDescription("drawLineGraph",$DataDescription);
1456     $this->validateData("drawLineGraph",$Data);
1457
1458     $GraphID = 0;
1459     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1460      {
1461       $ID = 0;
1462       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1463        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1464
1465       if ( $SerieName == "" || $SerieName == $ColName )
1466        {
1467         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1468         $XLast = -1;
1469         foreach ( $Data as $Key => $Values )
1470          {
1471           if ( isset($Data[$Key][$ColName]))
1472            {
1473             $Value = $Data[$Key][$ColName];
1474             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1475
1476             /* Save point into the image map if option activated */
1477             if ( $this->BuildMap )
1478              $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Line");
1479
1480             if (!is_numeric($Value)) { $XLast = -1; }
1481             if ( $XLast != -1 )
1482              $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1483
1484             $XLast = $XPos;
1485             $YLast = $YPos;
1486             if (!is_numeric($Value)) { $XLast = -1; }
1487            }
1488           $XPos = $XPos + $this->DivisionWidth;
1489          }
1490         $GraphID++;
1491        }
1492      }
1493    }
1494
1495   /* This function draw a line graph */
1496   function drawXYGraph($Data,$DataDescription,$YSerieName,$XSerieName,$PaletteID=0)
1497    {
1498     $YLast = -1; $XLast = -1;
1499     foreach ( $Data as $Key => $Values )
1500      {
1501       if ( isset($Data[$Key][$YSerieName]) && isset($Data[$Key][$XSerieName]) )
1502        {
1503         $X = $Data[$Key][$XSerieName];
1504         $Y = $Data[$Key][$YSerieName];
1505
1506         $Y = $this->GArea_Y2 - (($Y-$this->VMin) * $this->DivisionRatio);
1507         $X = $this->GArea_X1 + (($X-$this->VXMin) * $this->XDivisionRatio);
1508
1509         if ($XLast != -1 && $YLast != -1)
1510          {
1511           $this->drawLine($XLast,$YLast,$X,$Y,$this->Palette[$PaletteID]["R"],$this->Palette[$PaletteID]["G"],$this->Palette[$PaletteID]["B"],TRUE);
1512          }
1513
1514         $XLast = $X;
1515         $YLast = $Y;
1516        }
1517      }
1518    }
1519
1520   /* This function draw a cubic curve */
1521   function drawCubicCurve($Data,$DataDescription,$Accuracy=.1,$SerieName="")
1522    {
1523     /* Validate the Data and DataDescription array */
1524     $this->validateDataDescription("drawCubicCurve",$DataDescription);
1525     $this->validateData("drawCubicCurve",$Data);
1526
1527     $GraphID = 0;
1528     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1529      {
1530       if ( $SerieName == "" || $SerieName == $ColName )
1531        {
1532         $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1533         $XIn[0] = 0; $YIn[0] = 0;
1534
1535         $ID = 0;
1536         foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1537          { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1538
1539         $Index = 1;
1540         $XLast = -1; $Missing = "";
1541         foreach ( $Data as $Key => $Values )
1542          {
1543           if ( isset($Data[$Key][$ColName]) )
1544            {
1545             $Value = $Data[$Key][$ColName];
1546             $XIn[$Index] = $Index;
1547             $YIn[$Index] = $Value;
1548             if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1549             $Index++;
1550            }
1551          }
1552         $Index--;
1553
1554         $Yt[0] = 0;
1555         $Yt[1] = 0;
1556         $U[1]  = 0;
1557         for($i=2;$i<=$Index-1;$i++)
1558          {
1559           $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1560           $p      = $Sig * $Yt[$i-1] + 2;
1561           $Yt[$i] = ($Sig - 1) / $p;
1562           $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1563           $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1564          }
1565
1566         $qn = 0;
1567         $un = 0;
1568         $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1569
1570         for($k=$Index-1;$k>=1;$k--)
1571          $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1572
1573         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
1574         for($X=1;$X<=$Index;$X=$X+$Accuracy)
1575          {
1576           $klo = 1;
1577           $khi = $Index;
1578           $k   = $khi - $klo;
1579           while($k > 1)
1580            {
1581             $k = $khi - $klo;
1582             If ( $XIn[$k] >= $X )
1583              $khi = $k;
1584             else
1585              $klo = $k;
1586            }
1587           $klo = $khi - 1;
1588
1589           $h     = $XIn[$khi] - $XIn[$klo];
1590           $a     = ($XIn[$khi] - $X) / $h;
1591           $b     = ($X - $XIn[$klo]) / $h;
1592           $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1593
1594           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1595
1596           if ( $XLast != -1 && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]) )
1597            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1598
1599           $XLast = $XPos;
1600           $YLast = $YPos;
1601           $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
1602          }
1603
1604         // Add potentialy missing values
1605         $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1606         if ( $XPos < ($this->GArea_X2 - $this->GAreaXOffset) )
1607          {
1608           $YPos = $this->GArea_Y2 - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1609           $this->drawLine($XLast,$YLast,$this->GArea_X2-$this->GAreaXOffset,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1610          }
1611
1612         $GraphID++;
1613        }
1614      }
1615    }
1616
1617   /* This function draw a filled cubic curve */
1618   function drawFilledCubicCurve($Data,$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE)
1619    {
1620     /* Validate the Data and DataDescription array */
1621     $this->validateDataDescription("drawFilledCubicCurve",$DataDescription);
1622     $this->validateData("drawFilledCubicCurve",$Data);
1623
1624     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1625     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1626     $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1627     if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1628
1629     $GraphID = 0;
1630     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1631      {
1632       $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1633       $XIn[0] = 0; $YIn[0] = 0;
1634
1635       $ID = 0;
1636       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1637        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1638
1639       $Index = 1;
1640       $XLast = -1; $Missing = "";
1641       foreach ( $Data as $Key => $Values )
1642        {
1643         $Value = $Data[$Key][$ColName];
1644         $XIn[$Index] = $Index;
1645         $YIn[$Index] = $Value;
1646         if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1647         $Index++;
1648        }
1649       $Index--;
1650
1651       $Yt[0] = 0;
1652       $Yt[1] = 0;
1653       $U[1]  = 0;
1654       for($i=2;$i<=$Index-1;$i++)
1655        {
1656         $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1657         $p      = $Sig * $Yt[$i-1] + 2;
1658         $Yt[$i] = ($Sig - 1) / $p;
1659         $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1660         $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1661        }
1662
1663       $qn = 0;
1664       $un = 0;
1665       $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1666
1667       for($k=$Index-1;$k>=1;$k--)
1668        $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1669
1670       $Points   = "";
1671       $Points[] = $this->GAreaXOffset;
1672       $Points[] = $LayerHeight;
1673
1674       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1675       $C_White         =$this->AllocateColor($this->Layers[0],255,255,255);
1676       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1677       imagecolortransparent($this->Layers[0],$C_White);
1678
1679       $YLast = NULL;
1680       $XPos  = $this->GAreaXOffset; $PointsCount = 2;
1681       for($X=1;$X<=$Index;$X=$X+$Accuracy)
1682        {
1683         $klo = 1;
1684         $khi = $Index;
1685         $k   = $khi - $klo;
1686         while($k > 1)
1687          {
1688           $k = $khi - $klo;
1689           If ( $XIn[$k] >= $X )
1690            $khi = $k;
1691           else
1692            $klo = $k;
1693          }
1694         $klo = $khi - 1;
1695
1696         $h     = $XIn[$khi] - $XIn[$klo];
1697         $a     = ($XIn[$khi] - $X) / $h;
1698         $b     = ($X - $XIn[$klo]) / $h;
1699         $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1700
1701         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1702
1703         if ( $YLast != NULL && $AroundZero && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]))
1704          {
1705           $aPoints   = "";
1706           $aPoints[] = $XLast;
1707           $aPoints[] = $YLast;
1708           $aPoints[] = $XPos;
1709           $aPoints[] = $YPos;
1710           $aPoints[] = $XPos;
1711           $aPoints[] = $YZero;
1712           $aPoints[] = $XLast;
1713           $aPoints[] = $YZero;
1714
1715           $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1716           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1717          }
1718
1719         if ( !isset($Missing[floor($X)]) || $YLast == NULL )
1720          {
1721           $PointsCount++;
1722           $Points[] = $XPos;
1723           $Points[] = $YPos;
1724          }
1725         else
1726          {
1727           $PointsCount++; $Points[] = $XLast; $Points[] = $LayerHeight;
1728          }
1729
1730         $YLast = $YPos; $XLast = $XPos;
1731         $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
1732        }
1733
1734       // Add potentialy missing values
1735       $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1736       if ( $XPos < ($LayerWidth-$this->GAreaXOffset) )
1737        {
1738         $YPos = $LayerHeight - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1739
1740         if ( $YLast != NULL && $AroundZero )
1741          {
1742           $aPoints   = "";
1743           $aPoints[] = $XLast;
1744           $aPoints[] = $YLast;
1745           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1746           $aPoints[] = $YPos;
1747           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1748           $aPoints[] = $YZero;
1749           $aPoints[] = $XLast;
1750           $aPoints[] = $YZero;
1751
1752           $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1753           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1754          }
1755
1756         if ( $YIn[$klo] != "" && $YIn[$khi] != "" || $YLast == NULL )
1757          {
1758           $PointsCount++;
1759           $Points[] = $LayerWidth-$this->GAreaXOffset;
1760           $Points[] = $YPos;
1761          }
1762        }
1763
1764       $Points[] = $LayerWidth-$this->GAreaXOffset;
1765       $Points[] = $LayerHeight;
1766
1767       if ( !$AroundZero )
1768        {
1769         $C_Graph =$this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1770         imagefilledpolygon($this->Layers[0],$Points,$PointsCount,$C_Graph);
1771        }
1772
1773       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1774       imagedestroy($this->Layers[0]);
1775
1776       $this->drawCubicCurve($Data,$DataDescription,$Accuracy,$ColName);
1777
1778       $GraphID++;
1779      }
1780    }
1781
1782   /* This function draw a filled line graph */
1783   function drawFilledLineGraph($Data,$DataDescription,$Alpha=100,$AroundZero=FALSE)
1784    {
1785     $Empty = -2147483647;
1786
1787     /* Validate the Data and DataDescription array */
1788     $this->validateDataDescription("drawFilledLineGraph",$DataDescription);
1789     $this->validateData("drawFilledLineGraph",$Data);
1790
1791     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1792     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1793
1794     $GraphID = 0;
1795     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1796      {
1797       $ID = 0;
1798       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1799        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1800
1801       $aPoints   = "";
1802       $aPoints[] = $this->GAreaXOffset;
1803       $aPoints[] = $LayerHeight;
1804
1805       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1806       $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
1807       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1808       imagecolortransparent($this->Layers[0],$C_White);
1809
1810       $XPos  = $this->GAreaXOffset;
1811       $XLast = -1; $PointsCount = 2;
1812       $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1813       if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1814
1815       $YLast = $Empty;
1816       foreach ( $Data as $Key => $Values )
1817        {
1818         $Value = $Data[$Key][$ColName];
1819         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1820
1821         /* Save point into the image map if option activated */
1822         if ( $this->BuildMap )
1823          $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"FLine");
1824
1825         if ( !is_numeric($Value) )
1826          {
1827           $PointsCount++;
1828           $aPoints[] = $XLast;
1829           $aPoints[] = $LayerHeight;
1830
1831           $YLast = $Empty;
1832          }
1833         else
1834          {
1835           $PointsCount++;
1836           if ( $YLast <> $Empty )
1837            { $aPoints[] = $XPos; $aPoints[] = $YPos; }
1838           else
1839            { $PointsCount++; $aPoints[] = $XPos; $aPoints[] = $LayerHeight; $aPoints[] = $XPos; $aPoints[] = $YPos; }
1840
1841           if ($YLast <> $Empty && $AroundZero)
1842            {
1843             $Points   = "";
1844             $Points[] = $XLast; $Points[] = $YLast;
1845             $Points[] = $XPos;
1846             $Points[] = $YPos;
1847             $Points[] = $XPos;
1848             $Points[] = $YZero;
1849             $Points[] = $XLast;
1850             $Points[] = $YZero;
1851
1852             $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1853             imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1854            }
1855           $YLast = $YPos;
1856          }
1857
1858         $XLast = $XPos;
1859         $XPos  = $XPos + $this->DivisionWidth;
1860        }
1861       $aPoints[] = $LayerWidth - $this->GAreaXOffset;
1862       $aPoints[] = $LayerHeight;
1863
1864       if ( $AroundZero == FALSE )
1865        {
1866         $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1867         imagefilledpolygon($this->Layers[0],$aPoints,$PointsCount,$C_Graph);
1868        }
1869
1870       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1871       imagedestroy($this->Layers[0]);
1872       $GraphID++;
1873       $this->drawLineGraph($Data,$DataDescription,$ColName);
1874      }
1875    }
1876
1877   /* This function draw a bar graph */
1878   function drawOverlayBarGraph($Data,$DataDescription,$Alpha=50)
1879    {
1880     /* Validate the Data and DataDescription array */
1881     $this->validateDataDescription("drawOverlayBarGraph",$DataDescription);
1882     $this->validateData("drawOverlayBarGraph",$Data);
1883
1884     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1885     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1886
1887     $GraphID = 0;
1888     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1889      {
1890       $ID = 0;
1891       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1892        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1893
1894       $this->Layers[$GraphID] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1895       $C_White                = $this->AllocateColor($this->Layers[$GraphID],255,255,255);
1896       $C_Graph                = $this->AllocateColor($this->Layers[$GraphID],$this->Palette[$GraphID]["R"],$this->Palette[$GraphID]["G"],$this->Palette[$GraphID]["B"]);
1897       imagefilledrectangle($this->Layers[$GraphID],0,0,$LayerWidth,$LayerHeight,$C_White);
1898       imagecolortransparent($this->Layers[$GraphID],$C_White);
1899
1900       $XWidth = $this->DivisionWidth / 4;
1901       $XPos   = $this->GAreaXOffset;
1902       $YZero  = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1903       $XLast  = -1; $PointsCount = 2;
1904       foreach ( $Data as $Key => $Values )
1905        {
1906         if ( isset($Data[$Key][$ColName]) )
1907          {
1908           $Value = $Data[$Key][$ColName];
1909           if ( is_numeric($Value) )
1910            {
1911             $YPos  = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1912
1913             imagefilledrectangle($this->Layers[$GraphID],$XPos-$XWidth,$YPos,$XPos+$XWidth,$YZero,$C_Graph);
1914
1915             $X1 = floor($XPos - $XWidth + $this->GArea_X1); $Y1 = floor($YPos+$this->GArea_Y1) + .2;
1916             $X2 = floor($XPos + $XWidth + $this->GArea_X1); $Y2 = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1917             if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
1918             if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
1919
1920             /* Save point into the image map if option activated */
1921             if ( $this->BuildMap )
1922              $this->addToImageMap($X1,min($Y1,$Y2),$X2,max($Y1,$Y2),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"oBar");
1923
1924             $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1925            }
1926          }
1927         $XPos = $XPos + $this->DivisionWidth;
1928        }
1929
1930       $GraphID++;
1931      }
1932
1933     for($i=0;$i<=($GraphID-1);$i++)
1934      {
1935       imagecopymerge($this->Picture,$this->Layers[$i],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1936       imagedestroy($this->Layers[$i]);
1937      }
1938    }
1939
1940   /* This function draw a bar graph */
1941   function drawBarGraph($Data,$DataDescription,$Shadow=FALSE,$Alpha=100)
1942    {
1943     /* Validate the Data and DataDescription array */
1944     $this->validateDataDescription("drawBarGraph",$DataDescription);
1945     $this->validateData("drawBarGraph",$Data);
1946
1947     $GraphID      = 0;
1948     $Series       = count($DataDescription["Values"]);
1949     $SeriesWidth  = $this->DivisionWidth / ($Series+1);
1950     $SerieXOffset = $this->DivisionWidth / 2 - $SeriesWidth / 2;
1951
1952     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1953     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
1954
1955     $SerieID = 0;
1956     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1957      {
1958       $ID = 0;
1959       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1960        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1961
1962       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SerieXOffset + $SeriesWidth * $SerieID;
1963       $XLast = -1;
1964       foreach ( $Data as $Key => $Values )
1965        {
1966         if ( isset($Data[$Key][$ColName]))
1967          {
1968           if ( is_numeric($Data[$Key][$ColName]) )
1969            {
1970             $Value = $Data[$Key][$ColName];
1971             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1972
1973             /* Save point into the image map if option activated */
1974             if ( $this->BuildMap )
1975              {
1976               $this->addToImageMap($XPos+1,min($YZero,$YPos),$XPos+$SeriesWidth-1,max($YZero,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Bar");
1977              }
1978
1979             if ( $Shadow && $Alpha == 100 )
1980              $this->drawRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,25,25,25,TRUE,$Alpha);
1981
1982             $this->drawFilledRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
1983            }
1984          }
1985         $XPos = $XPos + $this->DivisionWidth;
1986        }
1987       $SerieID++;
1988      }
1989    }
1990
1991   /* This function draw a stacked bar graph */
1992   function drawStackedBarGraph($Data,$DataDescription,$Alpha=50,$Contiguous=FALSE)
1993    {
1994     /* Validate the Data and DataDescription array */
1995     $this->validateDataDescription("drawBarGraph",$DataDescription);
1996     $this->validateData("drawBarGraph",$Data);
1997
1998     $GraphID      = 0;
1999     $Series       = count($DataDescription["Values"]);
2000     if ( $Contiguous )
2001      $SeriesWidth  = $this->DivisionWidth;
2002     else
2003      $SeriesWidth  = $this->DivisionWidth * .8;
2004
2005     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
2006     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
2007
2008     $SerieID = 0; $LastValue = "";
2009     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2010      {
2011       $ID = 0;
2012       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2013        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2014
2015       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SeriesWidth / 2;
2016       $XLast = -1;
2017       foreach ( $Data as $Key => $Values )
2018        {
2019         if ( isset($Data[$Key][$ColName]))
2020          {
2021           if ( is_numeric($Data[$Key][$ColName]) )
2022            {
2023             $Value = $Data[$Key][$ColName];
2024
2025             if ( isset($LastValue[$Key]) )
2026              {
2027               $YPos    = $this->GArea_Y2 - ((($Value+$LastValue[$Key])-$this->VMin) * $this->DivisionRatio);
2028               $YBottom = $this->GArea_Y2 - (($LastValue[$Key]-$this->VMin) * $this->DivisionRatio);
2029               $LastValue[$Key] += $Value;
2030              }
2031             else
2032              {
2033               $YPos    = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
2034               $YBottom = $YZero;
2035               $LastValue[$Key] = $Value;
2036              }
2037
2038             /* Save point into the image map if option activated */
2039             if ( $this->BuildMap )
2040              $this->addToImageMap($XPos+1,min($YBottom,$YPos),$XPos+$SeriesWidth-1,max($YBottom,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"sBar");
2041
2042             $this->drawFilledRectangle($XPos+1,$YBottom,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
2043            }
2044          }
2045         $XPos = $XPos + $this->DivisionWidth;
2046        }
2047       $SerieID++;
2048      }
2049    }
2050
2051   /* This function draw a limits bar graphs */
2052   function drawLimitsGraph($Data,$DataDescription,$R=0,$G=0,$B=0)
2053    {
2054     /* Validate the Data and DataDescription array */
2055     $this->validateDataDescription("drawLimitsGraph",$DataDescription);
2056     $this->validateData("drawLimitsGraph",$Data);
2057
2058     $XWidth = $this->DivisionWidth / 4;
2059     $XPos   = $this->GArea_X1 + $this->GAreaXOffset;
2060
2061     foreach ( $Data as $Key => $Values )
2062      {
2063       $Min     = $Data[$Key][$DataDescription["Values"][0]];
2064       $Max     = $Data[$Key][$DataDescription["Values"][0]];
2065       $GraphID = 0; $MaxID = 0; $MinID = 0;
2066       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2067        {
2068         if ( isset($Data[$Key][$ColName]) )
2069          {
2070           if ( $Data[$Key][$ColName] > $Max && is_numeric($Data[$Key][$ColName]))
2071            { $Max = $Data[$Key][$ColName]; $MaxID = $GraphID; }
2072          }
2073         if ( isset($Data[$Key][$ColName]) && is_numeric($Data[$Key][$ColName]))
2074          {
2075           if ( $Data[$Key][$ColName] < $Min )
2076            { $Min = $Data[$Key][$ColName]; $MinID = $GraphID; }
2077           $GraphID++;
2078          }
2079        }
2080
2081       $YPos = $this->GArea_Y2 - (($Max-$this->VMin) * $this->DivisionRatio);
2082       $X1 = floor($XPos - $XWidth); $Y1 = floor($YPos) - .2;
2083       $X2 = floor($XPos + $XWidth);
2084       if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
2085       if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
2086
2087       $YPos = $this->GArea_Y2 - (($Min-$this->VMin) * $this->DivisionRatio);
2088       $Y2 = floor($YPos) + .2;
2089
2090       $this->drawLine(floor($XPos)-.2,$Y1+1,floor($XPos)-.2,$Y2-1,$R,$G,$B,TRUE);
2091       $this->drawLine(floor($XPos)+.2,$Y1+1,floor($XPos)+.2,$Y2-1,$R,$G,$B,TRUE);
2092       $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$MaxID]["R"],$this->Palette[$MaxID]["G"],$this->Palette[$MaxID]["B"],FALSE);
2093       $this->drawLine($X1,$Y2,$X2,$Y2,$this->Palette[$MinID]["R"],$this->Palette[$MinID]["G"],$this->Palette[$MinID]["B"],FALSE);
2094
2095       $XPos = $XPos + $this->DivisionWidth;
2096      }
2097    }
2098
2099   /* This function draw radar axis centered on the graph area */
2100   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)
2101    {
2102     /* Validate the Data and DataDescription array */
2103     $this->validateDataDescription("drawRadarAxis",$DataDescription);
2104     $this->validateData("drawRadarAxis",$Data);
2105
2106     $C_TextColor = $this->AllocateColor($this->Picture,$A_R,$A_G,$A_B);
2107
2108     /* Draw radar axis */
2109     $Points  = count($Data);
2110     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2111     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2112     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2113
2114     /* Search for the max value */
2115     if ( $MaxValue == -1 )
2116      {
2117       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2118        {
2119         foreach ( $Data as $Key => $Values )
2120          {
2121           if ( isset($Data[$Key][$ColName]))
2122            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2123          }
2124        }
2125      }
2126
2127     /* Draw the mosaic */
2128     if ( $Mosaic )
2129      {
2130       $RadiusScale = $Radius / $MaxValue;
2131       for ( $t=1; $t<=$MaxValue-1; $t++)
2132        {
2133         $TRadius  = $RadiusScale * $t;
2134         $LastX1   = -1;
2135
2136         for ( $i=0; $i<=$Points; $i++)
2137          {
2138           $Angle = -90 + $i * 360/$Points;
2139           $X1 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2140           $Y1 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2141           $X2 = cos($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $XCenter;
2142           $Y2 = sin($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $YCenter;
2143
2144           if ( $t % 2 == 1 && $LastX1 != -1)
2145            {
2146             $Plots   = "";
2147             $Plots[] = $X1; $Plots[] = $Y1;
2148             $Plots[] = $X2; $Plots[] = $Y2;
2149             $Plots[] = $LastX2; $Plots[] = $LastY2;
2150             $Plots[] = $LastX1; $Plots[] = $LastY1;
2151
2152             $C_Graph = $this->AllocateColor($this->Picture,250,250,250);
2153             imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_Graph);
2154            }
2155
2156           $LastX1 = $X1; $LastY1= $Y1;
2157           $LastX2 = $X2; $LastY2= $Y2;
2158          }
2159        }
2160      }
2161
2162
2163     /* Draw the spider web */
2164     for ( $t=1; $t<=$MaxValue; $t++)
2165      {
2166       $TRadius = ( $Radius / $MaxValue ) * $t;
2167       $LastX   = -1;
2168
2169       for ( $i=0; $i<=$Points; $i++)
2170        {
2171         $Angle = -90 + $i * 360/$Points;
2172         $X = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2173         $Y = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2174
2175         if ( $LastX != -1 )
2176          $this->drawDottedLine($LastX,$LastY,$X,$Y,4,$S_R,$S_G,$S_B);
2177
2178         $LastX = $X; $LastY= $Y;
2179        }
2180      }
2181
2182     /* Draw the axis */
2183     for ( $i=0; $i<=$Points; $i++)
2184      {
2185       $Angle = -90 + $i * 360/$Points;
2186       $X = cos($Angle * 3.1418 / 180 ) * $Radius + $XCenter;
2187       $Y = sin($Angle * 3.1418 / 180 ) * $Radius + $YCenter;
2188
2189       $this->drawLine($XCenter,$YCenter,$X,$Y,$A_R,$A_G,$A_B);
2190
2191       $XOffset = 0; $YOffset = 0;
2192       if (isset($Data[$i][$DataDescription["Position"]]))
2193        {
2194         $Label = $Data[$i][$DataDescription["Position"]];
2195
2196         $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Label);
2197         $Width  = $Positions[2] - $Positions[6];
2198         $Height = $Positions[3] - $Positions[7];
2199
2200         if ( $Angle >= 0 && $Angle <= 90 )
2201          $YOffset = $Height;
2202
2203         if ( $Angle > 90 && $Angle <= 180 )
2204          { $YOffset = $Height; $XOffset = -$Width; }
2205
2206         if ( $Angle > 180 && $Angle <= 270 )
2207          { $XOffset = -$Width; }
2208
2209         imagettftext($this->Picture,$this->FontSize,0,$X+$XOffset,$Y+$YOffset,$C_TextColor,$this->FontName,$Label);
2210        }
2211      }
2212
2213     /* Write the values */
2214     for ( $t=1; $t<=$MaxValue; $t++)
2215      {
2216       $TRadius = ( $Radius / $MaxValue ) * $t;
2217
2218       $Angle = -90 + 360 / $Points;
2219       $X1 = $XCenter;
2220       $Y1 = $YCenter - $TRadius;
2221       $X2 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
2222       $Y2 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
2223
2224       $XPos = floor(($X2-$X1)/2) + $X1;
2225       $YPos = floor(($Y2-$Y1)/2) + $Y1;
2226
2227       $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$t);
2228       $X = $XPos - ( $X+$Positions[2] - $X+$Positions[6] ) / 2;
2229       $Y = $YPos + $this->FontSize;
2230
2231       $this->drawFilledRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,240,240,240);
2232       $this->drawRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,220,220,220);
2233       imagettftext($this->Picture,$this->FontSize,0,$X,$Y,$C_TextColor,$this->FontName,$t);
2234      }
2235    }
2236
2237   /* This function draw a radar graph centered on the graph area */
2238   function drawRadar($Data,$DataDescription,$BorderOffset=10,$MaxValue=-1)
2239    {
2240     /* Validate the Data and DataDescription array */
2241     $this->validateDataDescription("drawRadar",$DataDescription);
2242     $this->validateData("drawRadar",$Data);
2243
2244     $Points  = count($Data);
2245     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2246     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
2247     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
2248
2249     /* Search for the max value */
2250     if ( $MaxValue == -1 )
2251      {
2252       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2253        {
2254         foreach ( $Data as $Key => $Values )
2255          {
2256           if ( isset($Data[$Key][$ColName]))
2257            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
2258          }
2259        }
2260      }
2261
2262     $GraphID = 0;
2263     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2264      {
2265       $ID = 0;
2266       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2267        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2268
2269       $Angle = -90;
2270       $XLast = -1;
2271       foreach ( $Data as $Key => $Values )
2272        {
2273         if ( isset($Data[$Key][$ColName]))
2274          {
2275           $Value    = $Data[$Key][$ColName];
2276           $Strength = ( $Radius / $MaxValue ) * $Value;
2277
2278           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2279           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2280
2281           if ( $XLast != -1 )
2282            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2283
2284           if ( $XLast == -1 )
2285            { $FirstX = $XPos; $FirstY = $YPos; }
2286
2287           $Angle = $Angle + (360/$Points);
2288           $XLast = $XPos;
2289           $YLast = $YPos;
2290          }
2291        }
2292       $this->drawLine($XPos,$YPos,$FirstX,$FirstY,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2293       $GraphID++;
2294      }
2295    }
2296
2297   /* This function draw a radar graph centered on the graph area */
2298   function drawFilledRadar($Data,$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1)
2299    {
2300     /* Validate the Data and DataDescription array */
2301     $this->validateDataDescription("drawFilledRadar",$DataDescription);
2302     $this->validateData("drawFilledRadar",$Data);
2303
2304     $Points      = count($Data);
2305     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
2306     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
2307     $Radius      = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
2308     $XCenter     = ( $this->GArea_X2 - $this->GArea_X1 ) / 2;
2309     $YCenter     = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2;
2310
2311     /* Search for the max value */
2312     if ( $MaxValue == -1 )
2313      {
2314       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2315        {
2316         foreach ( $Data as $Key => $Values )
2317          {
2318           if ( isset($Data[$Key][$ColName]))
2319            if ( $Data[$Key][$ColName] > $MaxValue && is_numeric($Data[$Key][$ColName])) { $MaxValue = $Data[$Key][$ColName]; }
2320          }
2321        }
2322      }
2323
2324     $GraphID = 0;
2325     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2326      {
2327       $ID = 0;
2328       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
2329        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
2330
2331       $Angle = -90;
2332       $XLast = -1;
2333       $Plots = "";
2334       foreach ( $Data as $Key => $Values )
2335        {
2336         if ( isset($Data[$Key][$ColName]))
2337          {
2338           $Value    = $Data[$Key][$ColName];
2339           if ( !is_numeric($Value) ) { $Value = 0; }
2340           $Strength = ( $Radius / $MaxValue ) * $Value;
2341
2342           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
2343           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
2344
2345           $Plots[] = $XPos;
2346           $Plots[] = $YPos;
2347
2348           $Angle = $Angle + (360/$Points);
2349           $XLast = $XPos;
2350           $YLast = $YPos;
2351          }
2352        }
2353
2354       if (isset($Plots[0]))
2355        {
2356         $Plots[] = $Plots[0];
2357         $Plots[] = $Plots[1];
2358
2359         $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
2360         $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
2361         imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
2362         imagecolortransparent($this->Layers[0],$C_White);
2363
2364         $C_Graph = $this->AllocateColor($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
2365         imagefilledpolygon($this->Layers[0],$Plots,(count($Plots)+1)/2,$C_Graph);
2366
2367         imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
2368         imagedestroy($this->Layers[0]);
2369
2370         for($i=0;$i<=count($Plots)-4;$i=$i+2)
2371          $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"]);
2372        }
2373
2374       $GraphID++;
2375      }
2376    }
2377
2378   /* This function draw a flat pie chart */
2379   function drawBasicPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0)
2380    {
2381     /* Validate the Data and DataDescription array */
2382     $this->validateDataDescription("drawBasicPieGraph",$DataDescription,FALSE);
2383     $this->validateData("drawBasicPieGraph",$Data);
2384
2385     /* Determine pie sum */
2386     $Series = 0; $PieSum = 0;
2387     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2388      {
2389       if ( $ColName != $DataDescription["Position"] )
2390        {
2391         $Series++;
2392         foreach ( $Data as $Key => $Values )
2393          {
2394           if ( isset($Data[$Key][$ColName]))
2395            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2396          }
2397        }
2398      }
2399
2400     /* Validate serie */
2401     if ( $Series != 1 )
2402      RaiseFatal("Pie chart can only accept one serie of data.");
2403
2404     $SpliceRatio         = 360 / $PieSum;
2405     $SplicePercent       = 100 / $PieSum;
2406
2407     /* Calculate all polygons */
2408     $Angle    = 0; $TopPlots = "";
2409     foreach($iValues as $Key => $Value)
2410      {
2411       $TopPlots[$Key][] = $XPos;
2412       $TopPlots[$Key][] = $YPos;
2413
2414       /* Process labels position & size */
2415       $Caption = "";
2416       if ( !($DrawLabels == PIE_NOLABEL) )
2417        {
2418         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2419         if ($DrawLabels == PIE_PERCENTAGE)
2420          $Caption  = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2421         elseif ($DrawLabels == PIE_LABELS)
2422          $Caption  = $iLabels[$Key];
2423         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2424          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2425         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2426          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2427
2428         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2429         $TextWidth  = $Position[2]-$Position[0];
2430         $TextHeight = abs($Position[1])+abs($Position[3]);
2431
2432         $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $XPos;
2433
2434         if ( $TAngle > 0 && $TAngle < 180 )
2435          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10) + $YPos + 4;
2436         else
2437          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+4) + $YPos - ($TextHeight/2);
2438
2439         if ( $TAngle > 90 && $TAngle < 270 )
2440          $TX = $TX - $TextWidth;
2441
2442         $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2443         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2444        }
2445
2446       /* Process pie slices */
2447       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2448        {
2449         $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
2450         $TopY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos;
2451
2452         $TopPlots[$Key][] = $TopX;
2453         $TopPlots[$Key][] = $TopY;
2454        }
2455
2456       $TopPlots[$Key][] = $XPos;
2457       $TopPlots[$Key][] = $YPos;
2458
2459       $Angle = $iAngle;
2460      }
2461     $PolyPlots = $TopPlots;
2462
2463     /* Set array values type to float --- PHP Bug with imagefilledpolygon casting to integer */
2464     foreach ($TopPlots as $Key => $Value)
2465      { foreach ($TopPlots[$Key] as $Key2 => $Value2) { settype($TopPlots[$Key][$Key2],"float"); } }
2466
2467     /* Draw Top polygons */
2468     foreach ($PolyPlots as $Key => $Value)
2469      {
2470       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2471       imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
2472      }
2473
2474     $this->drawCircle($XPos-.5,$YPos-.5,$Radius,$R,$G,$B);
2475     $this->drawCircle($XPos-.5,$YPos-.5,$Radius+.5,$R,$G,$B);
2476
2477     /* Draw Top polygons */
2478     foreach ($TopPlots as $Key => $Value)
2479      {
2480       for($j=0;$j<=count($TopPlots[$Key])-4;$j=$j+2)
2481        $this->drawLine($TopPlots[$Key][$j],$TopPlots[$Key][$j+1],$TopPlots[$Key][$j+2],$TopPlots[$Key][$j+3],$R,$G,$B);
2482      }
2483    }
2484
2485   function drawFlatPieGraphWithShadow($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0)
2486    {
2487     $this->drawFlatPieGraph($Data,$DataDescription,$XPos+$this->ShadowXDistance,$YPos+$this->ShadowYDistance,$Radius,PIE_NOLABEL,$SpliceDistance,$Decimals,TRUE);
2488     $this->drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius,$DrawLabels,$SpliceDistance,$Decimals,FALSE);
2489    }
2490
2491   /* This function draw a flat pie chart */
2492   function drawFlatPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals=0,$AllBlack=FALSE)
2493    {
2494     /* Validate the Data and DataDescription array */
2495     $this->validateDataDescription("drawFlatPieGraph",$DataDescription,FALSE);
2496     $this->validateData("drawFlatPieGraph",$Data);
2497
2498     $ShadowStatus = $this->ShadowActive ; $this->ShadowActive = FALSE;
2499
2500     /* Determine pie sum */
2501     $Series = 0; $PieSum = 0;
2502     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2503      {
2504       if ( $ColName != $DataDescription["Position"] )
2505        {
2506         $Series++;
2507         foreach ( $Data as $Key => $Values )
2508          {
2509           if ( isset($Data[$Key][$ColName]))
2510            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
2511          }
2512        }
2513      }
2514
2515     /* Validate serie */
2516     if ( $Series != 1 )
2517      {
2518       RaiseFatal("Pie chart can only accept one serie of data.");
2519       return(0);
2520      }
2521
2522     $SpliceRatio   = 360 / $PieSum;
2523     $SplicePercent = 100 / $PieSum;
2524
2525     /* Calculate all polygons */
2526     $Angle = 0; $TopPlots = "";
2527     foreach($iValues as $Key => $Value)
2528      {
2529       $XOffset = cos(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2530       $YOffset = sin(($Angle+($Value/2*$SpliceRatio)) * 3.1418 / 180 ) * $SpliceDistance;
2531
2532       $TopPlots[$Key][] = round($XPos + $XOffset);
2533       $TopPlots[$Key][] = round($YPos + $YOffset);
2534
2535       if ( $AllBlack )
2536        { $Rc = $this->ShadowRColor; $Gc = $this->ShadowGColor; $Bc = $this->ShadowBColor; }
2537       else
2538        { $Rc = $this->Palette[$Key]["R"]; $Gc = $this->Palette[$Key]["G"]; $Bc = $this->Palette[$Key]["B"]; }
2539
2540       $XLineLast = ""; $YLineLast = "";
2541
2542       /* Process labels position & size */
2543       $Caption = "";
2544       if ( !($DrawLabels == PIE_NOLABEL) )
2545        {
2546         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2547         if ($DrawLabels == PIE_PERCENTAGE)
2548          $Caption  = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2549         elseif ($DrawLabels == PIE_LABELS)
2550          $Caption  = $iLabels[$Key];
2551         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2552          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2553         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2554          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2555
2556         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2557         $TextWidth  = $Position[2]-$Position[0];
2558         $TextHeight = abs($Position[1])+abs($Position[3]);
2559
2560         $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $XPos;
2561
2562         if ( $TAngle > 0 && $TAngle < 180 )
2563          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $YPos + 4;
2564         else
2565          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+$SpliceDistance+4) + $YPos - ($TextHeight/2);
2566
2567         if ( $TAngle > 90 && $TAngle < 270 )
2568          $TX = $TX - $TextWidth;
2569
2570         $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2571         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2572        }
2573
2574       /* Process pie slices */
2575       if ( !$AllBlack )
2576        $LineColor = $this->AllocateColor($this->Picture,$Rc,$Gc,$Bc);
2577       else
2578        $LineColor = $this->AllocateColor($this->Picture,$Rc,$Gc,$Bc);
2579
2580       $XLineLast = ""; $YLineLast = "";
2581       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2582        {
2583         $PosX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos + $XOffset;
2584         $PosY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos + $YOffset;
2585
2586         $TopPlots[$Key][] = round($PosX); $TopPlots[$Key][] = round($PosY);
2587
2588         if ( $iAngle == $Angle || $iAngle == $Angle+$Value*$SpliceRatio || $iAngle +.5 > $Angle+$Value*$SpliceRatio)
2589          $this->drawLine($XPos+$XOffset,$YPos+$YOffset,$PosX,$PosY,$Rc,$Gc,$Bc);
2590
2591         if ( $XLineLast != "" )
2592          $this->drawLine($XLineLast,$YLineLast,$PosX,$PosY,$Rc,$Gc,$Bc);
2593
2594         $XLineLast = $PosX; $YLineLast = $PosY;
2595        }
2596
2597       $TopPlots[$Key][] = round($XPos + $XOffset);  $TopPlots[$Key][] = round($YPos + $YOffset);
2598
2599       $Angle = $iAngle;
2600      }
2601     $PolyPlots = $TopPlots;
2602
2603     /* Draw Top polygons */
2604     foreach ($PolyPlots as $Key => $Value)
2605      {
2606       if ( !$AllBlack )
2607        $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2608       else
2609        $C_GraphLo = $this->AllocateColor($this->Picture,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor);
2610
2611       imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
2612      }
2613     $this->ShadowActive = $ShadowStatus;
2614    }
2615
2616   /* This function draw a pseudo-3D pie chart */
2617   function drawPieGraph($Data,$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$EnhanceColors=TRUE,$Skew=60,$SpliceHeight=20,$SpliceDistance=0,$Decimals=0)
2618    {
2619     /* Validate the Data and DataDescription array */
2620     $this->validateDataDescription("drawPieGraph",$DataDescription,FALSE);
2621     $this->validateData("drawPieGraph",$Data);
2622
2623     /* Determine pie sum */
2624     $Series = 0; $PieSum = 0; $rPieSum = 0;
2625     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
2626      {
2627       if ( $ColName != $DataDescription["Position"] )
2628        {
2629         $Series++;
2630         foreach ( $Data as $Key => $Values )
2631          if ( isset($Data[$Key][$ColName]))
2632           {
2633            if ( $Data[$Key][$ColName] == 0 )
2634             { $iValues[] = 0; $rValues[] = 0; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; }
2635              // Removed : $PieSum++; $rValues[] = 1;
2636            else
2637             { $PieSum += $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; $rValues[] = $Data[$Key][$ColName]; $rPieSum += $Data[$Key][$ColName];}
2638           }
2639        }
2640      }
2641
2642     /* Validate serie */
2643     if ( $Series != 1 )
2644      RaiseFatal("Pie chart can only accept one serie of data.");
2645
2646     $SpliceDistanceRatio = $SpliceDistance;
2647     $SkewHeight          = ($Radius * $Skew) / 100;
2648     $SpliceRatio         = (360 - $SpliceDistanceRatio * count($iValues) ) / $PieSum;
2649     $SplicePercent       = 100 / $PieSum;
2650     $rSplicePercent      = 100 / $rPieSum;
2651
2652     /* Calculate all polygons */
2653     $Angle    = 0; $CDev = 5;
2654     $TopPlots = ""; $BotPlots = "";
2655     $aTopPlots = ""; $aBotPlots = "";
2656     foreach($iValues as $Key => $Value)
2657      {
2658       $XCenterPos = cos(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos;
2659       $YCenterPos = sin(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos;
2660       $XCenterPos2 = cos(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos;
2661       $YCenterPos2 = sin(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos;
2662
2663       $TopPlots[$Key][] = round($XCenterPos); $BotPlots[$Key][] = round($XCenterPos);
2664       $TopPlots[$Key][] = round($YCenterPos); $BotPlots[$Key][] = round($YCenterPos + $SpliceHeight);
2665       $aTopPlots[$Key][] = $XCenterPos; $aBotPlots[$Key][] = $XCenterPos;
2666       $aTopPlots[$Key][] = $YCenterPos; $aBotPlots[$Key][] = $YCenterPos + $SpliceHeight;
2667
2668       /* Process labels position & size */
2669       $Caption = "";
2670       if ( !($DrawLabels == PIE_NOLABEL) )
2671        {
2672         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2673         if ($DrawLabels == PIE_PERCENTAGE)
2674          $Caption  = (round($rValues[$Key] * pow(10,$Decimals) * $rSplicePercent)/pow(10,$Decimals))."%";
2675         elseif ($DrawLabels == PIE_LABELS)
2676          $Caption  = $iLabels[$Key];
2677         elseif ($DrawLabels == PIE_PERCENTAGE_LABEL)
2678          $Caption  = $iLabels[$Key]."\r\n".(round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
2679
2680         $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2681         $TextWidth  = $Position[2]-$Position[0];
2682         $TextHeight = abs($Position[1])+abs($Position[3]);
2683
2684         $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius + 10)+ $XPos;
2685
2686         if ( $TAngle > 0 && $TAngle < 180 )
2687          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 10) + $YPos + $SpliceHeight + 4;
2688         else
2689          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 4) + $YPos - ($TextHeight/2);
2690
2691         if ( $TAngle > 90 && $TAngle < 270 )
2692          $TX = $TX - $TextWidth;
2693
2694         $C_TextColor = $this->AllocateColor($this->Picture,70,70,70);
2695         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2696        }
2697
2698       /* Process pie slices */
2699       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2700        {
2701         $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
2702         $TopY = sin($iAngle * 3.1418 / 180 ) * $SkewHeight + $YPos;
2703
2704         $TopPlots[$Key][] = round($TopX); $BotPlots[$Key][] = round($TopX);
2705         $TopPlots[$Key][] = round($TopY); $BotPlots[$Key][] = round($TopY + $SpliceHeight);
2706         $aTopPlots[$Key][] = $TopX; $aBotPlots[$Key][] = $TopX;
2707         $aTopPlots[$Key][] = $TopY; $aBotPlots[$Key][] = $TopY + $SpliceHeight;
2708        }
2709
2710       $TopPlots[$Key][] = round($XCenterPos2); $BotPlots[$Key][] = round($XCenterPos2);
2711       $TopPlots[$Key][] = round($YCenterPos2); $BotPlots[$Key][] = round($YCenterPos2 + $SpliceHeight);
2712       $aTopPlots[$Key][] = $XCenterPos2; $aBotPlots[$Key][] = $XCenterPos2;
2713       $aTopPlots[$Key][] = $YCenterPos2; $aBotPlots[$Key][] = $YCenterPos2 + $SpliceHeight;
2714
2715       $Angle = $iAngle + $SpliceDistanceRatio;
2716      }
2717
2718     /* Draw Bottom polygons */
2719     foreach($iValues as $Key => $Value)
2720      {
2721       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-20);
2722       imagefilledpolygon($this->Picture,$BotPlots[$Key],(count($BotPlots[$Key])+1)/2,$C_GraphLo);
2723
2724       if ( $EnhanceColors ) { $En = -10; } else { $En = 0; }
2725
2726       for($j=0;$j<=count($aBotPlots[$Key])-4;$j=$j+2)
2727        $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);
2728      }
2729
2730     /* Draw pie layers */
2731     if ( $EnhanceColors ) { $ColorRatio = 30 / $SpliceHeight; } else { $ColorRatio = 25 / $SpliceHeight; }
2732     for($i=$SpliceHeight-1;$i>=1;$i--)
2733      {
2734       foreach($iValues as $Key => $Value)
2735        {
2736         $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-10);
2737         $Plots = ""; $Plot = 0;
2738         foreach($TopPlots[$Key] as $Key2 => $Value2)
2739          {
2740           $Plot++;
2741           if ( $Plot % 2 == 1 )
2742            $Plots[] = $Value2;
2743           else
2744            $Plots[] = $Value2+$i;
2745          }
2746         imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_GraphLo);
2747
2748         $Index       = count($Plots);
2749         if ($EnhanceColors ) {$ColorFactor = -20 + ($SpliceHeight - $i) * $ColorRatio; } else { $ColorFactor = 0; }
2750
2751         $this->drawAntialiasPixel($Plots[0],$Plots[1],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2752         $this->drawAntialiasPixel($Plots[2],$Plots[3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2753         $this->drawAntialiasPixel($Plots[$Index-4],$Plots[$Index-3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2754        }
2755      }
2756
2757     /* Draw Top polygons */
2758     for($Key=count($iValues)-1;$Key>=0;$Key--)
2759      {
2760       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2761       imagefilledpolygon($this->Picture,$TopPlots[$Key],(count($TopPlots[$Key])+1)/2,$C_GraphLo);
2762
2763       if ( $EnhanceColors ) { $En = 10; } else { $En = 0; }
2764       for($j=0;$j<=count($aTopPlots[$Key])-4;$j=$j+2)
2765        $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);
2766      }
2767    }
2768
2769   /* This function can be used to set the background color */
2770   function drawBackground($R,$G,$B)
2771    {
2772     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2773     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2774     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2775
2776     $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B);
2777     imagefilledrectangle($this->Picture,0,0,$this->XSize,$this->YSize,$C_Background);
2778    }
2779
2780   /* This function can be used to set the background color */
2781   function drawGraphAreaGradient($R,$G,$B,$Decay,$Target=TARGET_GRAPHAREA)
2782    {
2783     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2784     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2785     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2786
2787     if ( $Target == TARGET_GRAPHAREA )  { $X1 = $this->GArea_X1+1; $X2 = $this->GArea_X2-1; $Y1 = $this->GArea_Y1+1; $Y2 = $this->GArea_Y2; }
2788     if ( $Target == TARGET_BACKGROUND ) { $X1 = 0; $X2 = $this->XSize; $Y1 = 0; $Y2 = $this->YSize; }
2789
2790     /* Positive gradient */
2791     if ( $Decay > 0 )
2792      {
2793       $YStep = ($Y2 - $Y1 - 2) / $Decay;
2794       for($i=0;$i<=$Decay;$i++)
2795        {
2796         $R-=1;$G-=1;$B-=1;
2797         $Yi1 = $Y1 + ( $i * $YStep );
2798         $Yi2 = ceil( $Yi1 + ( $i * $YStep ) + $YStep );
2799         if ( $Yi2 >= $Yi2 ) { $Yi2 = $Y2-1; }
2800
2801         $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B);
2802         imagefilledrectangle($this->Picture,$X1,$Yi1,$X2,$Yi2,$C_Background);
2803        }
2804      }
2805
2806     /* Negative gradient */
2807     if ( $Decay < 0 )
2808      {
2809       $YStep = ($Y2 - $Y1 - 2) / -$Decay;
2810       $Yi1   = $Y1; $Yi2   = $Y1+$YStep;
2811       for($i=-$Decay;$i>=0;$i--)
2812        {
2813         $R+=1;$G+=1;$B+=1;
2814         $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B);
2815         imagefilledrectangle($this->Picture,$X1,$Yi1,$X2,$Yi2,$C_Background);
2816
2817         $Yi1+= $YStep;
2818         $Yi2+= $YStep;
2819         if ( $Yi2 >= $Yi2 ) { $Yi2 = $Y2-1; }
2820        }
2821      }
2822    }
2823
2824   /* This function create a rectangle with antialias */
2825   function drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B)
2826    {
2827     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2828     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2829     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2830
2831     $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2832
2833     $X1=$X1-.2;$Y1=$Y1-.2;
2834     $X2=$X2+.2;$Y2=$Y2+.2;
2835     $this->drawLine($X1,$Y1,$X2,$Y1,$R,$G,$B);
2836     $this->drawLine($X2,$Y1,$X2,$Y2,$R,$G,$B);
2837     $this->drawLine($X2,$Y2,$X1,$Y2,$R,$G,$B);
2838     $this->drawLine($X1,$Y2,$X1,$Y1,$R,$G,$B);
2839    }
2840
2841   /* This function create a filled rectangle with antialias */
2842   function drawFilledRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B,$DrawBorder=TRUE,$Alpha=100,$NoFallBack=FALSE)
2843    {
2844     if ( $X2 < $X1 ) { list($X1, $X2) = array($X2, $X1); }
2845     if ( $Y2 < $Y1 ) { list($Y1, $Y2) = array($Y2, $Y1); }
2846
2847     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2848     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2849     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2850
2851     if ( $Alpha == 100 )
2852      {
2853       /* Process shadows */
2854       if ( $this->ShadowActive && !$NoFallBack )
2855        {
2856         $this->drawFilledRectangle($X1+$this->ShadowXDistance,$Y1+$this->ShadowYDistance,$X2+$this->ShadowXDistance,$Y2+$this->ShadowYDistance,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,FALSE,$this->ShadowAlpha,TRUE);
2857         if ( $this->ShadowBlur != 0 )
2858          {
2859           $AlphaDecay = ($this->ShadowAlpha / $this->ShadowBlur);
2860
2861           for($i=1; $i<=$this->ShadowBlur; $i++)
2862            $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);
2863           for($i=1; $i<=$this->ShadowBlur; $i++)
2864            $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);
2865          }
2866        }
2867
2868       $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2869       imagefilledrectangle($this->Picture,round($X1),round($Y1),round($X2),round($Y2),$C_Rectangle);
2870      }
2871     else
2872      {
2873       $LayerWidth  = abs($X2-$X1)+2;
2874       $LayerHeight = abs($Y2-$Y1)+2;
2875
2876       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
2877       $C_White         = $this->AllocateColor($this->Layers[0],255,255,255);
2878       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
2879       imagecolortransparent($this->Layers[0],$C_White);
2880
2881       $C_Rectangle = $this->AllocateColor($this->Layers[0],$R,$G,$B);
2882       imagefilledrectangle($this->Layers[0],round(1),round(1),round($LayerWidth-1),round($LayerHeight-1),$C_Rectangle);
2883
2884       imagecopymerge($this->Picture,$this->Layers[0],round(min($X1,$X2)-1),round(min($Y1,$Y2)-1),0,0,$LayerWidth,$LayerHeight,$Alpha);
2885       imagedestroy($this->Layers[0]);
2886      }
2887
2888     if ( $DrawBorder )
2889      {
2890       $ShadowSettings = $this->ShadowActive; $this->ShadowActive = FALSE;
2891       $this->drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B);
2892       $this->ShadowActive = $ShadowSettings;
2893      }
2894    }
2895
2896   /* This function create a rectangle with rounded corners and antialias */
2897   function drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
2898    {
2899     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2900     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2901     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2902
2903     $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2904
2905     $Step = 90 / ((3.1418 * $Radius)/2);
2906
2907     for($i=0;$i<=90;$i=$i+$Step)
2908      {
2909       $X = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius;
2910       $Y = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius;
2911       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2912
2913       $X = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius;
2914       $Y = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius;
2915       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2916
2917       $X = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius;
2918       $Y = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius;
2919       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2920
2921       $X = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius;
2922       $Y = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius;
2923       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2924      }
2925
2926     $X1=$X1-.2;$Y1=$Y1-.2;
2927     $X2=$X2+.2;$Y2=$Y2+.2;
2928     $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B);
2929     $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B);
2930     $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B);
2931     $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B);
2932    }
2933
2934   /* This function create a filled rectangle with rounded corners and antialias */
2935   function drawFilledRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
2936    {
2937     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2938     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2939     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2940
2941     $C_Rectangle = $this->AllocateColor($this->Picture,$R,$G,$B);
2942
2943     $Step = 90 / ((3.1418 * $Radius)/2);
2944
2945     for($i=0;$i<=90;$i=$i+$Step)
2946      {
2947       $Xi1 = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius;
2948       $Yi1 = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius;
2949
2950       $Xi2 = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius;
2951       $Yi2 = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius;
2952
2953       $Xi3 = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius;
2954       $Yi3 = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius;
2955
2956       $Xi4 = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius;
2957       $Yi4 = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius;
2958
2959       imageline($this->Picture,$Xi1,$Yi1,$X1+$Radius,$Yi1,$C_Rectangle);
2960       imageline($this->Picture,$X2-$Radius,$Yi2,$Xi2,$Yi2,$C_Rectangle);
2961       imageline($this->Picture,$X2-$Radius,$Yi3,$Xi3,$Yi3,$C_Rectangle);
2962       imageline($this->Picture,$Xi4,$Yi4,$X1+$Radius,$Yi4,$C_Rectangle);
2963
2964       $this->drawAntialiasPixel($Xi1,$Yi1,$R,$G,$B);
2965       $this->drawAntialiasPixel($Xi2,$Yi2,$R,$G,$B);
2966       $this->drawAntialiasPixel($Xi3,$Yi3,$R,$G,$B);
2967       $this->drawAntialiasPixel($Xi4,$Yi4,$R,$G,$B);
2968      }
2969
2970     imagefilledrectangle($this->Picture,$X1,$Y1+$Radius,$X2,$Y2-$Radius,$C_Rectangle);
2971     imagefilledrectangle($this->Picture,$X1+$Radius,$Y1,$X2-$Radius,$Y2,$C_Rectangle);
2972
2973     $X1=$X1-.2;$Y1=$Y1-.2;
2974     $X2=$X2+.2;$Y2=$Y2+.2;
2975     $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B);
2976     $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B);
2977     $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B);
2978     $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B);
2979    }
2980
2981   /* This function create a circle with antialias */
2982   function drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
2983    {
2984     if ( $Width == 0 ) { $Width = $Height; }
2985     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2986     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2987     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2988
2989     $C_Circle = $this->AllocateColor($this->Picture,$R,$G,$B);
2990     $Step     = 360 / (2 * 3.1418 * max($Width,$Height));
2991
2992     for($i=0;$i<=360;$i=$i+$Step)
2993      {
2994       $X = cos($i*3.1418/180) * $Height + $Xc;
2995       $Y = sin($i*3.1418/180) * $Width + $Yc;
2996       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2997      }
2998    }
2999
3000   /* This function create a filled circle/ellipse with antialias */
3001   function drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
3002    {
3003     if ( $Width == 0 ) { $Width = $Height; }
3004     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3005     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3006     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3007
3008     $C_Circle = $this->AllocateColor($this->Picture,$R,$G,$B);
3009     $Step     = 360 / (2 * 3.1418 * max($Width,$Height));
3010
3011     for($i=90;$i<=270;$i=$i+$Step)
3012      {
3013       $X1 = cos($i*3.1418/180) * $Height + $Xc;
3014       $Y1 = sin($i*3.1418/180) * $Width + $Yc;
3015       $X2 = cos((180-$i)*3.1418/180) * $Height + $Xc;
3016       $Y2 = sin((180-$i)*3.1418/180) * $Width + $Yc;
3017
3018       $this->drawAntialiasPixel($X1-1,$Y1-1,$R,$G,$B);
3019       $this->drawAntialiasPixel($X2-1,$Y2-1,$R,$G,$B);
3020
3021       if ( ($Y1-1) > $Yc - max($Width,$Height) )
3022        imageline($this->Picture,$X1,$Y1-1,$X2-1,$Y2-1,$C_Circle);
3023      }
3024    }
3025
3026   /* This function will draw a filled ellipse */
3027   function drawEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
3028    { $this->drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); }
3029
3030   /* This function will draw an ellipse */
3031   function drawFilledEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
3032    { $this->drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); }
3033
3034   /* This function create a line with antialias */
3035   function drawLine($X1,$Y1,$X2,$Y2,$R,$G,$B,$GraphFunction=FALSE)
3036    {
3037     if ( $this->LineDotSize > 1 ) { $this->drawDottedLine($X1,$Y1,$X2,$Y2,$this->LineDotSize,$R,$G,$B,$GraphFunction); return(0); }
3038     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3039     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3040     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3041
3042     $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1));
3043     if ( $Distance == 0 )
3044      return(-1);
3045     $XStep = ($X2-$X1) / $Distance;
3046     $YStep = ($Y2-$Y1) / $Distance;
3047
3048     for($i=0;$i<=$Distance;$i++)
3049      {
3050       $X = $i * $XStep + $X1;
3051       $Y = $i * $YStep + $Y1;
3052
3053       if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction )
3054        {
3055         if ( $this->LineWidth == 1 )
3056          $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
3057         else
3058          {
3059           $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2);
3060           for($j=$StartOffset;$j<=$EndOffset;$j++)
3061            $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B);
3062          }
3063        }
3064      }
3065    }
3066
3067   /* This function create a line with antialias */
3068   function drawDottedLine($X1,$Y1,$X2,$Y2,$DotSize,$R,$G,$B,$GraphFunction=FALSE)
3069    {
3070     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3071     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3072     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3073
3074     $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1));
3075
3076     $XStep = ($X2-$X1) / $Distance;
3077     $YStep = ($Y2-$Y1) / $Distance;
3078
3079     $DotIndex = 0;
3080     for($i=0;$i<=$Distance;$i++)
3081      {
3082       $X = $i * $XStep + $X1;
3083       $Y = $i * $YStep + $Y1;
3084
3085       if ( $DotIndex <= $DotSize)
3086        {
3087         if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction )
3088          {
3089           if ( $this->LineWidth == 1 )
3090            $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
3091           else
3092            {
3093             $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2);
3094             for($j=$StartOffset;$j<=$EndOffset;$j++)
3095              $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B);
3096            }
3097          }
3098        }
3099
3100       $DotIndex++;
3101       if ( $DotIndex == $DotSize * 2 )
3102        $DotIndex = 0;
3103      }
3104    }
3105
3106   /* Load a PNG file and draw it over the chart */
3107   function drawFromPNG($FileName,$X,$Y,$Alpha=100)
3108    { $this->drawFromPicture(1,$FileName,$X,$Y,$Alpha); }
3109
3110   /* Load a GIF file and draw it over the chart */
3111   function drawFromGIF($FileName,$X,$Y,$Alpha=100)
3112    { $this->drawFromPicture(2,$FileName,$X,$Y,$Alpha); }
3113
3114   /* Load a JPEG file and draw it over the chart */
3115   function drawFromJPG($FileName,$X,$Y,$Alpha=100)
3116    { $this->drawFromPicture(3,$FileName,$X,$Y,$Alpha); }
3117
3118   /* Generic loader function for external pictures */
3119   function drawFromPicture($PicType,$FileName,$X,$Y,$Alpha=100)
3120    {
3121     if ( file_exists($FileName))
3122      {
3123       $Infos  = getimagesize($FileName);
3124       $Width  = $Infos[0];
3125       $Height = $Infos[1];
3126       if ( $PicType == 1 ) { $Raster = imagecreatefrompng($FileName); }
3127       if ( $PicType == 2 ) { $Raster = imagecreatefromgif($FileName); }
3128       if ( $PicType == 3 ) { $Raster = imagecreatefromjpeg($FileName); }
3129
3130       imagecopymerge($this->Picture,$Raster,$X,$Y,0,0,$Width,$Height,$Alpha);
3131       imagedestroy($Raster);
3132      }
3133    }
3134
3135   /* Draw an alpha pixel */
3136   function drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B)
3137    {
3138     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3139     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3140     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3141
3142     if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize )
3143      return(-1);
3144
3145     $RGB2 = imagecolorat($this->Picture, $X, $Y);
3146     $R2   = ($RGB2 >> 16) & 0xFF;
3147     $G2   = ($RGB2 >> 8) & 0xFF;
3148     $B2   = $RGB2 & 0xFF;
3149
3150     $iAlpha = (100 - $Alpha)/100;
3151     $Alpha  = $Alpha / 100;
3152
3153     $Ra   = floor($R*$Alpha+$R2*$iAlpha);
3154     $Ga   = floor($G*$Alpha+$G2*$iAlpha);
3155     $Ba   = floor($B*$Alpha+$B2*$iAlpha);
3156
3157     $C_Aliased = $this->AllocateColor($this->Picture,$Ra,$Ga,$Ba);
3158     imagesetpixel($this->Picture,$X,$Y,$C_Aliased);
3159    }
3160
3161   /* Color helper */
3162   function AllocateColor($Picture,$R,$G,$B,$Factor=0)
3163    {
3164     $R = $R + $Factor;
3165     $G = $G + $Factor;
3166     $B = $B + $Factor;
3167     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3168     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3169     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3170
3171     return(imagecolorallocate($Picture,$R,$G,$B));
3172    }
3173
3174   /* Add a border to the picture */
3175   function addBorder($Size=3,$R=0,$G=0,$B=0)
3176    {
3177     $Width  = $this->XSize+2*$Size;
3178     $Height = $this->YSize+2*$Size;
3179
3180     $Resampled    = imagecreatetruecolor($Width,$Height);
3181     $C_Background = $this->AllocateColor($Resampled,$R,$G,$B);
3182     imagefilledrectangle($Resampled,0,0,$Width,$Height,$C_Background);
3183
3184     imagecopy($Resampled,$this->Picture,$Size,$Size,0,0,$this->XSize,$this->YSize);
3185     imagedestroy($this->Picture);
3186
3187     $this->XSize = $Width;
3188     $this->YSize = $Height;
3189
3190     $this->Picture = imagecreatetruecolor($this->XSize,$this->YSize);
3191     $C_White = $this->AllocateColor($this->Picture,255,255,255);
3192     imagefilledrectangle($this->Picture,0,0,$this->XSize,$this->YSize,$C_White);
3193     imagecolortransparent($this->Picture,$C_White);
3194     imagecopy($this->Picture,$Resampled,0,0,0,0,$this->XSize,$this->YSize);
3195    }
3196
3197   /* Render the current picture to a file */
3198   function Render($FileName)
3199    {
3200     if ( $this->ErrorReporting )
3201      $this->printErrors($this->ErrorInterface);
3202
3203     /* Save image map if requested */
3204     if ( $this->BuildMap )
3205      $this->SaveImageMap();
3206
3207     imagepng($this->Picture,$FileName);
3208    }
3209
3210   /* Render the current picture to STDOUT */
3211   function Stroke()
3212    {
3213     if ( $this->ErrorReporting )
3214      $this->printErrors("GD");
3215
3216     /* Save image map if requested */
3217     if ( $this->BuildMap )
3218      $this->SaveImageMap();
3219
3220     header('Content-type: image/png');
3221     imagepng($this->Picture);
3222    }
3223
3224   /* Private functions for internal processing */
3225   function drawAntialiasPixel($X,$Y,$R,$G,$B,$Alpha=100,$NoFallBack=FALSE)
3226    {
3227     /* Process shadows */
3228     if ( $this->ShadowActive && !$NoFallBack )
3229      {
3230       $this->drawAntialiasPixel($X+$this->ShadowXDistance,$Y+$this->ShadowYDistance,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,$this->ShadowAlpha,TRUE);
3231       if ( $this->ShadowBlur != 0 )
3232        {
3233         $AlphaDecay = ($this->ShadowAlpha / $this->ShadowBlur);
3234
3235         for($i=1; $i<=$this->ShadowBlur; $i++)
3236          $this->drawAntialiasPixel($X+$this->ShadowXDistance-$i/2,$Y+$this->ShadowYDistance-$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
3237         for($i=1; $i<=$this->ShadowBlur; $i++)
3238          $this->drawAntialiasPixel($X+$this->ShadowXDistance+$i/2,$Y+$this->ShadowYDistance+$i/2,$this->ShadowRColor,$this->ShadowGColor,$this->ShadowBColor,$this->ShadowAlpha-$AlphaDecay*$i,TRUE);
3239        }
3240      }
3241
3242     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
3243     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
3244     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
3245
3246     $Plot = "";
3247     $Xi   = floor($X);
3248     $Yi   = floor($Y);
3249
3250     if ( $Xi == $X && $Yi == $Y)
3251      {
3252       if ( $Alpha == 100 )
3253        {
3254         $C_Aliased = $this->AllocateColor($this->Picture,$R,$G,$B);
3255         imagesetpixel($this->Picture,$X,$Y,$C_Aliased);
3256        }
3257       else
3258        $this->drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B);
3259      }
3260     else
3261      {
3262       $Alpha1 = (((1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
3263       if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); }
3264
3265       $Alpha2 = ((($X - floor($X)) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
3266       if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); }
3267
3268       $Alpha3 = (((1 - ($X - floor($X))) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
3269       if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); }
3270
3271       $Alpha4 = ((($X - floor($X)) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
3272       if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); }
3273      }
3274    }
3275
3276   /* Validate data contained in the description array */
3277   function validateDataDescription($FunctionName,&$DataDescription,$DescriptionRequired=TRUE)
3278    {
3279     if (!isset($DataDescription["Position"]))
3280      {
3281       $this->Errors[] = "[Warning] ".$FunctionName." - Y Labels are not set.";
3282       $DataDescription["Position"] = "Name";
3283      }
3284
3285     if ( $DescriptionRequired )
3286      {
3287       if (!isset($DataDescription["Description"]))
3288        {
3289         $this->Errors[] = "[Warning] ".$FunctionName." - Series descriptions are not set.";
3290         foreach($DataDescription["Values"] as $key => $Value)
3291          {
3292           $DataDescription["Description"][$Value] = $Value;
3293          }
3294        }
3295
3296       if (count($DataDescription["Description"]) < count($DataDescription["Values"]))
3297        {
3298         $this->Errors[] = "[Warning] ".$FunctionName." - Some series descriptions are not set.";
3299         foreach($DataDescription["Values"] as $key => $Value)
3300          {
3301           if ( !isset($DataDescription["Description"][$Value]))
3302            $DataDescription["Description"][$Value] = $Value;
3303          }
3304        }
3305      }
3306    }
3307
3308   /* Validate data contained in the data array */
3309   function validateData($FunctionName,&$Data)
3310    {
3311     $DataSummary = array();
3312
3313     foreach($Data as $key => $Values)
3314      {
3315       foreach($Values as $key2 => $Value)
3316        {
3317         if (!isset($DataSummary[$key2]))
3318          $DataSummary[$key2] = 1;
3319         else
3320          $DataSummary[$key2]++;
3321        }
3322      }
3323
3324     if ( max($DataSummary) == 0 )
3325      $this->Errors[] = "[Warning] ".$FunctionName." - No data set.";
3326
3327     foreach($DataSummary as $key => $Value)
3328      {
3329       if ($Value < max($DataSummary))
3330        {
3331         $this->Errors[] = "[Warning] ".$FunctionName." - Missing data in serie ".$key.".";
3332        }
3333      }
3334    }
3335
3336   /* Print all error messages on the CLI or graphically */
3337   function printErrors($Mode="CLI")
3338    {
3339     if (count($this->Errors) == 0)
3340      return(0);
3341
3342     if ( $Mode == "CLI" )
3343      {
3344       foreach($this->Errors as $key => $Value)
3345        echo $Value."\r\n";
3346      }
3347     elseif ( $Mode == "GD" )
3348      {
3349       $this->setLineStyle($Width=1);
3350       $MaxWidth = 0;
3351       foreach($this->Errors as $key => $Value)
3352        {
3353         $Position  = imageftbbox($this->ErrorFontSize,0,$this->ErrorFontName,$Value);
3354         $TextWidth = $Position[2]-$Position[0];
3355         if ( $TextWidth > $MaxWidth ) { $MaxWidth = $TextWidth; }
3356        }
3357       $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);
3358       $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);
3359
3360       $C_TextColor = $this->AllocateColor($this->Picture,133,85,85);
3361       $YPos        = $this->YSize - (18 + (count($this->Errors)-1) * ($this->ErrorFontSize + 4));
3362       foreach($this->Errors as $key => $Value)
3363        {
3364         imagettftext($this->Picture,$this->ErrorFontSize,0,$this->XSize-($MaxWidth+15),$YPos,$C_TextColor,$this->ErrorFontName,$Value);
3365         $YPos = $YPos + ($this->ErrorFontSize + 4);
3366        }
3367      }
3368    }
3369
3370   /* Activate the image map creation process */
3371   function setImageMap($Mode=TRUE,$GraphID="MyGraph")
3372    {
3373     $this->BuildMap = $Mode;
3374     $this->MapID    = $GraphID;
3375    }
3376
3377   /* Add a box into the image map */
3378   function addToImageMap($X1,$Y1,$X2,$Y2,$SerieName,$Value,$CallerFunction)
3379    {
3380     if ( $this->MapFunction == NULL || $this->MapFunction == $CallerFunction )
3381      {
3382       $this->ImageMap[]  = round($X1).",".round($Y1).",".round($X2).",".round($Y2).",".$SerieName.",".$Value;
3383       $this->MapFunction = $CallerFunction;
3384      }
3385    }
3386
3387   /* Load and cleanup the image map from disk */
3388   function getImageMap($MapName,$Flush=TRUE)
3389    {
3390     /* Strip HTML query strings */
3391     $Values   = $this->tmpFolder.$MapName;
3392     $Value    = split("\?",$Values);
3393     $FileName = $Value[0];
3394
3395     if ( file_exists($FileName) )
3396      {
3397       $Handle     = fopen($FileName, "r");
3398       $MapContent = fread($Handle, filesize($FileName));
3399       fclose($Handle);
3400       echo $MapContent;
3401
3402       if ( $Flush )
3403        unlink($FileName);
3404
3405       exit();
3406      }
3407     else
3408      {
3409       header("HTTP/1.0 404 Not Found");
3410       exit();
3411      }
3412    }
3413
3414   /* Save the image map to the disk */
3415   function SaveImageMap()
3416    {
3417     if ( !$this->BuildMap ) { return(-1); }
3418
3419     if ( $this->ImageMap == NULL )
3420      {
3421       $this->Errors[] = "[Warning] SaveImageMap - Image map is empty.";
3422       return(-1);
3423      }
3424
3425     $Handle = fopen($this->tmpFolder.$this->MapID, 'w');
3426     if ( !$Handle )
3427      {
3428       $this->Errors[] = "[Warning] SaveImageMap - Cannot save the image map.";
3429       return(-1);
3430      }
3431     else
3432      {
3433       foreach($this->ImageMap as $Key => $Value)
3434        fwrite($Handle, htmlentities($Value)."\r");
3435      }
3436     fclose ($Handle);
3437    }
3438
3439   /* Convert seconds to a time format string */
3440   function ToTime($Value)
3441    {
3442     $Hour   = floor($Value/3600);
3443     $Minute = floor(($Value - $Hour*3600)/60);
3444     $Second = floor($Value - $Hour*3600 - $Minute*60);
3445
3446     if (strlen($Hour) == 1 )   { $Hour = "0".$Hour; }
3447     if (strlen($Minute) == 1 ) { $Minute = "0".$Minute; }
3448     if (strlen($Second) == 1 ) { $Second = "0".$Second; }
3449
3450     return($Hour.":".$Minute.":".$Second);
3451    }
3452
3453   /* Convert to metric system */
3454   function ToMetric($Value)
3455    {
3456     $Go = floor($Value/1000000000);
3457     $Mo = floor(($Value - $Go*1000000000)/1000000);
3458     $Ko = floor(($Value - $Go*1000000000 - $Mo*1000000)/1000);
3459     $o  = floor($Value - $Go*1000000000 - $Mo*1000000 - $Ko*1000);
3460
3461     if ($Go != 0)   { return($Go.".".$Mo."g"); }
3462     if ($Mo != 0)   { return($Mo.".".$ko."m"); }
3463     if ($Ko != 0)   { return($Ko.".".$o)."k"; }
3464     return($o);
3465    }
3466
3467   /* Convert to curency */
3468   function ToCurrency($Value)
3469    {
3470     $Go = floor($Value/1000000000);
3471     $Mo = floor(($Value - $Go*1000000000)/1000000);
3472     $Ko = floor(($Value - $Go*1000000000 - $Mo*1000000)/1000);
3473     $o  = floor($Value - $Go*1000000000 - $Mo*1000000 - $Ko*1000);
3474
3475     if ( strlen($o) == 1 ) { $o = "00".$o; }
3476     if ( strlen($o) == 2 ) { $o = "0".$o; }
3477
3478     $ResultString = $o;
3479     if ( $Ko != 0 ) { $ResultString = $Ko.".".$ResultString; }
3480     if ( $Mo != 0 ) { $ResultString = $Mo.".".$ResultString; }
3481     if ( $Go != 0 ) { $ResultString = $Go.".".$ResultString; }
3482
3483     $ResultString = $this->Currency.$ResultString;
3484     return($ResultString);
3485    }
3486
3487   /* Set date format for axis labels */
3488   function setDateFormat($Format)
3489    {
3490     $this->DateFormat = $Format;
3491    }
3492
3493   /* Convert TS to a date format string */
3494   function ToDate($Value)
3495    {
3496     return(date($this->DateFormat,$Value));
3497    }
3498
3499   /* Check if a number is a full integer (for scaling) */
3500   function isRealInt($Value)
3501    {
3502     if ($Value == floor($Value))
3503      return(TRUE);
3504     return(FALSE);
3505    }
3506  }
3507
3508 function RaiseFatal($Message)
3509  {
3510   echo "[FATAL] ".$Message."\r\n";
3511   exit();
3512  }
3513?>