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