Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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