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