Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
103 - 1
<?php
2
 /*
3
     pChart - a PHP class to build charts!
4
     Copyright (C) 2008 Jean-Damien POGOLOTTI
5
     Version  1.27 last updated on 08/17/08
6
 
7
     http://pchart.sourceforge.net
8
 
9
     This program is free software: you can redistribute it and/or modify
10
     it under the terms of the GNU General Public License as published by
11
     the Free Software Foundation, either version 1,2,3 of the License, or
12
     (at your option) any later version.
13
 
14
     This program is distributed in the hope that it will be useful,
15
     but WITHOUT ANY WARRANTY; without even the implied warranty of
16
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
     GNU General Public License for more details.
18
 
19
     You should have received a copy of the GNU General Public License
20
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 
22
     Class initialisation :
23
      pChart($XSize,$YSize)
24
     Draw methods :
25
      drawBackground($R,$G,$B)
26
      drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B)
27
      drawFilledRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B,$DrawBorder=TRUE,$Alpha=100)
28
      drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
29
      drawFilledRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
30
      drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
31
      drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
32
      drawEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
33
      drawFilledEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
34
      drawLine($X1,$Y1,$X2,$Y2,$R,$G,$B,$GraphFunction=FALSE)
35
      drawDottedLine($X1,$Y1,$X2,$Y2,$DotSize,$R,$G,$B)
36
      drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B)
37
      drawFromPNG($FileName,$X,$Y,$Alpha=100)
38
      drawFromGIF($FileName,$X,$Y,$Alpha=100)
39
      drawFromJPG($FileName,$X,$Y,$Alpha=100)
40
     Graph setup methods :
41
      drawGraphArea($R,$G,$B,$Stripe=FALSE)
42
      drawScale(&$Data,&$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1)
43
      drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=100)
44
      drawLegend($XPos,$YPos,&$DataDescription,$R,$G,$B)
45
      drawPieLegend($XPos,$YPos,$Data,$DataDescription,$R,$G,$B)
46
      drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2=-1,$YPos2=-1)
47
      drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4)
48
      drawArea(&$Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50)
49
      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)
50
      getLegendBoxSize($DataDescription)
51
      loadColorPalette($FileName,$Delimiter=",")
52
      reportWarnings($Interface="CLI")
53
      setGraphArea($X1,$Y1,$X2,$Y2)
54
      setFixedScale($VMin,$VMax)
55
      setLabel(&$Data,&$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210)
56
      setColorPalette($ID,$R,$G,$B)
57
      setDateFormat($Format)
58
      setFontProperties($FontName,$FontSize)
59
      setLineStyle($Width=1,$DotSize=0)
60
      setFixedScale($VMin,$VMax,$Divisions=5)
61
      writeValues(&$Data,&$DataDescription,$Series)
62
    Graphs methods :
63
      drawPlotGraph(&$Data,&$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1)
64
      drawLineGraph(&$Data,&$DataDescription,$SerieName="")
65
      drawFilledLineGraph(&$Data,&$DataDescription,$Alpha=100,$AroundZero=FALSE)
66
      drawCubicCurve(&$Data,&$DataDescription,$Accuracy=.1,$SerieName="")
67
      drawFilledCubicCurve(&$Data,&$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE)
68
      drawOverlayBarGraph(&$Data,&$DataDescription,$Alpha=50)
69
      drawBarGraph(&$Data,&$DataDescription,$Shadow=FALSE)
70
      drawStackedBarGraph(&$Data,&$DataDescription,$Alpha=50)
71
      drawLimitsGraph(&$Data,&$DataDescription,$R=0,$G=0,$B=0)
72
      drawRadar(&$Data,&$DataDescription,$BorderOffset=10,$MaxValue=-1)
73
      drawFilledRadar(&$Data,&$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1)
74
      drawBasicPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0)
75
      drawFlatPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals = 0)
76
      drawPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$EnhanceColors=TRUE,$Skew=60,$SpliceHeight=20,$SpliceDistance=0,$Decimals=0)
77
     Other methods :
78
      Render($FileName)
79
      Stroke()
80
 */
81
 
82
 /* Declare some script wide constants */
83
 define("SCALE_NORMAL",1);
84
 define("SCALE_ADDALL",2);
85
 define("SCALE_START0",3);
86
 define("SCALE_ADDALLSTART0",4);
87
 define("PIE_PERCENTAGE", 1);
88
 define("PIE_LABELS",2);
89
 define("PIE_NOLABEL",3);
90
 
91
 /* pChart class definition */
92
 class pChart
93
  {
94
   /* Palettes definition */
95
   var $Palette = array("0"=>array("R"=>188,"G"=>224,"B"=>46),
96
                        "1"=>array("R"=>224,"G"=>100,"B"=>46),
97
                        "2"=>array("R"=>224,"G"=>214,"B"=>46),
98
                        "3"=>array("R"=>46,"G"=>151,"B"=>224),
99
                        "4"=>array("R"=>176,"G"=>46,"B"=>224),
100
                        "5"=>array("R"=>224,"G"=>46,"B"=>117),
101
                        "6"=>array("R"=>92,"G"=>224,"B"=>46),
102
                        "7"=>array("R"=>224,"G"=>176,"B"=>46));
103
 
104
   /* Some static vars used in the class */
105
   var $XSize          = NULL;
106
   var $YSize          = NULL;
107
   var $Picture        = NULL;
108
 
109
   /* Error management */
110
   var $ErrorReporting = FALSE;
111
   var $ErrorInterface = "CLI";
112
   var $Errors         = NULL;
113
   var $ErrorFontName  = "fonts/tahoma.ttf";
114
   var $ErrorFontSize  = 6;
115
 
116
   /* vars related to the graphing area */
117
   var $GArea_X1       = NULL;
118
   var $GArea_Y1       = NULL;
119
   var $GArea_X2       = NULL;
120
   var $GArea_Y2       = NULL;
121
   var $GAreaXOffset   = NULL;
122
   var $VMax           = NULL;
123
   var $VMin           = NULL;
124
   var $Divisions      = NULL;
125
   var $DivisionHeight = NULL;
126
   var $DivisionCount  = NULL;
127
   var $DivisionRatio  = NULL;
128
   var $DivisionWidth  = NULL;
129
   var $DataCount      = NULL;
130
 
131
   /* Text format related vars */
132
   var $FontName       = NULL;
133
   var $FontSize       = NULL;
134
   var $DateFormat     = "d/m/Y";
135
 
136
   /* Lines format related vars */
137
   var $LineWidth      = 1;
138
   var $LineDotSize    = 0;
139
 
140
   /* Layer related vars */
141
   var $Layers         = NULL;
142
 
143
   /* Set antialias quality : 0 is maximum, 100 minimum*/
144
   var $AntialiasQuality = 10;
145
 
146
   /* This function create the background picture */
147
   function __construct($XSize,$YSize)
148
    {
149
     $this->XSize   = $XSize;
150
     $this->YSize   = $YSize;
151
     $this->Picture = imagecreatetruecolor($XSize,$YSize);
152
 
153
     $C_White = imagecolorallocate($this->Picture,255,255,255);
154
     imagefilledrectangle($this->Picture,0,0,$XSize,$YSize,$C_White);
155
     imagecolortransparent($this->Picture,$C_White);
156
 
157
     $this->setFontProperties("tahoma.ttf",8);
158
    }
159
 
160
  /* Set if warnings should be reported */
161
  function reportWarnings($Interface="CLI")
162
   {
163
    $this->ErrorReporting = TRUE;
164
    $this->ErrorInterface = $Interface;
165
    }
166
 
167
   /* Set the font properties */
168
   function setFontProperties($FontName,$FontSize)
169
    {
170
     $this->FontName = $FontName;
171
     $this->FontSize = $FontSize;
172
    }
173
 
174
   /* Set Palette color */
175
   function setColorPalette($ID,$R,$G,$B)
176
    {
177
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
178
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
179
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
180
 
181
     $this->Palette[$ID]["R"] = $R;
182
     $this->Palette[$ID]["G"] = $G;
183
     $this->Palette[$ID]["B"] = $B;
184
    }
185
 
186
   /* Load Color Palette from file */
187
   function loadColorPalette($FileName,$Delimiter=",")
188
    {
189
     $handle  = @fopen($FileName,"r");
190
     $ColorID = 0;
191
     if ($handle)
192
      {
193
       while (!feof($handle))
194
        {
195
         $buffer = fgets($handle, 4096);
196
         $buffer = str_replace(chr(10),"",$buffer);
197
         $buffer = str_replace(chr(13),"",$buffer);
198
         $Values = split($Delimiter,$buffer);
199
         if ( count($Values) == 3 )
200
          {
201
           $this->Palette[$ColorID]["R"] = $Values[0];
202
           $this->Palette[$ColorID]["G"] = $Values[1];
203
           $this->Palette[$ColorID]["B"] = $Values[2];
204
           $ColorID++;
205
          }
206
        }
207
      }
208
    }
209
 
210
   /* Set line style */
211
  function setLineStyle($Width=1,$DotSize=0)
212
   {
213
    $this->LineWidth   = $Width;
214
    $this->LineDotSize = $DotSize;
215
   }
216
 
217
   /* Set the graph area location */
218
   function setGraphArea($X1,$Y1,$X2,$Y2)
219
    {
220
     $this->GArea_X1 = $X1;
221
     $this->GArea_Y1 = $Y1;
222
     $this->GArea_X2 = $X2;
223
     $this->GArea_Y2 = $Y2;
224
    }
225
 
226
   /* Prepare the graph area */
227
   function drawGraphArea($R,$G,$B,$Stripe=FALSE)
228
    {
229
     $this->drawFilledRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B,FALSE);
230
     $this->drawRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R-40,$G-40,$B-40);
231
 
232
     if ( $Stripe )
233
      {
234
       $R2 = $R-15; if ( $R2 < 0 ) { $R2 = 0; }
235
       $G2 = $R-15; if ( $G2 < 0 ) { $G2 = 0; }
236
       $B2 = $R-15; if ( $B2 < 0 ) { $B2 = 0; }
237
 
238
       $LineColor = imagecolorallocate($this->Picture,$R2,$G2,$B2);
239
       $SkewWidth = $this->GArea_Y2-$this->GArea_Y1-1;
240
 
241
       for($i=$this->GArea_X1-$SkewWidth;$i<=$this->GArea_X2;$i=$i+4)
242
        {
243
         $X1 = $i;            $Y1 = $this->GArea_Y2;
244
         $X2 = $i+$SkewWidth; $Y2 = $this->GArea_Y1;
245
 
246
 
247
         if ( $X1 < $this->GArea_X1 )
248
          { $X1 = $this->GArea_X1; $Y1 = $this->GArea_Y1 + $X2 - $this->GArea_X1 + 1; }
249
 
250
         if ( $X2 >= $this->GArea_X2 )
251
          { $Y2 = $this->GArea_Y1 + $X2 - $this->GArea_X2 +1; $X2 = $this->GArea_X2 - 1; }
252
// * Fixed in 1.27 *         { $X2 = $this->GArea_X2 - 1; $Y2 = $this->GArea_Y2 - ($this->GArea_X2 - $X1); }
253
 
254
         imageline($this->Picture,$X1,$Y1,$X2,$Y2+1,$LineColor);
255
        }
256
      }
257
    }
258
 
259
   /* Allow you to fix the scale, use this to bypass the automatic scaling */
260
   function setFixedScale($VMin,$VMax,$Divisions=5)
261
    {
262
     $this->VMin      = $VMin;
263
     $this->VMax      = $VMax;
264
     $this->Divisions = $Divisions;
265
    }
266
 
267
   /* Compute and draw the scale */
268
   function drawScale(&$Data,&$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1)
269
    {
270
     /* Validate the Data and DataDescription array */
271
     $this->validateData("drawScale",$Data);
272
 
273
     $C_TextColor         = imagecolorallocate($this->Picture,$R,$G,$B);
274
 
275
     $this->drawLine($this->GArea_X1,$this->GArea_Y1,$this->GArea_X1,$this->GArea_Y2,$R,$G,$B);
276
     $this->drawLine($this->GArea_X1,$this->GArea_Y2,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B);
277
 
278
     if ( $this->VMin == NULL && $this->VMax == NULL)
279
      {
280
       if (isset($DataDescription["Values"][0]))
281
        {
282
         $this->VMin = $Data[0][$DataDescription["Values"][0]];
283
         $this->VMax = $Data[0][$DataDescription["Values"][0]];
284
        }
285
       else { $this->VMin = 2147483647; $this->VMax = -2147483647; }
286
 
287
       /* Compute Min and Max values */
288
       if ( $ScaleMode == SCALE_NORMAL || $ScaleMode == SCALE_START0 )
289
        {
290
         if ( $ScaleMode == SCALE_START0 ) { $this->VMin = 0; }
291
 
292
         foreach ( $Data as $Key => $Values )
293
          {
294
           foreach ( $DataDescription["Values"] as $Key2 => $ColName )
295
            {
296
             if (isset($Data[$Key][$ColName]))
297
              {
298
               $Value = $Data[$Key][$ColName];
299
 
300
               if ( is_numeric($Value) )
301
                {
302
                 if ( $this->VMax < $Value) { $this->VMax = $Value; }
303
                 if ( $this->VMin > $Value) { $this->VMin = $Value; }
304
                }
305
              }
306
            }
307
          }
308
        }
309
       elseif ( $ScaleMode == SCALE_ADDALL || $ScaleMode == SCALE_ADDALLSTART0 ) /* Experimental */
310
        {
311
         if ( $ScaleMode == SCALE_ADDALLSTART0 ) { $this->VMin = 0; }
312
 
313
         foreach ( $Data as $Key => $Values )
314
          {
315
           $Sum = 0;
316
           foreach ( $DataDescription["Values"] as $Key2 => $ColName )
317
            {
318
             if (isset($Data[$Key][$ColName]))
319
              {
320
               $Value = $Data[$Key][$ColName];
321
               if ( is_numeric($Value) )
322
                $Sum  += $Value;
323
              }
324
            }
325
           if ( $this->VMax < $Sum) { $this->VMax = $Sum; }
326
           if ( $this->VMin > $Sum) { $this->VMin = $Sum; }
327
          }
328
        }
329
 
330
       $DataRange = $this->VMax - $this->VMin;
331
       if ( $DataRange == 0 ) { $DataRange = .1; }
332
 
333
       /* Compute automatic scaling */
334
       $ScaleOk = FALSE; $Factor = 1;
335
       $MinDivHeight = 25; $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight;
336
       if ($MaxDivs > 1)
337
        {
338
         while(!$ScaleOk)
339
          {
340
           $Scale1 = ( $this->VMax - $this->VMin ) / $Factor;
341
           $Scale2 = ( $this->VMax - $this->VMin ) / $Factor / 2;
342
           $Scale4 = ( $this->VMax - $this->VMin ) / $Factor / 4;
343
 
344
           if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale1); $Scale = 1;}
345
           if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale2); $Scale = 2;}
346
           if (!$ScaleOk)
347
            {
348
             if ( $Scale2 > 1 ) { $Factor = $Factor * 10; }
349
             if ( $Scale2 < 1 ) { $Factor = $Factor / 10; }
350
            }
351
          }
352
 
353
         if ( floor($this->VMax / $Scale / $Factor) != $this->VMax / $Scale / $Factor)
354
          {
355
           $GridID     = floor ( $this->VMax / $Scale / $Factor) + 1;
356
           $this->VMax = $GridID * $Scale * $Factor;
357
           $Divisions++;
358
          }
359
 
360
         if ( floor($this->VMin / $Scale / $Factor) != $this->VMin / $Scale / $Factor)
361
          {
362
           $GridID     = floor( $this->VMin / $Scale / $Factor);
363
           $this->VMin = $GridID * $Scale * $Factor;
364
           $Divisions++;
365
          }
366
        }
367
       else /* Can occurs for small graphs */
368
        $Scale = 1;
369
 
370
       if ( !isset($Divisions) )
371
        $Divisions = 2;
372
 
373
       if ($Scale == 1 && $Divisions%2 == 1)
374
        $Divisions--;
375
      }
376
     else
377
      $Divisions = $this->Divisions;
378
 
379
     $this->DivisionCount = $Divisions;
380
 
381
     $DataRange = $this->VMax - $this->VMin;
382
     if ( $DataRange == 0 ) { $DataRange = .1; }
383
 
384
     $this->DivisionHeight = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $Divisions;
385
     $this->DivisionRatio  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $DataRange;
386
 
387
     $this->GAreaXOffset  = 0;
388
     if ( count($Data) > 1 )
389
      {
390
       if ( $WithMargin == FALSE )
391
        $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data)-1);
392
       else
393
        {
394
         $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data));
395
         $this->GAreaXOffset  = $this->DivisionWidth / 2;
396
        }
397
      }
398
     else
399
      {
400
       $this->DivisionWidth = $this->GArea_X2 - $this->GArea_X1;
401
       $this->GAreaXOffset  = $this->DivisionWidth / 2;
402
      }
403
 
404
     $this->DataCount = count($Data);
405
 
406
     if ( $DrawTicks == FALSE )
407
      return(0);
408
 
409
     $YPos = $this->GArea_Y2; $XMin = NULL;
410
     for($i=1;$i<=$Divisions+1;$i++)
411
      {
412
       $this->drawLine($this->GArea_X1,$YPos,$this->GArea_X1-5,$YPos,$R,$G,$B);
413
       $Value     = $this->VMin + ($i-1) * (( $this->VMax - $this->VMin ) / $Divisions);
414
       $Value     = floor($Value * pow(10,$Decimals)) / pow(10,$Decimals);
415
       if ( $DataDescription["Format"]["Y"] == "number" )
416
        $Value = $Value.$DataDescription["Unit"]["Y"];
417
       if ( $DataDescription["Format"]["Y"] == "time" )
418
        $Value = $this->ToTime($Value);
419
       if ( $DataDescription["Format"]["Y"] == "date" )
420
        $Value = $this->ToDate($Value);
421
       if ( $DataDescription["Format"]["Y"] == "metric" )
422
        $Value = $this->ToMetric($Value);
423
 
424
       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
425
       $TextWidth = $Position[2]-$Position[0];
426
       imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1-10-$TextWidth,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
427
 
428
       if ( $XMin > $this->GArea_X1-10-$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X1-10-$TextWidth; }
429
 
430
       $YPos = $YPos - $this->DivisionHeight;
431
      }
432
 
433
     /* Write the Y Axis caption if set */
434
     if ( isset($DataDescription["Axis"]["Y"]) )
435
      {
436
       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["Y"]);
437
       $TextHeight = abs($Position[1])+abs($Position[3]);
438
       $TextTop    = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight/2);
439
       imagettftext($this->Picture,$this->FontSize,90,$XMin-$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]);
440
      }
441
 
442
     /* Horizontal Axis */
443
     $XPos = $this->GArea_X1 + $this->GAreaXOffset;
444
     $ID = 1; $YMax = NULL;
445
     foreach ( $Data as $Key => $Values )
446
      {
447
       if ( $ID % $SkipLabels == 0 )
448
        {
449
         $this->drawLine(floor($XPos),$this->GArea_Y2,floor($XPos),$this->GArea_Y2+5,$R,$G,$B);
450
         $Value      = $Data[$Key][$DataDescription["Position"]];
451
         if ( $DataDescription["Format"]["X"] == "number" )
452
          $Value = $Value.$DataDescription["Unit"]["X"];
453
         if ( $DataDescription["Format"]["X"] == "time" )
454
          $Value = $this->ToTime($Value);
455
         if ( $DataDescription["Format"]["X"] == "date" )
456
          $Value = $this->ToDate($Value);
457
         if ( $DataDescription["Format"]["X"] == "metric" )
458
          $Value = $this->ToMetric($Value);
459
 
460
         $Position   = imageftbbox($this->FontSize,$Angle,$this->FontName,$Value);
461
         $TextWidth  = abs($Position[2])+abs($Position[0]);
462
         $TextHeight = abs($Position[1])+abs($Position[3]);
463
 
464
         if ( $Angle == 0 )
465
          {
466
           $YPos = $this->GArea_Y2+18;
467
           imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-floor($TextWidth/2),$YPos,$C_TextColor,$this->FontName,$Value);
468
          }
469
         else
470
          {
471
           $YPos = $this->GArea_Y2+10+$TextHeight;
472
           if ( $Angle <= 90 )
473
            imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
474
           else
475
            imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)+$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value);
476
          }
477
         if ( $YMax < $YPos || $YMax == NULL ) { $YMax = $YPos; }
478
        }
479
 
480
       $XPos = $XPos + $this->DivisionWidth;
481
       $ID++;
482
      }
483
 
484
    /* Write the X Axis caption if set */
485
    if ( isset($DataDescription["Axis"]["X"]) )
486
      {
487
       $Position   = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["X"]);
488
       $TextWidth  = abs($Position[2])+abs($Position[0]);
489
       $TextLeft   = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth/2);
490
       imagettftext($this->Picture,$this->FontSize,0,$TextLeft,$YMax+$this->FontSize+5,$C_TextColor,$this->FontName,$DataDescription["Axis"]["X"]);
491
      }
492
    }
493
 
494
   /* Compute and draw the scale */
495
   function drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=100)
496
    {
497
     /* Draw mosaic */
498
     if ( $Mosaic )
499
      {
500
       $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
501
       $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
502
 
503
       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
504
       $C_White         = imagecolorallocate($this->Layers[0],255,255,255);
505
       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
506
       imagecolortransparent($this->Layers[0],$C_White);
507
 
508
       $C_Rectangle = imagecolorallocate($this->Layers[0],250,250,250);
509
 
510
       $YPos  = $LayerHeight; //$this->GArea_Y2-1;
511
       $LastY = $YPos;
512
       for($i=0;$i<=$this->DivisionCount;$i++)
513
        {
514
         $LastY = $YPos;
515
         $YPos  = $YPos - $this->DivisionHeight;
516
 
517
         if ( $YPos <= 0 ) { $YPos = 1; }
518
 
519
         if ( $i % 2 == 0 )
520
          {
521
           imagefilledrectangle($this->Layers[0],1,$YPos,$LayerWidth-1,$LastY,$C_Rectangle);
522
          }
523
        }
524
       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
525
       imagedestroy($this->Layers[0]);
526
      }
527
 
528
     /* Horizontal lines */
529
     $YPos = $this->GArea_Y2 - $this->DivisionHeight;
530
     for($i=1;$i<=$this->DivisionCount;$i++)
531
      {
532
       if ( $YPos > $this->GArea_Y1 && $YPos < $this->GArea_Y2 )
533
        $this->drawDottedLine($this->GArea_X1,$YPos,$this->GArea_X2,$YPos,$LineWidth,$R,$G,$B);
534
 
535
       $YPos = $YPos - $this->DivisionHeight;
536
      }
537
 
538
     /* Vertical lines */
539
     if ( $this->GAreaXOffset == 0 )
540
      { $XPos = $this->GArea_X1 + $this->DivisionWidth + $this->GAreaXOffset; $ColCount = $this->DataCount-2; }
541
     else
542
      { $XPos = $this->GArea_X1 + $this->GAreaXOffset; $ColCount = $this->DataCount; }
543
 
544
     for($i=1;$i<=$ColCount;$i++)
545
      {
546
       if ( $XPos > $this->GArea_X1 && $XPos < $this->GArea_X2 )
547
        $this->drawDottedLine(floor($XPos),$this->GArea_Y1,floor($XPos),$this->GArea_Y2,$LineWidth,$R,$G,$B);
548
       $XPos = $XPos + $this->DivisionWidth;
549
      }
550
    }
551
 
552
   /* retrieve the legends size */
553
   function getLegendBoxSize($DataDescription)
554
    {
555
     if ( !isset($DataDescription["Description"]) )
556
      return(-1);
557
 
558
     /* <-10->[8]<-4->Text<-10-> */
559
     $MaxWidth = 0; $MaxHeight = 8;
560
     foreach($DataDescription["Description"] as $Key => $Value)
561
      {
562
       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
563
       $TextWidth = $Position[2]-$Position[0];
564
       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
565
       $MaxHeight = $MaxHeight + ( $this->FontSize + 6 );
566
      }
567
     $MaxHeight = $MaxHeight - 3;
568
     $MaxWidth  = $MaxWidth + 32;
569
 
570
     return(array($MaxWidth,$MaxHeight));
571
    }
572
 
573
   /* Draw the data legends */
574
   function drawLegend($XPos,$YPos,&$DataDescription,$R,$G,$B)
575
    {
576
     /* Validate the Data and DataDescription array */
577
     $this->validateDataDescription("drawLegend",$DataDescription);
578
 
579
     if ( !isset($DataDescription["Description"]) )
580
      return(-1);
581
 
582
     $C_TextColor = imagecolorallocate($this->Picture,0,0,0);
583
 
584
     /* <-10->[8]<-4->Text<-10-> */
585
     $MaxWidth = 0; $MaxHeight = 8;
586
     foreach($DataDescription["Description"] as $Key => $Value)
587
      {
588
       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
589
       $TextWidth = $Position[2]-$Position[0];
590
       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
591
       $MaxHeight = $MaxHeight + ( $this->FontSize + 6 );
592
      }
593
     $MaxHeight = $MaxHeight - 5;
594
     $MaxWidth  = $MaxWidth + 32;
595
 
596
     $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$R-30,$G-30,$B-30);
597
     $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
598
 
599
     $YOffset = 4 + $this->FontSize; $ID = 0;
600
     foreach($DataDescription["Description"] as $Key => $Value)
601
      {
602
       $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"]);
603
 
604
       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value);
605
       $YOffset = $YOffset + ( $this->FontSize + 6 );
606
       $ID++;
607
      }
608
    }
609
 
610
   /* Draw the data legends */
611
   function drawPieLegend($XPos,$YPos,&$Data,&$DataDescription,$R,$G,$B)
612
    {
613
     /* Validate the Data and DataDescription array */
614
     $this->validateDataDescription("drawPieLegend",$DataDescription,FALSE);
615
     $this->validateData("drawPieLegend",$Data);
616
 
617
     if ( !isset($DataDescription["Position"]) )
618
      return(-1);
619
 
620
     $C_TextColor = imagecolorallocate($this->Picture,0,0,0);
621
 
622
     /* <-10->[8]<-4->Text<-10-> */
623
     $MaxWidth = 0; $MaxHeight = 8;
624
     foreach($Data as $Key => $Value)
625
      {
626
       $Value2 = $Value[$DataDescription["Position"]];
627
       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value2);
628
       $TextWidth = $Position[2]-$Position[0];
629
       if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; }
630
       $MaxHeight = $MaxHeight + ( $this->FontSize + 6 );
631
      }
632
     $MaxHeight = $MaxHeight - 3;
633
     $MaxWidth  = $MaxWidth + 32;
634
 
635
     $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$R-30,$G-30,$B-30);
636
     $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B);
637
 
638
     $YOffset = 4 + $this->FontSize; $ID = 0;
639
     foreach($Data as $Key => $Value)
640
      {
641
       $Value2 = $Value[$DataDescription["Position"]];
642
 
643
       $this->drawFilledRectangle($XPos+10,$YPos+$YOffset-6,$XPos+14,$YPos+$YOffset-2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]);
644
 
645
       imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value2);
646
       $YOffset = $YOffset + ( $this->FontSize + 6 );
647
       $ID++;
648
      }
649
    }
650
 
651
   /* Draw the graph title */
652
   function drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2 = -1, $YPos2 = -1)
653
    {
654
     $C_TextColor = imagecolorallocate($this->Picture,$R,$G,$B);
655
 
656
     if ( $XPos2 != -1 )
657
      {
658
       $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Value);
659
       $TextWidth = $Position[2]-$Position[0];
660
       $XPos      = floor(( $XPos2 - $XPos - $TextWidth ) / 2 ) + $XPos;
661
      }
662
 
663
     if ( $YPos2 != -1 )
664
      {
665
       $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Value);
666
       $TextHeight = $Position[5]-$Position[3];
667
       $YPos       = floor(( $YPos2 - $YPos - $TextHeight ) / 2 ) + $YPos;
668
      }
669
 
670
     imagettftext($this->Picture,$this->FontSize,0,$XPos,$YPos,$C_TextColor,$this->FontName,$Value);
671
    }
672
 
673
   /* Compute and draw the scale */
674
   function drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4)
675
    {
676
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
677
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
678
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
679
 
680
     $C_TextColor = imagecolorallocate($this->Picture,$R,$G,$B);
681
     $Y = $this->GArea_Y2 - ($Value - $this->VMin) * $this->DivisionRatio;
682
 
683
     if ( $Y <= $this->GArea_Y1 || $Y >= $this->GArea_Y2 )
684
      return(-1);
685
 
686
     if ( $TickWidth == 0 )
687
      $this->drawLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$R,$G,$B);
688
     else
689
      $this->drawDottedLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$TickWidth,$R,$G,$B);
690
 
691
     if ( $ShowLabel )
692
      {
693
       if ( $ShowOnRight )
694
        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+2,$Y+($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
695
       else
696
        imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1+2,$Y-($this->FontSize/2),$C_TextColor,$this->FontName,$Value);
697
      }
698
    }
699
 
700
   /* This function put a label on a specific point */
701
   function setLabel(&$Data,&$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210)
702
    {
703
     /* Validate the Data and DataDescription array */
704
     $this->validateDataDescription("setLabel",$DataDescription);
705
     $this->validateData("setLabel",$Data);
706
 
707
     $C_Label     = imagecolorallocate($this->Picture,$R,$G,$B);
708
     $C_Shadow    = imagecolorallocate($this->Picture,$R-30,$G-30,$B-30);
709
     $C_TextColor = imagecolorallocate($this->Picture,0,0,0);
710
 
711
     $Cp = 0; $Found = FALSE;
712
     foreach ( $Data as $Key => $Value )
713
      {
714
       if ( $Data[$Key][$DataDescription["Position"]] == $ValueName )
715
        { $NumericalValue = $Data[$Key][$SerieName]; $Found = TRUE; }
716
       if ( !$Found )
717
        $Cp++;
718
      }
719
 
720
     $XPos = $this->GArea_X1 + $this->GAreaXOffset + ( $this->DivisionWidth * $Cp ) + 2;
721
     $YPos = $this->GArea_Y2 - ($NumericalValue - $this->VMin) * $this->DivisionRatio;
722
 
723
     $Position   = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
724
     $TextHeight = $Position[3] - $Position[5];
725
     $TextWidth = $Position[2]-$Position[0];
726
     $TextOffset = floor($TextHeight/2);
727
 
728
     // Shadow
729
     $Poly = array($XPos+1,$YPos+1,$XPos + 9,$YPos - $TextOffset,$XPos + 8,$YPos + $TextOffset + 2);
730
     imagefilledpolygon($this->Picture,$Poly,3,$C_Shadow);
731
     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos - $TextOffset - 1,$R-30,$G-30,$B-30);
732
     $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos + $TextOffset + 3,$R-30,$G-30,$B-30);
733
     $this->drawFilledRectangle($XPos + 9,$YPos - $TextOffset,$XPos + 13 + $TextWidth,$YPos + $TextOffset + 2,$R-30,$G-30,$B-30);
734
 
735
     // Label background
736
     $Poly = array($XPos,$YPos,$XPos + 8,$YPos - $TextOffset - 1,$XPos + 8,$YPos + $TextOffset + 1);
737
     imagefilledpolygon($this->Picture,$Poly,3,$C_Label);
738
     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos - $TextOffset - 2,$R,$G,$B);
739
     $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos + $TextOffset + 2,$R,$G,$B);
740
     $this->drawFilledRectangle($XPos + 8,$YPos - $TextOffset - 1,$XPos + 12 + $TextWidth,$YPos + $TextOffset + 1,$R,$G,$B);
741
 
742
     imagettftext($this->Picture,$this->FontSize,0,$XPos + 10,$YPos + $TextOffset,$C_TextColor,$this->FontName,$Caption);
743
    }
744
 
745
   /* This function draw a line graph */
746
   function drawPlotGraph(&$Data,&$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1)
747
    {
748
     /* Validate the Data and DataDescription array */
749
     $this->validateDataDescription("drawPlotGraph",$DataDescription);
750
     $this->validateData("drawPlotGraph",$Data);
751
 
752
     $GraphID = 0;
753
 
754
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
755
      {
756
       $ID = 0;
757
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
758
        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
759
 
760
       $XPos   = $this->GArea_X1 + $this->GAreaXOffset;
761
       foreach ( $Data as $Key => $Values )
762
        {
763
         $Value = $Data[$Key][$ColName];
764
         $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
765
 
766
         if ( is_numeric($Value) )
767
          {
768
           $R = $this->Palette[$ColorID]["R"];
769
           $G = $this->Palette[$ColorID]["G"];
770
           $B = $this->Palette[$ColorID]["B"];
771
           $this->drawFilledCircle($XPos+1,$YPos+1,$BigRadius,$R,$G,$B);
772
 
773
           if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 )
774
            $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2);
775
           else
776
            {
777
             $R = $this->Palette[$ColorID]["R"]-5; if ( $R < 0 ) { $R = 0; }
778
             $G = $this->Palette[$ColorID]["G"]-5; if ( $G < 0 ) { $G = 0; }
779
             $B = $this->Palette[$ColorID]["B"]-5; if ( $B < 0 ) { $B = 0; }
780
 
781
             $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R,$G,$B);
782
            }
783
          }
784
 
785
         $XPos = $XPos + $this->DivisionWidth;
786
        }
787
       $GraphID++;
788
      }
789
    }
790
 
791
 
792
   /* This function draw an area between two series */
793
   function drawArea(&$Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50)
794
    {
795
     /* Validate the Data and DataDescription array */
796
     $this->validateData("drawArea",$Data);
797
 
798
     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
799
     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
800
 
801
     $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
802
     $C_White         = imagecolorallocate($this->Layers[0],255,255,255);
803
     imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
804
     imagecolortransparent($this->Layers[0],$C_White);
805
 
806
     $C_Graph = imagecolorallocate($this->Layers[0],$R,$G,$B);
807
 
808
     $XPos     = $this->GAreaXOffset;
809
     $LastXPos = -1;
810
     foreach ( $Data as $Key => $Values )
811
      {
812
       $Value1 = $Data[$Key][$Serie1];
813
       $Value2 = $Data[$Key][$Serie2];
814
       $YPos1  = $LayerHeight - (($Value1-$this->VMin) * $this->DivisionRatio);
815
       $YPos2  = $LayerHeight - (($Value2-$this->VMin) * $this->DivisionRatio);
816
 
817
       if ( $LastXPos != -1 )
818
        {
819
         $Points   = "";
820
         $Points[] = $LastXPos; $Points[] = $LastYPos1;
821
         $Points[] = $LastXPos; $Points[] = $LastYPos2;
822
         $Points[] = $XPos; $Points[] = $YPos2;
823
         $Points[] = $XPos; $Points[] = $YPos1;
824
 
825
         imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
826
        }
827
 
828
       $LastYPos1 = $YPos1;
829
       $LastYPos2 = $YPos2;
830
       $LastXPos  = $XPos;
831
 
832
       $XPos = $XPos + $this->DivisionWidth;
833
      }
834
 
835
     imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
836
     imagedestroy($this->Layers[0]);
837
    }
838
 
839
 
840
   /* This function write the values of the specified series */
841
   function writeValues(&$Data,&$DataDescription,$Series)
842
    {
843
     /* Validate the Data and DataDescription array */
844
     $this->validateDataDescription("writeValues",$DataDescription);
845
     $this->validateData("writeValues",$Data);
846
 
847
     if ( !is_array($Series) ) { $Series = array($Series); }
848
 
849
     foreach($Series as $Key => $Serie)
850
      {
851
       $ID = 0;
852
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
853
        { if ( $keyI == $Serie ) { $ColorID = $ID; }; $ID++; }
854
 
855
       $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
856
       $XLast = -1;
857
       foreach ( $Data as $Key => $Values )
858
        {
859
         if ( isset($Data[$Key][$Serie]) && is_numeric($Data[$Key][$Serie]))
860
          {
861
           $Value = $Data[$Key][$Serie];
862
           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
863
 
864
           $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Value);
865
           $Width  = $Positions[2] - $Positions[6]; $XOffset = $XPos - ($Width/2);
866
           $Height = $Positions[3] - $Positions[7]; $YOffset = $YPos - 4;
867
 
868
           $C_TextColor = imagecolorallocate($this->Picture,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
869
           imagettftext($this->Picture,$this->FontSize,0,$XOffset,$YOffset,$C_TextColor,$this->FontName,$Value);
870
          }
871
         $XPos = $XPos + $this->DivisionWidth;
872
        }
873
 
874
      }
875
    }
876
 
877
   /* This function draw a line graph */
878
   function drawLineGraph(&$Data,&$DataDescription,$SerieName="")
879
    {
880
     /* Validate the Data and DataDescription array */
881
     $this->validateDataDescription("drawLineGraph",$DataDescription);
882
     $this->validateData("drawLineGraph",$Data);
883
 
884
     $GraphID = 0;
885
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
886
      {
887
       $ID = 0;
888
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
889
        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
890
 
891
       if ( $SerieName == "" || $SerieName == $ColName )
892
        {
893
         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
894
         $XLast = -1;
895
         foreach ( $Data as $Key => $Values )
896
          {
897
           if ( isset($Data[$Key][$ColName]))
898
            {
899
             $Value = $Data[$Key][$ColName];
900
             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
901
 
902
             if (!is_numeric($Value)) { $XLast = -1; }
903
             if ( $XLast != -1 )
904
              $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
905
 
906
             $XLast = $XPos;
907
             $YLast = $YPos;
908
             if (!is_numeric($Value)) { $XLast = -1; }
909
            }
910
           $XPos = $XPos + $this->DivisionWidth;
911
          }
912
         $GraphID++;
913
        }
914
      }
915
    }
916
 
917
   /* This function draw a cubic curve */
918
   function drawCubicCurve(&$Data,&$DataDescription,$Accuracy=.1,$SerieName="")
919
    {
920
     /* Validate the Data and DataDescription array */
921
     $this->validateDataDescription("drawCubicCurve",$DataDescription);
922
     $this->validateData("drawCubicCurve",$Data);
923
 
924
     $GraphID = 0;
925
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
926
      {
927
       if ( $SerieName == "" || $SerieName == $ColName )
928
        {
929
         $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
930
         $XIn[0] = 0; $YIn[0] = 0;
931
 
932
         $ID = 0;
933
         foreach ( $DataDescription["Description"] as $keyI => $ValueI )
934
          { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
935
 
936
         $Index = 1;
937
         $XLast = -1; $Missing = "";
938
         foreach ( $Data as $Key => $Values )
939
          {
940
           if ( isset($Data[$Key][$ColName]) )
941
            {
942
             $Value = $Data[$Key][$ColName];
943
             $XIn[$Index] = $Index;
944
             $YIn[$Index] = $Value;
945
             if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
946
             $Index++;
947
            }
948
          }
949
         $Index--;
950
 
951
         $Yt[0] = 0;
952
         $Yt[1] = 0;
953
         $U[1]  = 0;
954
         for($i=2;$i<=$Index-1;$i++)
955
          {
956
           $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
957
           $p      = $Sig * $Yt[$i-1] + 2;
958
           $Yt[$i] = ($Sig - 1) / $p;
959
           $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
960
           $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
961
          }
962
 
963
         $qn = 0;
964
         $un = 0;
965
         $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
966
 
967
         for($k=$Index-1;$k>=1;$k--)
968
          $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
969
 
970
         $XPos  = $this->GArea_X1 + $this->GAreaXOffset;
971
         for($X=1;$X<=$Index;$X=$X+$Accuracy)
972
          {
973
           $klo = 1;
974
           $khi = $Index;
975
           $k   = $khi - $klo;
976
           while($k > 1)
977
            {
978
             $k = $khi - $klo;
979
             If ( $XIn[$k] >= $X )
980
              $khi = $k;
981
             else
982
              $klo = $k;
983
            }
984
           $klo = $khi - 1;
985
 
986
           $h     = $XIn[$khi] - $XIn[$klo];
987
           $a     = ($XIn[$khi] - $X) / $h;
988
           $b     = ($X - $XIn[$klo]) / $h;
989
           $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
990
 
991
           $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
992
 
993
           if ( $XLast != -1 && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]) )
994
            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
995
 
996
           $XLast = $XPos;
997
           $YLast = $YPos;
998
           $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
999
          }
1000
 
1001
         // Add potentialy missing values
1002
         $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1003
         if ( $XPos < ($this->GArea_X2 - $this->GAreaXOffset) )
1004
          {
1005
           $YPos = $this->GArea_Y2 - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1006
           $this->drawLine($XLast,$YLast,$this->GArea_X2-$this->GAreaXOffset,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1007
          }
1008
 
1009
         $GraphID++;
1010
        }
1011
      }
1012
    }
1013
 
1014
   /* This function draw a filled cubic curve */
1015
   function drawFilledCubicCurve(&$Data,&$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE)
1016
    {
1017
     /* Validate the Data and DataDescription array */
1018
     $this->validateDataDescription("drawFilledCubicCurve",$DataDescription);
1019
     $this->validateData("drawFilledCubicCurve",$Data);
1020
 
1021
     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1022
     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1023
     $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1024
     if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1025
 
1026
     $GraphID = 0;
1027
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1028
      {
1029
       $XIn = ""; $Yin = ""; $Yt = ""; $U = "";
1030
       $XIn[0] = 0; $YIn[0] = 0;
1031
 
1032
       $ID = 0;
1033
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1034
        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1035
 
1036
       $Index = 1;
1037
       $XLast = -1; $Missing = "";
1038
       foreach ( $Data as $Key => $Values )
1039
        {
1040
         $Value = $Data[$Key][$ColName];
1041
         $XIn[$Index] = $Index;
1042
         $YIn[$Index] = $Value;
1043
         if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; }
1044
         $Index++;
1045
        }
1046
       $Index--;
1047
 
1048
       $Yt[0] = 0;
1049
       $Yt[1] = 0;
1050
       $U[1]  = 0;
1051
       for($i=2;$i<=$Index-1;$i++)
1052
        {
1053
         $Sig    = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]);
1054
         $p      = $Sig * $Yt[$i-1] + 2;
1055
         $Yt[$i] = ($Sig - 1) / $p;
1056
         $U[$i]  = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]);
1057
         $U[$i]  = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p;
1058
        }
1059
 
1060
       $qn = 0;
1061
       $un = 0;
1062
       $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1);
1063
 
1064
       for($k=$Index-1;$k>=1;$k--)
1065
        $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k];
1066
 
1067
       $Points   = "";
1068
       $Points[] = $this->GAreaXOffset;
1069
       $Points[] = $LayerHeight;
1070
 
1071
       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1072
       $C_White         = imagecolorallocate($this->Layers[0],255,255,255);
1073
       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1074
       imagecolortransparent($this->Layers[0],$C_White);
1075
 
1076
       $YLast = NULL;
1077
       $XPos  = $this->GAreaXOffset; $PointsCount = 2;
1078
       for($X=1;$X<=$Index;$X=$X+$Accuracy)
1079
        {
1080
         $klo = 1;
1081
         $khi = $Index;
1082
         $k   = $khi - $klo;
1083
         while($k > 1)
1084
          {
1085
           $k = $khi - $klo;
1086
           If ( $XIn[$k] >= $X )
1087
            $khi = $k;
1088
           else
1089
            $klo = $k;
1090
          }
1091
         $klo = $khi - 1;
1092
 
1093
         $h     = $XIn[$khi] - $XIn[$klo];
1094
         $a     = ($XIn[$khi] - $X) / $h;
1095
         $b     = ($X - $XIn[$klo]) / $h;
1096
         $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6;
1097
 
1098
         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1099
 
1100
         if ( $YLast != NULL && $AroundZero && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]))
1101
          {
1102
           $aPoints   = "";
1103
           $aPoints[] = $XLast;
1104
           $aPoints[] = $YLast;
1105
           $aPoints[] = $XPos;
1106
           $aPoints[] = $YPos;
1107
           $aPoints[] = $XPos;
1108
           $aPoints[] = $YZero;
1109
           $aPoints[] = $XLast;
1110
           $aPoints[] = $YZero;
1111
 
1112
           $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1113
           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1114
          }
1115
 
1116
         if ( !isset($Missing[floor($X)]) || $YLast == NULL )
1117
          {
1118
           $PointsCount++;
1119
           $Points[] = $XPos;
1120
           $Points[] = $YPos;
1121
          }
1122
         else
1123
          {
1124
           $PointsCount++; $Points[] = $XLast; $Points[] = $LayerHeight;
1125
          }
1126
 
1127
         $YLast = $YPos; $XLast = $XPos;
1128
         $XPos  = $XPos + $this->DivisionWidth * $Accuracy;
1129
        }
1130
 
1131
       // Add potentialy missing values
1132
       $XPos  = $XPos - $this->DivisionWidth * $Accuracy;
1133
       if ( $XPos < ($LayerWidth-$this->GAreaXOffset) )
1134
        {
1135
         $YPos = $LayerHeight - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio);
1136
 
1137
         if ( $YLast != NULL && $AroundZero )
1138
          {
1139
           $aPoints   = "";
1140
           $aPoints[] = $XLast;
1141
           $aPoints[] = $YLast;
1142
           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1143
           $aPoints[] = $YPos;
1144
           $aPoints[] = $LayerWidth-$this->GAreaXOffset;
1145
           $aPoints[] = $YZero;
1146
           $aPoints[] = $XLast;
1147
           $aPoints[] = $YZero;
1148
 
1149
           $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1150
           imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph);
1151
          }
1152
 
1153
         if ( $YIn[$klo] != "" && $YIn[$khi] != "" || $YLast == NULL )
1154
          {
1155
           $PointsCount++;
1156
           $Points[] = $LayerWidth-$this->GAreaXOffset;
1157
           $Points[] = $YPos;
1158
          }
1159
        }
1160
 
1161
       $Points[] = $LayerWidth-$this->GAreaXOffset;
1162
       $Points[] = $LayerHeight;
1163
 
1164
       if ( !$AroundZero )
1165
        {
1166
         $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1167
         imagefilledpolygon($this->Layers[0],$Points,$PointsCount,$C_Graph);
1168
        }
1169
 
1170
       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1171
       imagedestroy($this->Layers[0]);
1172
 
1173
       $this->drawCubicCurve($Data,$DataDescription,$Accuracy,$ColName);
1174
 
1175
       $GraphID++;
1176
      }
1177
    }
1178
 
1179
   /* This function draw a filled line graph */
1180
   function drawFilledLineGraph(&$Data,&$DataDescription,$Alpha=100,$AroundZero=FALSE)
1181
    {
1182
     $Empty = -2147483647;
1183
 
1184
     /* Validate the Data and DataDescription array */
1185
     $this->validateDataDescription("drawFilledLineGraph",$DataDescription);
1186
     $this->validateData("drawFilledLineGraph",$Data);
1187
 
1188
     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1189
     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1190
 
1191
     $GraphID = 0;
1192
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1193
      {
1194
       $ID = 0;
1195
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1196
        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1197
 
1198
       $aPoints   = "";
1199
       $aPoints[] = $this->GAreaXOffset;
1200
       $aPoints[] = $LayerHeight;
1201
 
1202
       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1203
       $C_White         = imagecolorallocate($this->Layers[0],255,255,255);
1204
       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1205
       imagecolortransparent($this->Layers[0],$C_White);
1206
 
1207
       $XPos  = $this->GAreaXOffset;
1208
       $XLast = -1; $PointsCount = 2;
1209
       $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1210
       if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; }
1211
 
1212
       $YLast = $Empty;
1213
       foreach ( $Data as $Key => $Values )
1214
        {
1215
         $Value = $Data[$Key][$ColName];
1216
         $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1217
 
1218
         if ( !is_numeric($Value) )
1219
          {
1220
           $PointsCount++;
1221
           $aPoints[] = $XLast;
1222
           $aPoints[] = $LayerHeight;
1223
 
1224
           $YLast = $Empty;
1225
          }
1226
         else
1227
          {
1228
           $PointsCount++;
1229
           if ( $YLast <> $Empty )
1230
            { $aPoints[] = $XPos; $aPoints[] = $YPos; }
1231
           else
1232
            { $PointsCount++; $aPoints[] = $XPos; $aPoints[] = $LayerHeight; $aPoints[] = $XPos; $aPoints[] = $YPos; }
1233
 
1234
           if ($YLast <> $Empty && $AroundZero)
1235
            {
1236
             $Points   = "";
1237
             $Points[] = $XLast; $Points[] = $YLast;
1238
             $Points[] = $XPos;
1239
             $Points[] = $YPos;
1240
             $Points[] = $XPos;
1241
             $Points[] = $YZero;
1242
             $Points[] = $XLast;
1243
             $Points[] = $YZero;
1244
 
1245
             $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1246
             imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph);
1247
            }
1248
           $YLast = $YPos;
1249
          }
1250
 
1251
         $XLast = $XPos;
1252
         $XPos  = $XPos + $this->DivisionWidth;
1253
        }
1254
       $aPoints[] = $LayerWidth - $this->GAreaXOffset;
1255
       $aPoints[] = $LayerHeight;
1256
 
1257
       if ( $AroundZero == FALSE )
1258
        {
1259
         $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1260
         imagefilledpolygon($this->Layers[0],$aPoints,$PointsCount,$C_Graph);
1261
        }
1262
 
1263
       imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1264
       imagedestroy($this->Layers[0]);
1265
       $GraphID++;
1266
       $this->drawLineGraph($Data,$DataDescription,$ColName);
1267
      }
1268
    }
1269
 
1270
   /* This function draw a bar graph */
1271
   function drawOverlayBarGraph(&$Data,&$DataDescription,$Alpha=50)
1272
    {
1273
     /* Validate the Data and DataDescription array */
1274
     $this->validateDataDescription("drawOverlayBarGraph",$DataDescription);
1275
     $this->validateData("drawOverlayBarGraph",$Data);
1276
 
1277
     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1278
     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1279
 
1280
     $GraphID = 0;
1281
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1282
      {
1283
       $ID = 0;
1284
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1285
        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1286
 
1287
       $this->Layers[$GraphID] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1288
       $C_White                = imagecolorallocate($this->Layers[$GraphID],255,255,255);
1289
       $C_Graph                = imagecolorallocate($this->Layers[$GraphID],$this->Palette[$GraphID]["R"],$this->Palette[$GraphID]["G"],$this->Palette[$GraphID]["B"]);
1290
       imagefilledrectangle($this->Layers[$GraphID],0,0,$LayerWidth,$LayerHeight,$C_White);
1291
       imagecolortransparent($this->Layers[$GraphID],$C_White);
1292
 
1293
       $XWidth = $this->DivisionWidth / 4;
1294
       $XPos   = $this->GAreaXOffset;
1295
       $YZero  = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio);
1296
       $XLast  = -1; $PointsCount = 2;
1297
       foreach ( $Data as $Key => $Values )
1298
        {
1299
         if ( isset($Data[$Key][$ColName]) )
1300
          {
1301
           $Value = $Data[$Key][$ColName];
1302
           if ( is_numeric($Value) )
1303
            {
1304
             $YPos  = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio);
1305
 
1306
             imagefilledrectangle($this->Layers[$GraphID],$XPos-$XWidth,$YPos,$XPos+$XWidth,$YZero,$C_Graph);
1307
 
1308
             $X1 = floor($XPos - $XWidth + $this->GArea_X1); $Y1 = floor($YPos+$this->GArea_Y1) + .2;
1309
             $X2 = floor($XPos + $XWidth + $this->GArea_X1);
1310
             if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
1311
             if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
1312
 
1313
             $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE);
1314
            }
1315
          }
1316
         $XPos = $XPos + $this->DivisionWidth;
1317
        }
1318
 
1319
       $GraphID++;
1320
      }
1321
 
1322
     for($i=0;$i<=($GraphID-1);$i++)
1323
      {
1324
       imagecopymerge($this->Picture,$this->Layers[$i],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1325
       imagedestroy($this->Layers[$i]);
1326
      }
1327
    }
1328
 
1329
   /* This function draw a bar graph */
1330
   function drawBarGraph(&$Data,&$DataDescription,$Shadow=FALSE,$Alpha=100)
1331
    {
1332
     /* Validate the Data and DataDescription array */
1333
     $this->validateDataDescription("drawBarGraph",$DataDescription);
1334
     $this->validateData("drawBarGraph",$Data);
1335
 
1336
     $GraphID      = 0;
1337
     $Series       = count($DataDescription["Values"]);
1338
     $SeriesWidth  = $this->DivisionWidth / ($Series+1);
1339
     $SerieXOffset = $this->DivisionWidth / 2 - $SeriesWidth / 2;
1340
 
1341
     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1342
     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
1343
 
1344
     $SerieID = 0;
1345
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1346
      {
1347
       $ID = 0;
1348
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1349
        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1350
 
1351
       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SerieXOffset + $SeriesWidth * $SerieID;
1352
       $XLast = -1;
1353
       foreach ( $Data as $Key => $Values )
1354
        {
1355
         if ( isset($Data[$Key][$ColName]))
1356
          {
1357
           if ( is_numeric($Data[$Key][$ColName]) )
1358
            {
1359
             $Value = $Data[$Key][$ColName];
1360
             $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1361
 
1362
             if ( $Shadow && $Alpha == 100 )
1363
              $this->drawRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,25,25,25,TRUE,$Alpha);
1364
 
1365
             $this->drawFilledRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
1366
            }
1367
          }
1368
         $XPos = $XPos + $this->DivisionWidth;
1369
        }
1370
       $SerieID++;
1371
      }
1372
    }
1373
 
1374
   /* This function draw a stacked bar graph */
1375
   function drawStackedBarGraph(&$Data,&$DataDescription,$Alpha=50)
1376
    {
1377
     /* Validate the Data and DataDescription array */
1378
     $this->validateDataDescription("drawBarGraph",$DataDescription);
1379
     $this->validateData("drawBarGraph",$Data);
1380
 
1381
     $GraphID      = 0;
1382
     $Series       = count($DataDescription["Values"]);
1383
     $SeriesWidth  = $this->DivisionWidth * .8;
1384
 
1385
     $YZero  = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio);
1386
     if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; }
1387
 
1388
     $SerieID = 0; $LastValue = "";
1389
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1390
      {
1391
       $ID = 0;
1392
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1393
        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1394
 
1395
       $XPos  = $this->GArea_X1 + $this->GAreaXOffset - $SeriesWidth / 2;
1396
       $XLast = -1;
1397
       foreach ( $Data as $Key => $Values )
1398
        {
1399
         if ( isset($Data[$Key][$ColName]))
1400
          {
1401
           if ( is_numeric($Data[$Key][$ColName]) )
1402
            {
1403
             $Value = $Data[$Key][$ColName];
1404
 
1405
             if ( isset($LastValue[$Key]) )
1406
              {
1407
               $YPos    = $this->GArea_Y2 - ((($Value+$LastValue[$Key])-$this->VMin) * $this->DivisionRatio);
1408
               $YBottom = $this->GArea_Y2 - (($LastValue[$Key]-$this->VMin) * $this->DivisionRatio);
1409
               $LastValue[$Key] += $Value;
1410
              }
1411
             else
1412
              {
1413
               $YPos    = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio);
1414
               $YBottom = $YZero;
1415
               $LastValue[$Key] = $Value;
1416
              }
1417
             $this->drawFilledRectangle($XPos+1,$YBottom,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha);
1418
            }
1419
          }
1420
         $XPos = $XPos + $this->DivisionWidth;
1421
        }
1422
       $SerieID++;
1423
      }
1424
    }
1425
 
1426
   /* This function draw a limits bar graphs */
1427
   function drawLimitsGraph(&$Data,&$DataDescription,$R=0,$G=0,$B=0)
1428
    {
1429
     /* Validate the Data and DataDescription array */
1430
     $this->validateDataDescription("drawLimitsGraph",$DataDescription);
1431
     $this->validateData("drawLimitsGraph",$Data);
1432
 
1433
     $XWidth = $this->DivisionWidth / 4;
1434
     $XPos   = $this->GArea_X1 + $this->GAreaXOffset;
1435
 
1436
     foreach ( $Data as $Key => $Values )
1437
      {
1438
       $Min     = $Data[$Key][$DataDescription["Values"][0]];
1439
       $Max     = $Data[$Key][$DataDescription["Values"][0]];
1440
       $GraphID = 0; $MaxID = 0; $MinID = 0;
1441
       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1442
        {
1443
         if ( isset($Data[$Key][$ColName]) )
1444
          {
1445
           if ( $Data[$Key][$ColName] > $Max && is_numeric($Data[$Key][$ColName]))
1446
            { $Max = $Data[$Key][$ColName]; $MaxID = $GraphID; }
1447
          }
1448
         if ( isset($Data[$Key][$ColName]) && is_numeric($Data[$Key][$ColName]))
1449
          {
1450
           if ( $Data[$Key][$ColName] < $Min )
1451
            { $Min = $Data[$Key][$ColName]; $MinID = $GraphID; }
1452
           $GraphID++;
1453
          }
1454
        }
1455
 
1456
       $YPos = $this->GArea_Y2 - (($Max-$this->VMin) * $this->DivisionRatio);
1457
       $X1 = floor($XPos - $XWidth); $Y1 = floor($YPos) - .2;
1458
       $X2 = floor($XPos + $XWidth);
1459
       if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; }
1460
       if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; }
1461
 
1462
       $YPos = $this->GArea_Y2 - (($Min-$this->VMin) * $this->DivisionRatio);
1463
       $Y2 = floor($YPos) + .2;
1464
 
1465
       $this->drawLine(floor($XPos)-.2,$Y1+1,floor($XPos)-.2,$Y2-1,$R,$G,$B,TRUE);
1466
       $this->drawLine(floor($XPos)+.2,$Y1+1,floor($XPos)+.2,$Y2-1,$R,$G,$B,TRUE);
1467
       $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$MaxID]["R"],$this->Palette[$MaxID]["G"],$this->Palette[$MaxID]["B"],FALSE);
1468
       $this->drawLine($X1,$Y2,$X2,$Y2,$this->Palette[$MinID]["R"],$this->Palette[$MinID]["G"],$this->Palette[$MinID]["B"],FALSE);
1469
 
1470
       $XPos = $XPos + $this->DivisionWidth;
1471
      }
1472
    }
1473
 
1474
   /* This function draw radar axis centered on the graph area */
1475
   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)
1476
    {
1477
     /* Validate the Data and DataDescription array */
1478
     $this->validateDataDescription("drawRadarAxis",$DataDescription);
1479
     $this->validateData("drawRadarAxis",$Data);
1480
 
1481
     $C_TextColor = imagecolorallocate($this->Picture,$A_R,$A_G,$A_B);
1482
 
1483
     /* Draw radar axis */
1484
     $Points  = count($Data);
1485
     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
1486
     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
1487
     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
1488
 
1489
     /* Search for the max value */
1490
     if ( $MaxValue == -1 )
1491
      {
1492
       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1493
        {
1494
         foreach ( $Data as $Key => $Values )
1495
          {
1496
           if ( isset($Data[$Key][$ColName]))
1497
            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
1498
          }
1499
        }
1500
      }
1501
 
1502
     /* Draw the mosaic */
1503
     if ( $Mosaic )
1504
      {
1505
       $RadiusScale = $Radius / $MaxValue;
1506
       for ( $t=1; $t<=$MaxValue-1; $t++)
1507
        {
1508
         $TRadius  = $RadiusScale * $t;
1509
         $LastX1   = -1;
1510
 
1511
         for ( $i=0; $i<=$Points; $i++)
1512
          {
1513
           $Angle = -90 + $i * 360/$Points;
1514
           $X1 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
1515
           $Y1 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
1516
           $X2 = cos($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $XCenter;
1517
           $Y2 = sin($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $YCenter;
1518
 
1519
           if ( $t % 2 == 1 && $LastX1 != -1)
1520
            {
1521
             $Plots   = "";
1522
             $Plots[] = $X1; $Plots[] = $Y1;
1523
             $Plots[] = $X2; $Plots[] = $Y2;
1524
             $Plots[] = $LastX2; $Plots[] = $LastY2;
1525
             $Plots[] = $LastX1; $Plots[] = $LastY1;
1526
 
1527
             $C_Graph = imagecolorallocate($this->Picture,250,250,250);
1528
             imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_Graph);
1529
            }
1530
 
1531
           $LastX1 = $X1; $LastY1= $Y1;
1532
           $LastX2 = $X2; $LastY2= $Y2;
1533
          }
1534
        }
1535
      }
1536
 
1537
 
1538
     /* Draw the spider web */
1539
     for ( $t=1; $t<=$MaxValue; $t++)
1540
      {
1541
       $TRadius = ( $Radius / $MaxValue ) * $t;
1542
       $LastX   = -1;
1543
 
1544
       for ( $i=0; $i<=$Points; $i++)
1545
        {
1546
         $Angle = -90 + $i * 360/$Points;
1547
         $X = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
1548
         $Y = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
1549
 
1550
         if ( $LastX != -1 )
1551
          $this->drawDottedLine($LastX,$LastY,$X,$Y,4,$S_R,$S_G,$S_B);
1552
 
1553
         $LastX = $X; $LastY= $Y;
1554
        }
1555
      }
1556
 
1557
     /* Draw the axis */
1558
     for ( $i=0; $i<=$Points; $i++)
1559
      {
1560
       $Angle = -90 + $i * 360/$Points;
1561
       $X = cos($Angle * 3.1418 / 180 ) * $Radius + $XCenter;
1562
       $Y = sin($Angle * 3.1418 / 180 ) * $Radius + $YCenter;
1563
 
1564
       $this->drawLine($XCenter,$YCenter,$X,$Y,$A_R,$A_G,$A_B);
1565
 
1566
       $XOffset = 0; $YOffset = 0;
1567
       if (isset($Data[$i][$DataDescription["Position"]]))
1568
        {
1569
         $Label = $Data[$i][$DataDescription["Position"]];
1570
 
1571
         $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Label);
1572
         $Width  = $Positions[2] - $Positions[6];
1573
         $Height = $Positions[3] - $Positions[7];
1574
 
1575
         if ( $Angle >= 0 && $Angle <= 90 )
1576
          $YOffset = $Height;
1577
 
1578
         if ( $Angle > 90 && $Angle <= 180 )
1579
          { $YOffset = $Height; $XOffset = -$Width; }
1580
 
1581
         if ( $Angle > 180 && $Angle <= 270 )
1582
          { $XOffset = -$Width; }
1583
 
1584
         imagettftext($this->Picture,$this->FontSize,0,$X+$XOffset,$Y+$YOffset,$C_TextColor,$this->FontName,$Label);
1585
        }
1586
      }
1587
 
1588
     /* Write the values */
1589
     for ( $t=1; $t<=$MaxValue; $t++)
1590
      {
1591
       $TRadius = ( $Radius / $MaxValue ) * $t;
1592
 
1593
       $Angle = -90 + 360 / $Points;
1594
       $X1 = $XCenter;
1595
       $Y1 = $YCenter - $TRadius;
1596
       $X2 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter;
1597
       $Y2 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter;
1598
 
1599
       $XPos = floor(($X2-$X1)/2) + $X1;
1600
       $YPos = floor(($Y2-$Y1)/2) + $Y1;
1601
 
1602
       $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$t);
1603
       $X = $XPos - ( $X+$Positions[2] - $X+$Positions[6] ) / 2;
1604
       $Y = $YPos + $this->FontSize;
1605
 
1606
       $this->drawFilledRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,240,240,240);
1607
       $this->drawRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,220,220,220);
1608
       imagettftext($this->Picture,$this->FontSize,0,$X,$Y,$C_TextColor,$this->FontName,$t);
1609
      }
1610
    }
1611
 
1612
   /* This function draw a radar graph centered on the graph area */
1613
   function drawRadar(&$Data,&$DataDescription,$BorderOffset=10,$MaxValue=-1)
1614
    {
1615
     /* Validate the Data and DataDescription array */
1616
     $this->validateDataDescription("drawRadar",$DataDescription);
1617
     $this->validateData("drawRadar",$Data);
1618
 
1619
     $Points  = count($Data);
1620
     $Radius  = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
1621
     $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1;
1622
     $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1;
1623
 
1624
     /* Search for the max value */
1625
     if ( $MaxValue == -1 )
1626
      {
1627
       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1628
        {
1629
         foreach ( $Data as $Key => $Values )
1630
          {
1631
           if ( isset($Data[$Key][$ColName]))
1632
            if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; }
1633
          }
1634
        }
1635
      }
1636
 
1637
     $GraphID = 0;
1638
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1639
      {
1640
       $ID = 0;
1641
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1642
        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1643
 
1644
       $Angle = -90;
1645
       $XLast = -1;
1646
       foreach ( $Data as $Key => $Values )
1647
        {
1648
         if ( isset($Data[$Key][$ColName]))
1649
          {
1650
           $Value    = $Data[$Key][$ColName];
1651
           $Strength = ( $Radius / $MaxValue ) * $Value;
1652
 
1653
           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
1654
           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
1655
 
1656
           if ( $XLast != -1 )
1657
            $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1658
 
1659
           if ( $XLast == -1 )
1660
            { $FirstX = $XPos; $FirstY = $YPos; }
1661
 
1662
           $Angle = $Angle + (360/$Points);
1663
           $XLast = $XPos;
1664
           $YLast = $YPos;
1665
          }
1666
        }
1667
       $this->drawLine($XPos,$YPos,$FirstX,$FirstY,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1668
       $GraphID++;
1669
      }
1670
    }
1671
 
1672
   /* This function draw a radar graph centered on the graph area */
1673
   function drawFilledRadar(&$Data,&$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1)
1674
    {
1675
     /* Validate the Data and DataDescription array */
1676
     $this->validateDataDescription("drawFilledRadar",$DataDescription);
1677
     $this->validateData("drawFilledRadar",$Data);
1678
 
1679
     $Points      = count($Data);
1680
     $LayerWidth  = $this->GArea_X2-$this->GArea_X1;
1681
     $LayerHeight = $this->GArea_Y2-$this->GArea_Y1;
1682
     $Radius      = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset;
1683
     $XCenter     = ( $this->GArea_X2 - $this->GArea_X1 ) / 2;
1684
     $YCenter     = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2;
1685
 
1686
     /* Search for the max value */
1687
     if ( $MaxValue == -1 )
1688
      {
1689
       foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1690
        {
1691
         foreach ( $Data as $Key => $Values )
1692
          {
1693
           if ( isset($Data[$Key][$ColName]))
1694
            if ( $Data[$Key][$ColName] > $MaxValue && is_numeric($Data[$Key][$ColName])) { $MaxValue = $Data[$Key][$ColName]; }
1695
          }
1696
        }
1697
      }
1698
 
1699
     $GraphID = 0;
1700
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1701
      {
1702
       $ID = 0;
1703
       foreach ( $DataDescription["Description"] as $keyI => $ValueI )
1704
        { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; }
1705
 
1706
       $Angle = -90;
1707
       $XLast = -1;
1708
       $Plots = "";
1709
       foreach ( $Data as $Key => $Values )
1710
        {
1711
         if ( isset($Data[$Key][$ColName]))
1712
          {
1713
           $Value    = $Data[$Key][$ColName];
1714
           if ( !is_numeric($Value) ) { $Value = 0; }
1715
           $Strength = ( $Radius / $MaxValue ) * $Value;
1716
 
1717
           $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter;
1718
           $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter;
1719
 
1720
           $Plots[] = $XPos;
1721
           $Plots[] = $YPos;
1722
 
1723
           $Angle = $Angle + (360/$Points);
1724
           $XLast = $XPos;
1725
           $YLast = $YPos;
1726
          }
1727
        }
1728
 
1729
       if (isset($Plots[0]))
1730
        {
1731
         $Plots[] = $Plots[0];
1732
         $Plots[] = $Plots[1];
1733
 
1734
         $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
1735
         $C_White         = imagecolorallocate($this->Layers[0],255,255,255);
1736
         imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
1737
         imagecolortransparent($this->Layers[0],$C_White);
1738
 
1739
         $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]);
1740
         imagefilledpolygon($this->Layers[0],$Plots,(count($Plots)+1)/2,$C_Graph);
1741
 
1742
         imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha);
1743
         imagedestroy($this->Layers[0]);
1744
 
1745
         for($i=0;$i<=count($Plots)-4;$i=$i+2)
1746
          $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"]);
1747
        }
1748
 
1749
       $GraphID++;
1750
      }
1751
    }
1752
 
1753
   /* This function draw a flat pie chart */
1754
   function drawBasicPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0)
1755
    {
1756
     /* Validate the Data and DataDescription array */
1757
     $this->validateDataDescription("drawBasicPieGraph",$DataDescription,FALSE);
1758
     $this->validateData("drawBasicPieGraph",$Data);
1759
 
1760
     /* Determine pie sum */
1761
     $Series = 0; $PieSum = 0;
1762
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1763
      {
1764
       if ( $ColName != $DataDescription["Position"] )
1765
        {
1766
         $Series++;
1767
         foreach ( $Data as $Key => $Values )
1768
          {
1769
           if ( isset($Data[$Key][$ColName]))
1770
            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
1771
          }
1772
        }
1773
      }
1774
 
1775
     /* Validate serie */
1776
     if ( $Series != 1 )
1777
      RaiseFatal("Pie chart can only accept one serie of data.");
1778
 
1779
     $SpliceRatio         = 360 / $PieSum;
1780
     $SplicePercent       = 100 / $PieSum;
1781
 
1782
     /* Calculate all polygons */
1783
     $Angle    = 0; $TopPlots = "";
1784
     foreach($iValues as $Key => $Value)
1785
      {
1786
       $TopPlots[$Key][] = $XPos;
1787
       $TopPlots[$Key][] = $YPos;
1788
 
1789
       /* Process labels position & size */
1790
       if ( !($DrawLabels == PIE_NOLABEL) )
1791
        {
1792
         $TAngle   = $Angle+($Value*$SpliceRatio/2);
1793
         if ($DrawLabels == PIE_PERCENTAGE)
1794
          $Caption  = (floor($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
1795
         elseif ($DrawLabels == PIE_LABELS)
1796
          $Caption  = $iLabels[$Key];
1797
         $TX       = cos(($TAngle) * 3.1418 / 180 ) * ($Radius + 10)+ $XPos;
1798
         $TY       = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+ 10) + $YPos + 4;
1799
 
1800
         if ( $TAngle > 90 && $TAngle < 270 )
1801
          {
1802
           $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
1803
           $TextWidth = $Position[2]-$Position[0];
1804
           $TX = $TX - $TextWidth;
1805
          }
1806
 
1807
         $C_TextColor = imagecolorallocate($this->Picture,70,70,70);
1808
         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
1809
        }
1810
 
1811
       /* Process pie slices */
1812
       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
1813
        {
1814
         $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
1815
         $TopY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos;
1816
 
1817
         $TopPlots[$Key][] = $TopX;
1818
         $TopPlots[$Key][] = $TopY;
1819
        }
1820
 
1821
       $TopPlots[$Key][] = $XPos;
1822
       $TopPlots[$Key][] = $YPos;
1823
 
1824
       $Angle = $iAngle;
1825
      }
1826
     $PolyPlots = $TopPlots;
1827
 
1828
     /* Set array values type to float --- PHP Bug with imagefilledpolygon casting to integer */
1829
     foreach ($TopPlots as $Key => $Value)
1830
      { foreach ($TopPlots[$Key] as $Key2 => $Value2) { settype($TopPlots[$Key][$Key2],"float"); } }
1831
 
1832
     /* Draw Top polygons */
1833
     foreach ($PolyPlots as $Key => $Value)
1834
      {
1835
       $C_GraphLo = imagecolorallocate($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
1836
       imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
1837
      }
1838
 
1839
     $this->drawCircle($XPos-.5,$YPos-.5,$Radius,$R,$G,$B);
1840
     $this->drawCircle($XPos-.5,$YPos-.5,$Radius+.5,$R,$G,$B);
1841
 
1842
     /* Draw Top polygons */
1843
     foreach ($TopPlots as $Key => $Value)
1844
      {
1845
       for($j=0;$j<=count($TopPlots[$Key])-4;$j=$j+2)
1846
        $this->drawLine($TopPlots[$Key][$j],$TopPlots[$Key][$j+1],$TopPlots[$Key][$j+2],$TopPlots[$Key][$j+3],$R,$G,$B);
1847
      }
1848
    }
1849
 
1850
   /* This function draw a flat pie chart */
1851
   function drawFlatPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals = 0)
1852
    {
1853
     /* Validate the Data and DataDescription array */
1854
     $this->validateDataDescription("drawFlatPieGraph",$DataDescription,FALSE);
1855
     $this->validateData("drawFlatPieGraph",$Data);
1856
 
1857
     /* Determine pie sum */
1858
     $Series = 0; $PieSum = 0;
1859
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1860
      {
1861
       if ( $ColName != $DataDescription["Position"] )
1862
        {
1863
         $Series++;
1864
         foreach ( $Data as $Key => $Values )
1865
          {
1866
           if ( isset($Data[$Key][$ColName]))
1867
            $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]];
1868
          }
1869
        }
1870
      }
1871
 
1872
     /* Validate serie */
1873
     if ( $Series != 1 )
1874
      RaiseFatal("Pie chart can only accept one serie of data.");
1875
 
1876
     $SpliceDistanceRatio = $SpliceDistance;
1877
     $SpliceRatio         = (360 - $SpliceDistanceRatio * count($iValues) ) / $PieSum;
1878
     $SplicePercent       = 100 / $PieSum;
1879
 
1880
     /* Calculate all polygons */
1881
     $Angle    = 0; $TopPlots = "";
1882
     foreach($iValues as $Key => $Value)
1883
      {
1884
       $XCenterPos = cos(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos;
1885
       $YCenterPos = sin(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos;
1886
 
1887
       $TopPlots[$Key][] = $XCenterPos;
1888
       $TopPlots[$Key][] = $YCenterPos;
1889
 
1890
       /* Process labels position & size */
1891
       if ( !($DrawLabels == PIE_NOLABEL) )
1892
        {
1893
         $TAngle   = $Angle+($Value*$SpliceRatio/2);
1894
         if ($DrawLabels == PIE_PERCENTAGE)
1895
          $Caption  = (floor($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%";
1896
         elseif ($DrawLabels == PIE_LABELS)
1897
          $Caption  = $iLabels[$Key];
1898
         $TX       = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance)+$XPos;
1899
         $TY       = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $YPos + 4;
1900
 
1901
         if ( $TAngle > 90 && $TAngle < 270 )
1902
          {
1903
           $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
1904
           $TextWidth = $Position[2]-$Position[0];
1905
           $TX = $TX - $TextWidth;
1906
          }
1907
 
1908
         $C_TextColor = imagecolorallocate($this->Picture,70,70,70);
1909
         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
1910
        }
1911
 
1912
       /* Draw borders to correct imagefilledpolygon bug */
1913
       $BMax = 2;
1914
       for($i=-1;$i<=$BMax;$i++)
1915
        {
1916
         $BorderX1 = cos(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * ($SpliceDistance+$i) + $XPos;
1917
         $BorderY1 = sin(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * ($SpliceDistance+$i) + $YPos;
1918
         $BorderX2 = cos(($Angle+$i*.5) * 3.1418 / 180 ) * (($Radius+$BMax)+$SpliceDistance) + $XPos;
1919
         $BorderY2 = sin(($Angle+$i*.5) * 3.1418 / 180 ) * (($Radius+$BMax)+$SpliceDistance) + $YPos;
1920
         $this->drawLine($BorderX1,$BorderY1,$BorderX2,$BorderY2,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
1921
 
1922
         $BorderX1 = cos(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * ($SpliceDistance+$i) + $XPos;
1923
         $BorderY1 = sin(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * ($SpliceDistance+$i) + $YPos;
1924
         $BorderX2 = cos(($Angle-$i*.5+$Value*$SpliceRatio) * 3.1418 / 180 ) * (($Radius+$BMax)+$SpliceDistance) + $XPos;
1925
         $BorderY2 = sin(($Angle-$i*.5+$Value*$SpliceRatio) * 3.1418 / 180 ) * (($Radius+$BMax)+$SpliceDistance) + $YPos;
1926
         $this->drawLine($BorderX1,$BorderY1,$BorderX2,$BorderY2,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
1927
        }
1928
 
1929
       /* Process pie slices */
1930
       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
1931
        {
1932
         $TopX = cos($iAngle * 3.1418 / 180 ) * ($Radius+$SpliceDistance) + $XPos;
1933
         $TopY = sin($iAngle * 3.1418 / 180 ) * ($Radius+$SpliceDistance) + $YPos;
1934
 
1935
         $TopPlots[$Key][] = $TopX;
1936
         $TopPlots[$Key][] = $TopY;
1937
 
1938
         if ( $iAngle != $Angle )
1939
          {
1940
           for($i=-1;$i<=2;$i++)
1941
            {
1942
             $BorderX1 = cos(($iAngle-.5) * 3.1418 / 180 ) * (($Radius+$i)+$SpliceDistance) + $XPos;
1943
             $BorderY1 = sin(($iAngle-.5) * 3.1418 / 180 ) * (($Radius+$i)+$SpliceDistance) + $YPos;
1944
             $BorderX2 = cos($iAngle * 3.1418 / 180 ) * (($Radius+$i)+$SpliceDistance) + $XPos;
1945
             $BorderY2 = sin($iAngle * 3.1418 / 180 ) * (($Radius+$i)+$SpliceDistance) + $YPos;
1946
 
1947
             $this->drawLine($BorderX1,$BorderY1,$BorderX2,$BorderY2,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
1948
            }
1949
          }
1950
        }
1951
 
1952
       $TopPlots[$Key][] = $XCenterPos;
1953
       $TopPlots[$Key][] = $YCenterPos;
1954
 
1955
       $Angle = $iAngle + $SpliceDistanceRatio;
1956
      }
1957
     $PolyPlots = $TopPlots;
1958
 
1959
     /* Set array values type to float --- PHP Bug with imagefilledpolygon casting to integer */
1960
     foreach ($TopPlots as $Key => $Value)
1961
      { foreach ($TopPlots[$Key] as $Key2 => $Value2) { settype($TopPlots[$Key][$Key2],"float"); } }
1962
 
1963
     /* Draw Top polygons */
1964
     foreach ($TopPlots as $Key => $Value)
1965
      {
1966
       $C_GraphLo = imagecolorallocate($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
1967
       imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo);
1968
      }
1969
    }
1970
 
1971
   /* This function draw a pseudo-3D pie chart */
1972
   function drawPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$EnhanceColors=TRUE,$Skew=60,$SpliceHeight=20,$SpliceDistance=0,$Decimals=0)
1973
    {
1974
     /* Validate the Data and DataDescription array */
1975
     $this->validateDataDescription("drawPieGraph",$DataDescription,FALSE);
1976
     $this->validateData("drawPieGraph",$Data);
1977
 
1978
     /* Determine pie sum */
1979
     $Series = 0; $PieSum = 0; $rPieSum = 0;
1980
     foreach ( $DataDescription["Values"] as $Key2 => $ColName )
1981
      {
1982
       if ( $ColName != $DataDescription["Position"] )
1983
        {
1984
         $Series++;
1985
         foreach ( $Data as $Key => $Values )
1986
          if ( isset($Data[$Key][$ColName]))
1987
           {
1988
            if ( $Data[$Key][$ColName] == 0 )
1989
             { $PieSum++; $iValues[] = 1; $rValues[] = 0; }
1990
            else
1991
             { $PieSum += $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; $rValues[] = $Data[$Key][$ColName]; $rPieSum += $Data[$Key][$ColName];}
1992
           }
1993
        }
1994
      }
1995
 
1996
     /* Validate serie */
1997
     if ( $Series != 1 )
1998
      RaiseFatal("Pie chart can only accept one serie of data.");
1999
 
2000
     $SpliceDistanceRatio = $SpliceDistance;
2001
     $SkewHeight          = ($Radius * $Skew) / 100;
2002
     $SpliceRatio         = (360 - $SpliceDistanceRatio * count($iValues) ) / $PieSum;
2003
     $SplicePercent       = 100 / $PieSum;
2004
     $rSplicePercent      = 100 / $rPieSum;
2005
 
2006
     /* Calculate all polygons */
2007
     $Angle    = 0; $TopPlots = ""; $BotPlots = ""; $CDev = 5;
2008
     foreach($iValues as $Key => $Value)
2009
      {
2010
       $XCenterPos = cos(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos;
2011
       $YCenterPos = sin(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos;
2012
       $XCenterPos2 = cos(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos;
2013
       $YCenterPos2 = sin(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos;
2014
 
2015
       $TopPlots[$Key][] = $XCenterPos; $BotPlots[$Key][] = $XCenterPos;
2016
       $TopPlots[$Key][] = $YCenterPos; $BotPlots[$Key][] = $YCenterPos + $SpliceHeight;
2017
 
2018
       /* Process labels position & size */
2019
       if ( !($DrawLabels == PIE_NOLABEL) )
2020
        {
2021
         $TAngle   = $Angle+($Value*$SpliceRatio/2);
2022
         if ($DrawLabels == PIE_PERCENTAGE)
2023
          $Caption  = (floor($rValues[$Key] * pow(10,$Decimals) * $rSplicePercent)/pow(10,$Decimals))."%";
2024
         elseif ($DrawLabels == PIE_LABELS)
2025
          $Caption  = $iLabels[$Key];
2026
 
2027
         $TX       = cos(($TAngle) * 3.1418 / 180 ) * ($Radius + 10)+ $XPos;
2028
 
2029
         if ( $TAngle > 0 && $TAngle < 180 )
2030
          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 10) + $YPos + $SpliceHeight + 4;
2031
         else
2032
          $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 10) + $YPos + 4;
2033
 
2034
         if ( $TAngle > 90 && $TAngle < 270 )
2035
          {
2036
           $Position  = imageftbbox($this->FontSize,0,$this->FontName,$Caption);
2037
           $TextWidth = $Position[2]-$Position[0];
2038
           $TX = $TX - $TextWidth;
2039
          }
2040
 
2041
         $C_TextColor = imagecolorallocate($this->Picture,70,70,70);
2042
         imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption);
2043
        }
2044
 
2045
       /* Process pie slices */
2046
       for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5)
2047
        {
2048
         $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos;
2049
         $TopY = sin($iAngle * 3.1418 / 180 ) * $SkewHeight + $YPos;
2050
 
2051
         $TopPlots[$Key][] = $TopX; $BotPlots[$Key][] = $TopX;
2052
         $TopPlots[$Key][] = $TopY; $BotPlots[$Key][] = $TopY + $SpliceHeight;
2053
        }
2054
 
2055
       $TopPlots[$Key][] = $XCenterPos2; $BotPlots[$Key][] = $XCenterPos2;
2056
       $TopPlots[$Key][] = $YCenterPos2; $BotPlots[$Key][] = $YCenterPos2 + $SpliceHeight;
2057
 
2058
       $Angle = $iAngle + $SpliceDistanceRatio;
2059
      }
2060
 
2061
     /* Draw Bottom polygons */
2062
     foreach($iValues as $Key => $Value)
2063
      {
2064
       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-20);
2065
       imagefilledpolygon($this->Picture,$BotPlots[$Key],(count($BotPlots[$Key])+1)/2,$C_GraphLo);
2066
 
2067
       for($j=0;$j<=count($BotPlots[$Key])-4;$j=$j+2)
2068
        $this->drawLine($BotPlots[$Key][$j],$BotPlots[$Key][$j+1],$BotPlots[$Key][$j+2],$BotPlots[$Key][$j+3],$this->Palette[$Key]["R"]-20,$this->Palette[$Key]["G"]-20,$this->Palette[$Key]["B"]-20);
2069
      }
2070
 
2071
     /* Draw pie layers */
2072
     if ( $EnhanceColors ) { $ColorRatio = 30 / $SpliceHeight; } else { $ColorRatio = 25 / $SpliceHeight; }
2073
     for($i=$SpliceHeight-1;$i>=1;$i--)
2074
      {
2075
       foreach($iValues as $Key => $Value)
2076
        {
2077
         $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-10);
2078
         $Plots = ""; $Plot = 0;
2079
         foreach($TopPlots[$Key] as $Key2 => $Value2)
2080
          {
2081
           $Plot++;
2082
           if ( $Plot % 2 == 1 )
2083
            $Plots[] = $Value2;
2084
           else
2085
            $Plots[] = $Value2+$i;
2086
          }
2087
         imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_GraphLo);
2088
 
2089
         $Index       = count($Plots);
2090
         $ColorFactor = -20 + ($SpliceHeight - $i) * $ColorRatio;
2091
         $this->drawAntialiasPixel($Plots[0],$Plots[1],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2092
         $this->drawAntialiasPixel($Plots[2],$Plots[3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2093
         $this->drawAntialiasPixel($Plots[$Index-4],$Plots[$Index-3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor);
2094
        }
2095
      }
2096
 
2097
     /* Draw Top polygons */
2098
     for($Key=count($iValues)-1;$Key>=0;$Key--)
2099
      {
2100
       $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]);
2101
       imagefilledpolygon($this->Picture,$TopPlots[$Key],(count($TopPlots[$Key])+1)/2,$C_GraphLo);
2102
 
2103
       if ( $EnhanceColors ) { $En = 10; } else { $En = 5; }
2104
       for($j=0;$j<=count($TopPlots[$Key])-4;$j=$j+2)
2105
        $this->drawLine($TopPlots[$Key][$j],$TopPlots[$Key][$j+1],$TopPlots[$Key][$j+2],$TopPlots[$Key][$j+3],$this->Palette[$Key]["R"]+$En,$this->Palette[$Key]["G"]+$En,$this->Palette[$Key]["B"]+$En);
2106
      }
2107
    }
2108
 
2109
   /* This function can be used to set the background color */
2110
   function drawBackground($R,$G,$B)
2111
    {
2112
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2113
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2114
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2115
 
2116
     $C_Background = imagecolorallocate($this->Picture,$R,$G,$B);
2117
     imagefilledrectangle($this->Picture,0,0,$this->XSize,$this->YSize,$C_Background);
2118
    }
2119
 
2120
   /* This function create a rectangle with antialias */
2121
   function drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B)
2122
    {
2123
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2124
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2125
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2126
 
2127
     $C_Rectangle = imagecolorallocate($this->Picture,$R,$G,$B);
2128
 
2129
     $X1=$X1-.2;$Y1=$Y1-.2;
2130
     $X2=$X2+.2;$Y2=$Y2+.2;
2131
     $this->drawLine($X1,$Y1,$X2,$Y1,$R,$G,$B);
2132
     $this->drawLine($X2,$Y1,$X2,$Y2,$R,$G,$B);
2133
     $this->drawLine($X2,$Y2,$X1,$Y2,$R,$G,$B);
2134
     $this->drawLine($X1,$Y2,$X1,$Y1,$R,$G,$B);
2135
    }
2136
 
2137
   /* This function create a filled rectangle with antialias */
2138
   function drawFilledRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B,$DrawBorder=TRUE,$Alpha=100)
2139
    {
2140
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2141
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2142
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2143
 
2144
     if ( $Alpha == 100 )
2145
      {
2146
       $C_Rectangle = imagecolorallocate($this->Picture,$R,$G,$B);
2147
       imagefilledrectangle($this->Picture,$X1,$Y1,$X2,$Y2,$C_Rectangle);
2148
      }
2149
     else
2150
      {
2151
       $LayerWidth  = abs($X2-$X1)+2;
2152
       $LayerHeight = abs($Y2-$Y1)+2;
2153
 
2154
       $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight);
2155
       $C_White         = imagecolorallocate($this->Layers[0],255,255,255);
2156
       imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White);
2157
       imagecolortransparent($this->Layers[0],$C_White);
2158
 
2159
       $C_Rectangle = imagecolorallocate($this->Layers[0],$R,$G,$B);
2160
       imagefilledrectangle($this->Layers[0],1,1,$LayerWidth-1,$LayerHeight-1,$C_Rectangle);
2161
 
2162
       imagecopymerge($this->Picture,$this->Layers[0],min($X1,$X2)-1,min($Y1,$Y2)-1,0,0,$LayerWidth,$LayerHeight,$Alpha);
2163
       imagedestroy($this->Layers[0]);
2164
      }
2165
 
2166
     if ( $DrawBorder )
2167
      $this->drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B);
2168
    }
2169
 
2170
   /* This function create a rectangle with rounded corners and antialias */
2171
   function drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
2172
    {
2173
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2174
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2175
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2176
 
2177
     $C_Rectangle = imagecolorallocate($this->Picture,$R,$G,$B);
2178
 
2179
     $Step = 90 / ((3.1418 * $Radius)/2);
2180
 
2181
     for($i=0;$i<=90;$i=$i+$Step)
2182
      {
2183
       $X = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius;
2184
       $Y = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius;
2185
       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2186
 
2187
       $X = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius;
2188
       $Y = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius;
2189
       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2190
 
2191
       $X = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius;
2192
       $Y = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius;
2193
       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2194
 
2195
       $X = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius;
2196
       $Y = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius;
2197
       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2198
      }
2199
 
2200
     $X1=$X1-.2;$Y1=$Y1-.2;
2201
     $X2=$X2+.2;$Y2=$Y2+.2;
2202
     $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B);
2203
     $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B);
2204
     $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B);
2205
     $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B);
2206
    }
2207
 
2208
   /* This function create a filled rectangle with rounded corners and antialias */
2209
   function drawFilledRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B)
2210
    {
2211
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2212
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2213
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2214
 
2215
     $C_Rectangle = imagecolorallocate($this->Picture,$R,$G,$B);
2216
 
2217
     $Step = 90 / ((3.1418 * $Radius)/2);
2218
 
2219
     for($i=0;$i<=90;$i=$i+$Step)
2220
      {
2221
       $Xi1 = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius;
2222
       $Yi1 = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius;
2223
 
2224
       $Xi2 = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius;
2225
       $Yi2 = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius;
2226
 
2227
       $Xi3 = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius;
2228
       $Yi3 = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius;
2229
 
2230
       $Xi4 = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius;
2231
       $Yi4 = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius;
2232
 
2233
       imageline($this->Picture,$Xi1,$Yi1,$X1+$Radius,$Yi1,$C_Rectangle);
2234
       imageline($this->Picture,$X2-$Radius,$Yi2,$Xi2,$Yi2,$C_Rectangle);
2235
       imageline($this->Picture,$X2-$Radius,$Yi3,$Xi3,$Yi3,$C_Rectangle);
2236
       imageline($this->Picture,$Xi4,$Yi4,$X1+$Radius,$Yi4,$C_Rectangle);
2237
 
2238
       $this->drawAntialiasPixel($Xi1,$Yi1,$R,$G,$B);
2239
       $this->drawAntialiasPixel($Xi2,$Yi2,$R,$G,$B);
2240
       $this->drawAntialiasPixel($Xi3,$Yi3,$R,$G,$B);
2241
       $this->drawAntialiasPixel($Xi4,$Yi4,$R,$G,$B);
2242
      }
2243
 
2244
     imagefilledrectangle($this->Picture,$X1,$Y1+$Radius,$X2,$Y2-$Radius,$C_Rectangle);
2245
     imagefilledrectangle($this->Picture,$X1+$Radius,$Y1,$X2-$Radius,$Y2,$C_Rectangle);
2246
 
2247
     $X1=$X1-.2;$Y1=$Y1-.2;
2248
     $X2=$X2+.2;$Y2=$Y2+.2;
2249
     $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B);
2250
     $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B);
2251
     $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B);
2252
     $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B);
2253
    }
2254
 
2255
   /* This function create a circle with antialias */
2256
   function drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
2257
    {
2258
     if ( $Width == 0 ) { $Width = $Height; }
2259
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2260
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2261
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2262
 
2263
     $C_Circle = imagecolorallocate($this->Picture,$R,$G,$B);
2264
     $Step     = 360 / (2 * 3.1418 * max($Width,$Height));
2265
 
2266
     for($i=0;$i<=360;$i=$i+$Step)
2267
      {
2268
       $X = cos($i*3.1418/180) * $Height + $Xc;
2269
       $Y = sin($i*3.1418/180) * $Width + $Yc;
2270
       $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2271
      }
2272
    }
2273
 
2274
   /* This function create a filled circle/ellipse with antialias */
2275
   function drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0)
2276
    {
2277
     if ( $Width == 0 ) { $Width = $Height; }
2278
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2279
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2280
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2281
 
2282
     $C_Circle = imagecolorallocate($this->Picture,$R,$G,$B);
2283
     $Step     = 360 / (2 * 3.1418 * max($Width,$Height));
2284
 
2285
     for($i=90;$i<=270;$i=$i+$Step)
2286
      {
2287
       $X1 = cos($i*3.1418/180) * $Height + $Xc;
2288
       $Y1 = sin($i*3.1418/180) * $Width + $Yc;
2289
       $X2 = cos((180-$i)*3.1418/180) * $Height + $Xc;
2290
       $Y2 = sin((180-$i)*3.1418/180) * $Width + $Yc;
2291
 
2292
       $this->drawAntialiasPixel($X1-1,$Y1-1,$R,$G,$B);
2293
       $this->drawAntialiasPixel($X2-1,$Y2-1,$R,$G,$B);
2294
 
2295
       if ( ($Y1-1) > $Yc - max($Width,$Height) )
2296
        imageline($this->Picture,$X1,$Y1-1,$X2-1,$Y2-1,$C_Circle);
2297
      }
2298
    }
2299
 
2300
   /* This function will draw a filled ellipse */
2301
   function drawEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
2302
    { $this->drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); }
2303
 
2304
   /* This function will draw an ellipse */
2305
   function drawFilledEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B)
2306
    { $this->drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); }
2307
 
2308
   /* This function create a line with antialias */
2309
   function drawLine($X1,$Y1,$X2,$Y2,$R,$G,$B,$GraphFunction=FALSE)
2310
    {
2311
     if ( $this->LineDotSize > 1 ) { $this->drawDottedLine($X1,$Y1,$X2,$Y2,$this->LineDotSize,$R,$G,$B,$GraphFunction); return(0); }
2312
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2313
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2314
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2315
 
2316
     $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1));
2317
     if ( $Distance == 0 )
2318
      return(-1);
2319
     $XStep = ($X2-$X1) / $Distance;
2320
     $YStep = ($Y2-$Y1) / $Distance;
2321
 
2322
     for($i=0;$i<=$Distance;$i++)
2323
      {
2324
       $X = $i * $XStep + $X1;
2325
       $Y = $i * $YStep + $Y1;
2326
 
2327
       if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction )
2328
        {
2329
         if ( $this->LineWidth == 1 )
2330
          $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2331
         else
2332
          {
2333
           $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2);
2334
           for($j=$StartOffset;$j<=$EndOffset;$j++)
2335
            $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B);
2336
          }
2337
        }
2338
      }
2339
    }
2340
 
2341
   /* This function create a line with antialias */
2342
   function drawDottedLine($X1,$Y1,$X2,$Y2,$DotSize,$R,$G,$B,$GraphFunction=FALSE)
2343
    {
2344
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2345
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2346
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2347
 
2348
     $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1));
2349
 
2350
     $XStep = ($X2-$X1) / $Distance;
2351
     $YStep = ($Y2-$Y1) / $Distance;
2352
 
2353
     $DotIndex = 0;
2354
     for($i=0;$i<=$Distance;$i++)
2355
      {
2356
       $X = $i * $XStep + $X1;
2357
       $Y = $i * $YStep + $Y1;
2358
 
2359
       if ( $DotIndex <= $DotSize)
2360
        {
2361
         if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction )
2362
          {
2363
           if ( $this->LineWidth == 1 )
2364
            $this->drawAntialiasPixel($X,$Y,$R,$G,$B);
2365
           else
2366
            {
2367
             $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2);
2368
             for($j=$StartOffset;$j<=$EndOffset;$j++)
2369
              $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B);
2370
            }
2371
          }
2372
        }
2373
 
2374
       $DotIndex++;
2375
       if ( $DotIndex == $DotSize * 2 )
2376
        $DotIndex = 0;
2377
      }
2378
    }
2379
 
2380
   /* Load a PNG file and draw it over the chart */
2381
   function drawFromPNG($FileName,$X,$Y,$Alpha=100)
2382
    { $this->drawFromPicture(1,$FileName,$X,$Y,$Alpha); }
2383
 
2384
   /* Load a GIF file and draw it over the chart */
2385
   function drawFromGIF($FileName,$X,$Y,$Alpha=100)
2386
    { $this->drawFromPicture(2,$FileName,$X,$Y,$Alpha); }
2387
 
2388
   /* Load a JPEG file and draw it over the chart */
2389
   function drawFromJPG($FileName,$X,$Y,$Alpha=100)
2390
    { $this->drawFromPicture(3,$FileName,$X,$Y,$Alpha); }
2391
 
2392
   /* Generic loader function for external pictures */
2393
   function drawFromPicture($PicType,$FileName,$X,$Y,$Alpha=100)
2394
    {
2395
     if ( file_exists($FileName))
2396
      {
2397
       $Infos  = getimagesize($FileName);
2398
       $Width  = $Infos[0];
2399
       $Height = $Infos[1];
2400
       if ( $PicType == 1 ) { $Raster = imagecreatefrompng($FileName); }
2401
       if ( $PicType == 2 ) { $Raster = imagecreatefromgif($FileName); }
2402
       if ( $PicType == 3 ) { $Raster = imagecreatefromjpeg($FileName); }
2403
 
2404
       imagecopymerge($this->Picture,$Raster,$X,$Y,0,0,$Width,$Height,$Alpha);
2405
       imagedestroy($Raster);
2406
      }
2407
    }
2408
 
2409
   /* Draw an alpha pixel */
2410
   function drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B)
2411
    {
2412
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2413
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2414
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2415
 
2416
     if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize )
2417
      return(-1);
2418
 
2419
     $RGB2 = imagecolorat($this->Picture, $X, $Y);
2420
     $R2   = ($RGB2 >> 16) & 0xFF;
2421
     $G2   = ($RGB2 >> 8) & 0xFF;
2422
     $B2   = $RGB2 & 0xFF;
2423
 
2424
     $iAlpha = (100 - $Alpha)/100;
2425
     $Alpha  = $Alpha / 100;
2426
 
2427
     $Ra   = floor($R*$Alpha+$R2*$iAlpha);
2428
     $Ga   = floor($G*$Alpha+$G2*$iAlpha);
2429
     $Ba   = floor($B*$Alpha+$B2*$iAlpha);
2430
 
2431
     $C_Aliased = imagecolorallocate($this->Picture,$Ra,$Ga,$Ba);
2432
     imagesetpixel($this->Picture,$X,$Y,$C_Aliased);
2433
    }
2434
 
2435
   /* Color helper */
2436
   function AllocateColor($Picture,$R,$G,$B,$Factor=0)
2437
    {
2438
     $R = $R + $Factor;
2439
     $G = $G + $Factor;
2440
     $B = $B + $Factor;
2441
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2442
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2443
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2444
 
2445
     return(imagecolorallocate($Picture,$R,$G,$B));
2446
    }
2447
 
2448
   /* Render the current picture to a file */
2449
   function Render($FileName)
2450
    {
2451
     if ( $this->ErrorReporting )
2452
      $this->printErrors($this->ErrorInterface);
2453
 
2454
     imagepng($this->Picture,$FileName);
2455
    }
2456
 
2457
   /* Render the current picture to STDOUT */
2458
   function Stroke()
2459
    {
2460
     if ( $this->ErrorReporting )
2461
      $this->printErrors("GD");
2462
 
2463
     header('Content-type: image/png');
2464
     imagepng($this->Picture);
2465
    }
2466
 
2467
   /* Private functions for internal processing */
2468
   function drawAntialiasPixel($X,$Y,$R,$G,$B)
2469
    {
2470
     if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
2471
     if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
2472
     if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
2473
 
2474
     $Plot = "";
2475
     $Xi   = floor($X);
2476
     $Yi   = floor($Y);
2477
 
2478
     if ( $Xi == $X && $Yi == $Y)
2479
      {
2480
       /* $this->drawAlphaPixel($Xi,$Yi,0,$R,$G,$B); */
2481
       $C_Aliased = imagecolorallocate($this->Picture,$R,$G,$B);
2482
       imagesetpixel($this->Picture,$X,$Y,$C_Aliased);
2483
      }
2484
     else
2485
      {
2486
       $Alpha1 = (1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100;
2487
       if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); }
2488
 
2489
       $Alpha2 = ($X - floor($X)) * (1 - ($Y - floor($Y))) * 100;
2490
       if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); }
2491
 
2492
       $Alpha3 = (1 - ($X - floor($X))) * ($Y - floor($Y)) * 100;
2493
       if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); }
2494
 
2495
       $Alpha4 = ($X - floor($X)) * ($Y - floor($Y)) * 100;
2496
       if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); }
2497
      }
2498
    }
2499
 
2500
   /* Validate data contained in the description array */
2501
   function validateDataDescription($FunctionName,&$DataDescription,$DescriptionRequired=TRUE)
2502
    {
2503
     if (!isset($DataDescription["Position"]))
2504
      {
2505
       $this->Errors[] = "[Warning] ".$FunctionName." - Y Labels are not set.";
2506
       $DataDescription["Position"] = "Name";
2507
      }
2508
 
2509
     if ( $DescriptionRequired )
2510
      {
2511
       if (!isset($DataDescription["Description"]))
2512
        {
2513
         $this->Errors[] = "[Warning] ".$FunctionName." - Series descriptions are not set.";
2514
         foreach($DataDescription["Values"] as $key => $Value)
2515
          {
2516
           $DataDescription["Description"][$Value] = $Value;
2517
          }
2518
        }
2519
 
2520
       if (count($DataDescription["Description"]) < count($DataDescription["Values"]))
2521
        {
2522
         $this->Errors[] = "[Warning] ".$FunctionName." - Some series descriptions are not set.";
2523
         foreach($DataDescription["Values"] as $key => $Value)
2524
          {
2525
           if ( !isset($DataDescription["Description"][$Value]))
2526
            $DataDescription["Description"][$Value] = $Value;
2527
          }
2528
        }
2529
      }
2530
    }
2531
 
2532
   /* Validate data contained in the data array */
2533
   function validateData($FunctionName,&$Data)
2534
    {
2535
     $DataSummary = "";
2536
 
2537
     foreach($Data as $key => $Values)
2538
      {
2539
       foreach($Values as $key2 => $Value)
2540
        {
2541
         if (!isset($DataSummary[$key2]))
2542
          $DataSummary[$key2] = 1;
2543
         else
2544
          $DataSummary[$key2]++;
2545
        }
2546
      }
2547
 
2548
     if ( max($DataSummary) == 0 )
2549
      $this->Errors[] = "[Warning] ".$FunctionName." - No data set.";
2550
 
2551
     foreach($DataSummary as $key => $Value)
2552
      {
2553
       if ($Value < max($DataSummary))
2554
        {
2555
         $this->Errors[] = "[Warning] ".$FunctionName." - Missing data in serie ".$key.".";
2556
        }
2557
      }
2558
    }
2559
 
2560
   /* Print all error messages on the CLI or graphically */
2561
   function printErrors($Mode="CLI")
2562
    {
2563
     if (count($this->Errors) == 0)
2564
      return(0);
2565
 
2566
     if ( $Mode == "CLI" )
2567
      {
2568
       foreach($this->Errors as $key => $Value)
2569
        echo $Value."\r\n";
2570
      }
2571
     elseif ( $Mode == "GD" )
2572
      {
2573
       $this->setLineStyle($Width=1);
2574
       $MaxWidth = 0;
2575
       foreach($this->Errors as $key => $Value)
2576
        {
2577
         $Position  = imageftbbox($this->ErrorFontSize,0,$this->ErrorFontName,$Value);
2578
         $TextWidth = $Position[2]-$Position[0];
2579
         if ( $TextWidth > $MaxWidth ) { $MaxWidth = $TextWidth; }
2580
        }
2581
       $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);
2582
       $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);
2583
 
2584
       $C_TextColor = imagecolorallocate($this->Picture,133,85,85);
2585
       $YPos        = $this->YSize - (18 + (count($this->Errors)-1) * ($this->ErrorFontSize + 4));
2586
       foreach($this->Errors as $key => $Value)
2587
        {
2588
         imagettftext($this->Picture,$this->ErrorFontSize,0,$this->XSize-($MaxWidth+15),$YPos,$C_TextColor,$this->ErrorFontName,$Value);
2589
         $YPos = $YPos + ($this->ErrorFontSize + 4);
2590
        }
2591
      }
2592
    }
2593
 
2594
   /* Convert seconds to a time format string */
2595
   function ToTime($Value)
2596
    {
2597
     $Hour   = floor($Value/3600);
2598
     $Minute = floor(($Value - $Hour*3600)/60);
2599
     $Second = floor($Value - $Hour*3600 - $Minute*60);
2600
 
2601
     if (strlen($Hour) == 1 )   { $Hour = "0".$Hour; }
2602
     if (strlen($Minute) == 1 ) { $Minute = "0".$Minute; }
2603
     if (strlen($Second) == 1 ) { $Second = "0".$Second; }
2604
 
2605
     return($Hour.":".$Minute.":".$Second);
2606
    }
2607
 
2608
   /* Convert to metric system */
2609
   function ToMetric($Value)
2610
    {
2611
     $Go = floor($Value/1000000000);
2612
     $Mo = floor(($Value - $Go*1000000000)/1000000);
2613
     $Ko = floor(($Value - $Go*1000000000 - $Mo*1000000)/1000);
2614
     $o  = floor($Value - $Go*1000000000 - $Mo*1000000 - $Ko*1000);
2615
 
2616
     if ($Go != 0)   { return($Go.".".$Mo."g"); }
2617
     if ($Mo != 0)   { return($Mo.".".$ko."m"); }
2618
     if ($Ko != 0)   { return($Ko.".".$o)."k"; }
2619
     return($o);
2620
    }
2621
 
2622
   /* Set date format for axis labels */
2623
   function setDateFormat($Format)
2624
    {
2625
     $this->DateFormat = $Format;
2626
    }
2627
 
2628
   /* Convert TS to a date format string */
2629
   function ToDate($Value)
2630
    {
2631
     return(date($this->DateFormat,$Value));
2632
    }
2633
  }
2634
 
2635
 function RaiseFatal($Message)
2636
  {
2637
   echo "[FATAL] ".$Message."\r\n";
2638
   exit();
2639
  }
2640
?>