1<?php
2
3namespace CpChart;
4
5use Exception;
6
7/**
8 *  Draw - class extension with drawing methods
9 *
10 *  Version     : 2.1.4
11 *  Made by     : Jean-Damien POGOLOTTI
12 *  Last Update : 19/01/2014
13 *
14 *  This file can be distributed under the license you can find at :
15 *
16 *  http://www.pchart.net/license
17 *
18 *  You can find the whole class documentation on the pChart web site.
19 */
20abstract class Draw extends BaseDraw
21{
22    /**
23     * Draw a polygon
24     * @param array $Points
25     * @param array $Format
26     */
27    public function drawPolygon(array $Points, array $Format = [])
28    {
29        $R = isset($Format["R"]) ? $Format["R"] : 0;
30        $G = isset($Format["G"]) ? $Format["G"] : 0;
31        $B = isset($Format["B"]) ? $Format["B"] : 0;
32        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
33        $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : false;
34        $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : false;
35        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
36        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
37        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
38        $BorderAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $Alpha / 2;
39        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
40        $SkipX = isset($Format["SkipX"]) ? $Format["SkipX"] : OUT_OF_SIGHT;
41        $SkipY = isset($Format["SkipY"]) ? $Format["SkipY"] : OUT_OF_SIGHT;
42
43        /* Calling the ImageFilledPolygon() public function over the $Points array will round it */
44        $Backup = $Points;
45
46        if ($Surrounding != null) {
47            $BorderR = $R + $Surrounding;
48            $BorderG = $G + $Surrounding;
49            $BorderB = $B + $Surrounding;
50        }
51
52        if ($SkipX != OUT_OF_SIGHT) {
53            $SkipX = floor($SkipX);
54        }
55        if ($SkipY != OUT_OF_SIGHT) {
56            $SkipY = floor($SkipY);
57        }
58
59        $RestoreShadow = $this->Shadow;
60        if (!$NoFill) {
61            if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
62                $this->Shadow = false;
63                for ($i = 0; $i <= count($Points) - 1; $i = $i + 2) {
64                    $Shadow[] = $Points[$i] + $this->ShadowX;
65                    $Shadow[] = $Points[$i + 1] + $this->ShadowY;
66                }
67                $this->drawPolygon(
68                    $Shadow,
69                    [
70                        "R" => $this->ShadowR,
71                        "G" => $this->ShadowG,
72                        "B" => $this->ShadowB,
73                        "Alpha" => $this->Shadowa,
74                        "NoBorder" => true
75                    ]
76                );
77            }
78
79            $FillColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
80
81            if (count($Points) >= 6) {
82                ImageFilledPolygon($this->Picture, $Points, count($Points) / 2, $FillColor);
83            }
84        }
85
86        if (!$NoBorder) {
87            $Points = $Backup;
88
89            if ($NoFill) {
90                $BorderSettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
91            } else {
92                $BorderSettings = [
93                    "R" => $BorderR,
94                    "G" => $BorderG,
95                    "B" => $BorderB,
96                    "Alpha" => $BorderAlpha
97                ];
98            }
99
100            for ($i = 0; $i <= count($Points) - 1; $i = $i + 2) {
101                if (isset($Points[$i + 2])
102                    && !($Points[$i] == $Points[$i + 2] && $Points[$i] == $SkipX)
103                    && !($Points[$i + 1] == $Points[$i + 3] && $Points[$i + 1] == $SkipY)
104                ) {
105                    $this->drawLine(
106                        $Points[$i],
107                        $Points[$i + 1],
108                        $Points[$i + 2],
109                        $Points[$i + 3],
110                        $BorderSettings
111                    );
112                } elseif (!($Points[$i] == $Points[0] && $Points[$i] == $SkipX)
113                    && !($Points[$i + 1] == $Points[1] && $Points[$i + 1] == $SkipY)
114                ) {
115                    $this->drawLine($Points[$i], $Points[$i + 1], $Points[0], $Points[1], $BorderSettings);
116                }
117            }
118        }
119
120        $this->Shadow = $RestoreShadow;
121    }
122
123    /**
124     * Draw a rectangle with rounded corners
125     * @param int $X1
126     * @param int $Y1
127     * @param int $X2
128     * @param int $Y2
129     * @param int|float $Radius
130     * @param array $Format
131     * @return null|integer
132     */
133    public function drawRoundedRectangle($X1, $Y1, $X2, $Y2, $Radius, array $Format = [])
134    {
135        $R = isset($Format["R"]) ? $Format["R"] : 0;
136        $G = isset($Format["G"]) ? $Format["G"] : 0;
137        $B = isset($Format["B"]) ? $Format["B"] : 0;
138        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
139
140        list($X1, $Y1, $X2, $Y2) = $this->fixBoxCoordinates($X1, $Y1, $X2, $Y2);
141
142        if ($X2 - $X1 < $Radius) {
143            $Radius = floor((($X2 - $X1)) / 2);
144        }
145        if ($Y2 - $Y1 < $Radius) {
146            $Radius = floor((($Y2 - $Y1)) / 2);
147        }
148
149        $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "NoBorder" => true];
150
151        if ($Radius <= 0) {
152            $this->drawRectangle($X1, $Y1, $X2, $Y2, $Color);
153            return 0;
154        }
155
156        if ($this->Antialias) {
157            $this->drawLine($X1 + $Radius, $Y1, $X2 - $Radius, $Y1, $Color);
158            $this->drawLine($X2, $Y1 + $Radius, $X2, $Y2 - $Radius, $Color);
159            $this->drawLine($X2 - $Radius, $Y2, $X1 + $Radius, $Y2, $Color);
160            $this->drawLine($X1, $Y1 + $Radius, $X1, $Y2 - $Radius, $Color);
161        } else {
162            $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
163            imageline($this->Picture, $X1 + $Radius, $Y1, $X2 - $Radius, $Y1, $Color);
164            imageline($this->Picture, $X2, $Y1 + $Radius, $X2, $Y2 - $Radius, $Color);
165            imageline($this->Picture, $X2 - $Radius, $Y2, $X1 + $Radius, $Y2, $Color);
166            imageline($this->Picture, $X1, $Y1 + $Radius, $X1, $Y2 - $Radius, $Color);
167        }
168
169        $Step = 360 / (2 * PI * $Radius);
170        for ($i = 0; $i <= 90; $i = $i + $Step) {
171            $X = cos(($i + 180) * PI / 180) * $Radius + $X1 + $Radius;
172            $Y = sin(($i + 180) * PI / 180) * $Radius + $Y1 + $Radius;
173            $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
174
175            $X = cos(($i + 90) * PI / 180) * $Radius + $X1 + $Radius;
176            $Y = sin(($i + 90) * PI / 180) * $Radius + $Y2 - $Radius;
177            $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
178
179            $X = cos($i * PI / 180) * $Radius + $X2 - $Radius;
180            $Y = sin($i * PI / 180) * $Radius + $Y2 - $Radius;
181            $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
182
183            $X = cos(($i + 270) * PI / 180) * $Radius + $X2 - $Radius;
184            $Y = sin(($i + 270) * PI / 180) * $Radius + $Y1 + $Radius;
185            $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
186        }
187    }
188
189    /**
190     * Draw a rectangle with rounded corners
191     * @param int $X1
192     * @param int $Y1
193     * @param int $X2
194     * @param int $Y2
195     * @param int|float $Radius
196     * @param array $Format
197     * @return null|integer
198     */
199    public function drawRoundedFilledRectangle($X1, $Y1, $X2, $Y2, $Radius, array $Format = [])
200    {
201        $R = isset($Format["R"]) ? $Format["R"] : 0;
202        $G = isset($Format["G"]) ? $Format["G"] : 0;
203        $B = isset($Format["B"]) ? $Format["B"] : 0;
204        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
205        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
206        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
207        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
208        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
209
210        /* Temporary fix for AA issue */
211        $Y1 = floor($Y1);
212        $Y2 = floor($Y2);
213        $X1 = floor($X1);
214        $X2 = floor($X2);
215
216        if ($Surrounding != null) {
217            $BorderR = $R + $Surrounding;
218            $BorderG = $G + $Surrounding;
219            $BorderB = $B + $Surrounding;
220        }
221        if ($BorderR == -1) {
222            $BorderR = $R;
223            $BorderG = $G;
224            $BorderB = $B;
225        }
226
227        list($X1, $Y1, $X2, $Y2) = $this->fixBoxCoordinates($X1, $Y1, $X2, $Y2);
228
229        if ($X2 - $X1 < $Radius * 2) {
230            $Radius = floor((($X2 - $X1)) / 4);
231        }
232        if ($Y2 - $Y1 < $Radius * 2) {
233            $Radius = floor((($Y2 - $Y1)) / 4);
234        }
235
236        $RestoreShadow = $this->Shadow;
237        if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
238            $this->Shadow = false;
239            $this->drawRoundedFilledRectangle(
240                $X1 + $this->ShadowX,
241                $Y1 + $this->ShadowY,
242                $X2 + $this->ShadowX,
243                $Y2 + $this->ShadowY,
244                $Radius,
245                [
246                    "R" => $this->ShadowR,
247                    "G" => $this->ShadowG,
248                    "B" => $this->ShadowB,
249                    "Alpha" => $this->Shadowa
250                ]
251            );
252        }
253
254        $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "NoBorder" => true];
255
256        if ($Radius <= 0) {
257            $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Color);
258            return 0;
259        }
260
261        $YTop = $Y1 + $Radius;
262        $YBottom = $Y2 - $Radius;
263
264        $Step = 360 / (2 * PI * $Radius);
265        $Positions = [];
266        $Radius--;
267        $MinY = null;
268        $MaxY = null;
269        for ($i = 0; $i <= 90; $i = $i + $Step) {
270            $Xp1 = cos(($i + 180) * PI / 180) * $Radius + $X1 + $Radius;
271            $Xp2 = cos(((90 - $i) + 270) * PI / 180) * $Radius + $X2 - $Radius;
272            $Yp = floor(sin(($i + 180) * PI / 180) * $Radius + $YTop);
273            if (null === $MinY || $Yp > $MinY) {
274                $MinY = $Yp;
275            }
276
277            if ($Xp1 <= floor($X1)) {
278                $Xp1++;
279            }
280            if ($Xp2 >= floor($X2)) {
281                $Xp2--;
282            }
283            $Xp1++;
284
285            if (!isset($Positions[$Yp])) {
286                $Positions[$Yp]["X1"] = $Xp1;
287                $Positions[$Yp]["X2"] = $Xp2;
288            } else {
289                $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"] + $Xp1) / 2;
290                $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"] + $Xp2) / 2;
291            }
292
293            $Xp1 = cos(($i + 90) * PI / 180) * $Radius + $X1 + $Radius;
294            $Xp2 = cos((90 - $i) * PI / 180) * $Radius + $X2 - $Radius;
295            $Yp = floor(sin(($i + 90) * PI / 180) * $Radius + $YBottom);
296            if (null === $MaxY || $Yp < $MaxY) {
297                $MaxY = $Yp;
298            }
299
300            if ($Xp1 <= floor($X1)) {
301                $Xp1++;
302            }
303            if ($Xp2 >= floor($X2)) {
304                $Xp2--;
305            }
306            $Xp1++;
307
308            if (!isset($Positions[$Yp])) {
309                $Positions[$Yp]["X1"] = $Xp1;
310                $Positions[$Yp]["X2"] = $Xp2;
311            } else {
312                $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"] + $Xp1) / 2;
313                $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"] + $Xp2) / 2;
314            }
315        }
316
317        $ManualColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
318        foreach ($Positions as $Yp => $Bounds) {
319            $X1 = $Bounds["X1"];
320            $X1Dec = $this->getFirstDecimal($X1);
321            if ($X1Dec != 0) {
322                $X1 = floor($X1) + 1;
323            }
324            $X2 = $Bounds["X2"];
325            $X2Dec = $this->getFirstDecimal($X2);
326            if ($X2Dec != 0) {
327                $X2 = floor($X2) - 1;
328            }
329            imageline($this->Picture, $X1, $Yp, $X2, $Yp, $ManualColor);
330        }
331        $this->drawFilledRectangle($X1, $MinY + 1, floor($X2), $MaxY - 1, $Color);
332
333        $Radius++;
334        $this->drawRoundedRectangle(
335            $X1,
336            $Y1,
337            $X2 + 1,
338            $Y2 - 1,
339            $Radius,
340            ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]
341        );
342
343        $this->Shadow = $RestoreShadow;
344    }
345
346    /**
347     * Draw a rectangle
348     * @param int $X1
349     * @param int $Y1
350     * @param int $X2
351     * @param int $Y2
352     * @param array $Format
353     */
354    public function drawRectangle($X1, $Y1, $X2, $Y2, array $Format = [])
355    {
356        $R = isset($Format["R"]) ? $Format["R"] : 0;
357        $G = isset($Format["G"]) ? $Format["G"] : 0;
358        $B = isset($Format["B"]) ? $Format["B"] : 0;
359        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
360        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
361        $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : false;
362
363        if ($X1 > $X2) {
364            list($X1, $X2) = [$X2, $X1];
365        }
366        if ($Y1 > $Y2) {
367            list($Y1, $Y2) = [$Y2, $Y1];
368        }
369
370        if ($this->Antialias) {
371            if ($NoAngle) {
372                $this->drawLine(
373                    $X1 + 1,
374                    $Y1,
375                    $X2 - 1,
376                    $Y1,
377                    ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]
378                );
379                $this->drawLine(
380                    $X2,
381                    $Y1 + 1,
382                    $X2,
383                    $Y2 - 1,
384                    ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]
385                );
386                $this->drawLine(
387                    $X2 - 1,
388                    $Y2,
389                    $X1 + 1,
390                    $Y2,
391                    ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]
392                );
393                $this->drawLine(
394                    $X1,
395                    $Y1 + 1,
396                    $X1,
397                    $Y2 - 1,
398                    ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]
399                );
400            } else {
401                $this->drawLine(
402                    $X1 + 1,
403                    $Y1,
404                    $X2 - 1,
405                    $Y1,
406                    ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]
407                );
408                $this->drawLine(
409                    $X2,
410                    $Y1,
411                    $X2,
412                    $Y2,
413                    ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]
414                );
415                $this->drawLine(
416                    $X2 - 1,
417                    $Y2,
418                    $X1 + 1,
419                    $Y2,
420                    ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]
421                );
422                $this->drawLine(
423                    $X1,
424                    $Y1,
425                    $X1,
426                    $Y2,
427                    ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]
428                );
429            }
430        } else {
431            $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
432            imagerectangle($this->Picture, $X1, $Y1, $X2, $Y2, $Color);
433        }
434    }
435
436    /**
437     * Draw a filled rectangle
438     * @param int $X1
439     * @param int $Y1
440     * @param int $X2
441     * @param int $Y2
442     * @param array $Format
443     */
444    public function drawFilledRectangle($X1, $Y1, $X2, $Y2, array $Format = [])
445    {
446        $R = isset($Format["R"]) ? $Format["R"] : 0;
447        $G = isset($Format["G"]) ? $Format["G"] : 0;
448        $B = isset($Format["B"]) ? $Format["B"] : 0;
449        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
450        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
451        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
452        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
453        $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha;
454        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
455        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
456        $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : null;
457        $Dash = isset($Format["Dash"]) ? $Format["Dash"] : false;
458        $DashStep = isset($Format["DashStep"]) ? $Format["DashStep"] : 4;
459        $DashR = isset($Format["DashR"]) ? $Format["DashR"] : 0;
460        $DashG = isset($Format["DashG"]) ? $Format["DashG"] : 0;
461        $DashB = isset($Format["DashB"]) ? $Format["DashB"] : 0;
462        $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : false;
463
464        if ($Surrounding != null) {
465            $BorderR = $R + $Surrounding;
466            $BorderG = $G + $Surrounding;
467            $BorderB = $B + $Surrounding;
468        }
469
470        if ($X1 > $X2) {
471            list($X1, $X2) = [$X2, $X1];
472        }
473        if ($Y1 > $Y2) {
474            list($Y1, $Y2) = [$Y2, $Y1];
475        }
476
477        $RestoreShadow = $this->Shadow;
478        if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
479            $this->Shadow = false;
480            $this->drawFilledRectangle(
481                $X1 + $this->ShadowX,
482                $Y1 + $this->ShadowY,
483                $X2 + $this->ShadowX,
484                $Y2 + $this->ShadowY,
485                [
486                    "R" => $this->ShadowR,
487                    "G" => $this->ShadowG,
488                    "B" => $this->ShadowB,
489                    "Alpha" => $this->Shadowa,
490                    "Ticks" => $Ticks,
491                    "NoAngle" => $NoAngle
492                ]
493            );
494        }
495
496        $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
497        if ($NoAngle) {
498            imagefilledrectangle($this->Picture, ceil($X1) + 1, ceil($Y1), floor($X2) - 1, floor($Y2), $Color);
499            imageline($this->Picture, ceil($X1), ceil($Y1) + 1, ceil($X1), floor($Y2) - 1, $Color);
500            imageline($this->Picture, floor($X2), ceil($Y1) + 1, floor($X2), floor($Y2) - 1, $Color);
501        } else {
502            imagefilledrectangle($this->Picture, ceil($X1), ceil($Y1), floor($X2), floor($Y2), $Color);
503        }
504        if ($Dash) {
505            if ($BorderR != -1) {
506                $iX1 = $X1 + 1;
507                $iY1 = $Y1 + 1;
508                $iX2 = $X2 - 1;
509                $iY2 = $Y2 - 1;
510            } else {
511                $iX1 = $X1;
512                $iY1 = $Y1;
513                $iX2 = $X2;
514                $iY2 = $Y2;
515            }
516
517            $Color = $this->allocateColor($this->Picture, $DashR, $DashG, $DashB, $Alpha);
518            $Y = $iY1 - $DashStep;
519            for ($X = $iX1; $X <= $iX2 + ($iY2 - $iY1); $X = $X + $DashStep) {
520                $Y = $Y + $DashStep;
521                if ($X > $iX2) {
522                    $Xa = $X - ($X - $iX2);
523                    $Ya = $iY1 + ($X - $iX2);
524                } else {
525                    $Xa = $X;
526                    $Ya = $iY1;
527                }
528                if ($Y > $iY2) {
529                    $Xb = $iX1 + ($Y - $iY2);
530                    $Yb = $Y - ($Y - $iY2);
531                } else {
532                    $Xb = $iX1;
533                    $Yb = $Y;
534                }
535                imageline($this->Picture, $Xa, $Ya, $Xb, $Yb, $Color);
536            }
537        }
538
539        if ($this->Antialias && !$NoBorder) {
540            if ($X1 < ceil($X1)) {
541                $AlphaA = $Alpha * (ceil($X1) - $X1);
542                $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA);
543                imageline($this->Picture, ceil($X1) - 1, ceil($Y1), ceil($X1) - 1, floor($Y2), $Color);
544            }
545
546            if ($Y1 < ceil($Y1)) {
547                $AlphaA = $Alpha * (ceil($Y1) - $Y1);
548                $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA);
549                imageline($this->Picture, ceil($X1), ceil($Y1) - 1, floor($X2), ceil($Y1) - 1, $Color);
550            }
551
552            if ($X2 > floor($X2)) {
553                $AlphaA = $Alpha * (.5 - ($X2 - floor($X2)));
554                $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA);
555                imageline($this->Picture, floor($X2) + 1, ceil($Y1), floor($X2) + 1, floor($Y2), $Color);
556            }
557
558            if ($Y2 > floor($Y2)) {
559                $AlphaA = $Alpha * (.5 - ($Y2 - floor($Y2)));
560                $Color = $this->allocateColor($this->Picture, $R, $G, $B, $AlphaA);
561                imageline($this->Picture, ceil($X1), floor($Y2) + 1, floor($X2), floor($Y2) + 1, $Color);
562            }
563        }
564
565        if ($BorderR != -1) {
566            $this->drawRectangle(
567                $X1,
568                $Y1,
569                $X2,
570                $Y2,
571                [
572                    "R" => $BorderR,
573                    "G" => $BorderG,
574                    "B" => $BorderB,
575                    "Alpha" => $BorderAlpha,
576                    "Ticks" => $Ticks,
577                    "NoAngle" => $NoAngle
578                ]
579            );
580        }
581        $this->Shadow = $RestoreShadow;
582    }
583
584    /**
585     * Draw a rectangular marker of the specified size
586     * @param int $X
587     * @param int $Y
588     * @param array $Format
589     */
590    public function drawRectangleMarker($X, $Y, array $Format = [])
591    {
592        $Size = isset($Format["Size"]) ? $Format["Size"] : 4;
593
594        $HalfSize = floor($Size / 2);
595        $this->drawFilledRectangle($X - $HalfSize, $Y - $HalfSize, $X + $HalfSize, $Y + $HalfSize, $Format);
596    }
597
598    /**
599     * Drawn a spline based on the bezier public function
600     * @param array $Coordinates
601     * @param array $Format
602     * @return array
603     */
604    public function drawSpline(array $Coordinates, array $Format = [])
605    {
606        $R = isset($Format["R"]) ? $Format["R"] : 0;
607        $G = isset($Format["G"]) ? $Format["G"] : 0;
608        $B = isset($Format["B"]) ? $Format["B"] : 0;
609        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
610        $Force = isset($Format["Force"]) ? $Format["Force"] : 30;
611        $Forces = isset($Format["Forces"]) ? $Format["Forces"] : null;
612        $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : false;
613        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
614        $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : false;
615        $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
616
617        $Cpt = null;
618        $Mode = null;
619        $Result = [];
620        for ($i = 1; $i <= count($Coordinates) - 1; $i++) {
621            $X1 = $Coordinates[$i - 1][0];
622            $Y1 = $Coordinates[$i - 1][1];
623            $X2 = $Coordinates[$i][0];
624            $Y2 = $Coordinates[$i][1];
625
626            if ($Forces != null) {
627                $Force = $Forces[$i];
628            }
629
630            /* First segment */
631            if ($i == 1) {
632                $Xv1 = $X1;
633                $Yv1 = $Y1;
634            } else {
635                $Angle1 = $this->getAngle($XLast, $YLast, $X1, $Y1);
636                $Angle2 = $this->getAngle($X1, $Y1, $X2, $Y2);
637                $XOff = cos($Angle2 * PI / 180) * $Force + $X1;
638                $YOff = sin($Angle2 * PI / 180) * $Force + $Y1;
639
640                $Xv1 = cos($Angle1 * PI / 180) * $Force + $XOff;
641                $Yv1 = sin($Angle1 * PI / 180) * $Force + $YOff;
642            }
643
644            /* Last segment */
645            if ($i == count($Coordinates) - 1) {
646                $Xv2 = $X2;
647                $Yv2 = $Y2;
648            } else {
649                $Angle1 = $this->getAngle($X2, $Y2, $Coordinates[$i + 1][0], $Coordinates[$i + 1][1]);
650                $Angle2 = $this->getAngle($X1, $Y1, $X2, $Y2);
651                $XOff = cos(($Angle2 + 180) * PI / 180) * $Force + $X2;
652                $YOff = sin(($Angle2 + 180) * PI / 180) * $Force + $Y2;
653
654                $Xv2 = cos(($Angle1 + 180) * PI / 180) * $Force + $XOff;
655                $Yv2 = sin(($Angle1 + 180) * PI / 180) * $Force + $YOff;
656            }
657
658            $Path = $this->drawBezier($X1, $Y1, $X2, $Y2, $Xv1, $Yv1, $Xv2, $Yv2, $Format);
659            if ($PathOnly) {
660                $Result[] = $Path;
661            }
662
663            $XLast = $X1;
664            $YLast = $Y1;
665        }
666
667        return $Result;
668    }
669
670    /**
671     * Draw a bezier curve with two controls points
672     * @param int $X1
673     * @param int $Y1
674     * @param int $X2
675     * @param int $Y2
676     * @param int $Xv1
677     * @param int $Yv1
678     * @param int $Xv2
679     * @param int $Yv2
680     * @param array $Format
681     * @return array
682     */
683    public function drawBezier($X1, $Y1, $X2, $Y2, $Xv1, $Yv1, $Xv2, $Yv2, array $Format = [])
684    {
685        $R = isset($Format["R"]) ? $Format["R"] : 0;
686        $G = isset($Format["G"]) ? $Format["G"] : 0;
687        $B = isset($Format["B"]) ? $Format["B"] : 0;
688        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
689        $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : false;
690        $Segments = isset($Format["Segments"]) ? $Format["Segments"] : null;
691        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
692        $NoDraw = isset($Format["NoDraw"]) ? $Format["NoDraw"] : false;
693        $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : false;
694        $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
695        $DrawArrow = isset($Format["DrawArrow"]) ? $Format["DrawArrow"] : false;
696        $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 10;
697        $ArrowRatio = isset($Format["ArrowRatio"]) ? $Format["ArrowRatio"] : .5;
698        $ArrowTwoHeads = isset($Format["ArrowTwoHeads"]) ? $Format["ArrowTwoHeads"] : false;
699
700        if ($Segments == null) {
701            $Length = $this->getLength($X1, $Y1, $X2, $Y2);
702            $Precision = ($Length * 125) / 1000;
703        } else {
704            $Precision = $Segments;
705        }
706        $P[0]["X"] = $X1;
707        $P[0]["Y"] = $Y1;
708        $P[1]["X"] = $Xv1;
709        $P[1]["Y"] = $Yv1;
710        $P[2]["X"] = $Xv2;
711        $P[2]["Y"] = $Yv2;
712        $P[3]["X"] = $X2;
713        $P[3]["Y"] = $Y2;
714
715        /* Compute the bezier points */
716        $Q = [];
717        $ID = 0;
718        for ($i = 0; $i <= $Precision; $i = $i + 1) {
719            $u = $i / $Precision;
720
721            $C = [];
722            $C[0] = (1 - $u) * (1 - $u) * (1 - $u);
723            $C[1] = ($u * 3) * (1 - $u) * (1 - $u);
724            $C[2] = 3 * $u * $u * (1 - $u);
725            $C[3] = $u * $u * $u;
726
727            for ($j = 0; $j <= 3; $j++) {
728                if (!isset($Q[$ID])) {
729                    $Q[$ID] = [];
730                }
731                if (!isset($Q[$ID]["X"])) {
732                    $Q[$ID]["X"] = 0;
733                }
734                if (!isset($Q[$ID]["Y"])) {
735                    $Q[$ID]["Y"] = 0;
736                }
737
738                $Q[$ID]["X"] = $Q[$ID]["X"] + $P[$j]["X"] * $C[$j];
739                $Q[$ID]["Y"] = $Q[$ID]["Y"] + $P[$j]["Y"] * $C[$j];
740            }
741            $ID++;
742        }
743        $Q[$ID]["X"] = $X2;
744        $Q[$ID]["Y"] = $Y2;
745
746        if (!$NoDraw) {
747            /* Display the control points */
748            if ($ShowC && !$PathOnly) {
749                $Xv1 = floor($Xv1);
750                $Yv1 = floor($Yv1);
751                $Xv2 = floor($Xv2);
752                $Yv2 = floor($Yv2);
753
754                $this->drawLine($X1, $Y1, $X2, $Y2, ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 30]);
755
756                $MyMarkerSettings = [
757                    "R" => 255,
758                    "G" => 0,
759                    "B" => 0,
760                    "BorderR" => 255,
761                    "BorderB" => 255,
762                    "BorderG" => 255,
763                    "Size" => 4
764                ];
765                $this->drawRectangleMarker($Xv1, $Yv1, $MyMarkerSettings);
766                $this->drawText($Xv1 + 4, $Yv1, "v1");
767                $MyMarkerSettings = [
768                    "R" => 0,
769                    "G" => 0,
770                    "B" => 255,
771                    "BorderR" => 255,
772                    "BorderB" => 255,
773                    "BorderG" => 255,
774                    "Size" => 4
775                ];
776                $this->drawRectangleMarker($Xv2, $Yv2, $MyMarkerSettings);
777                $this->drawText($Xv2 + 4, $Yv2, "v2");
778            }
779
780            /* Draw the bezier */
781            $LastX = null;
782            $LastY = null;
783            $Cpt = null;
784            $Mode = null;
785            $ArrowS = [];
786            foreach ($Q as $Point) {
787                $X = $Point["X"];
788                $Y = $Point["Y"];
789
790                /* Get the first segment */
791                if (!count($ArrowS) && $LastX != null && $LastY != null) {
792                    $ArrowS["X2"] = $LastX;
793                    $ArrowS["Y2"] = $LastY;
794                    $ArrowS["X1"] = $X;
795                    $ArrowS["Y1"] = $Y;
796                }
797
798                if ($LastX != null && $LastY != null && !$PathOnly) {
799                    list($Cpt, $Mode) = $this->drawLine(
800                        $LastX,
801                        $LastY,
802                        $X,
803                        $Y,
804                        [
805                            "R" => $R,
806                            "G" => $G,
807                            "B" => $B,
808                            "Alpha" => $Alpha,
809                            "Ticks" => $Ticks,
810                            "Cpt" => $Cpt,
811                            "Mode" => $Mode,
812                            "Weight" => $Weight
813                        ]
814                    );
815                }
816                /* Get the last segment */
817                $ArrowE["X1"] = $LastX;
818                $ArrowE["Y1"] = $LastY;
819                $ArrowE["X2"] = $X;
820                $ArrowE["Y2"] = $Y;
821
822                $LastX = $X;
823                $LastY = $Y;
824            }
825
826            if ($DrawArrow && !$PathOnly) {
827                $ArrowSettings = [
828                    "FillR" => $R,
829                    "FillG" => $G,
830                    "FillB" => $B,
831                    "Alpha" => $Alpha,
832                    "Size" => $ArrowSize,
833                    "Ratio" => $ArrowRatio
834                ];
835                if ($ArrowTwoHeads) {
836                    $this->drawArrow($ArrowS["X1"], $ArrowS["Y1"], $ArrowS["X2"], $ArrowS["Y2"], $ArrowSettings);
837                }
838                $this->drawArrow($ArrowE["X1"], $ArrowE["Y1"], $ArrowE["X2"], $ArrowE["Y2"], $ArrowSettings);
839            }
840        }
841        return $Q;
842    }
843
844    /**
845     * Draw a line between two points
846     * @param int|float $X1
847     * @param int|float $Y1
848     * @param int|float $X2
849     * @param int|float $Y2
850     * @param array $Format
851     * @return array|int
852     */
853    public function drawLine($X1, $Y1, $X2, $Y2, array $Format = [])
854    {
855        $R = isset($Format["R"]) ? $Format["R"] : 0;
856        $G = isset($Format["G"]) ? $Format["G"] : 0;
857        $B = isset($Format["B"]) ? $Format["B"] : 0;
858        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
859        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
860        $Cpt = isset($Format["Cpt"]) ? $Format["Cpt"] : 1;
861        $Mode = isset($Format["Mode"]) ? $Format["Mode"] : 1;
862        $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
863        $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null;
864
865        if ($this->Antialias == false && $Ticks == null) {
866            if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
867                $ShadowColor = $this->allocateColor(
868                    $this->Picture,
869                    $this->ShadowR,
870                    $this->ShadowG,
871                    $this->ShadowB,
872                    $this->Shadowa
873                );
874                imageline(
875                    $this->Picture,
876                    $X1 + $this->ShadowX,
877                    $Y1 + $this->ShadowY,
878                    $X2 + $this->ShadowX,
879                    $Y2 + $this->ShadowY,
880                    $ShadowColor
881                );
882            }
883
884            $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
885            imageline($this->Picture, $X1, $Y1, $X2, $Y2, $Color);
886            return 0;
887        }
888
889        $Distance = sqrt(($X2 - $X1) * ($X2 - $X1) + ($Y2 - $Y1) * ($Y2 - $Y1));
890        if ($Distance == 0) {
891            return -1;
892        }
893
894        /* Derivative algorithm for overweighted lines, re-route to polygons primitives */
895        if ($Weight != null) {
896            $Angle = $this->getAngle($X1, $Y1, $X2, $Y2);
897            $PolySettings = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "BorderAlpha" => $Alpha];
898
899            if ($Ticks == null) {
900                $Points = [];
901                $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $X1;
902                $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Y1;
903                $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $X1;
904                $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Y1;
905                $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $X2;
906                $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Y2;
907                $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $X2;
908                $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Y2;
909
910                $this->drawPolygon($Points, $PolySettings);
911            } else {
912                for ($i = 0; $i <= $Distance; $i = $i + $Ticks * 2) {
913                    $Xa = (($X2 - $X1) / $Distance) * $i + $X1;
914                    $Ya = (($Y2 - $Y1) / $Distance) * $i + $Y1;
915                    $Xb = (($X2 - $X1) / $Distance) * ($i + $Ticks) + $X1;
916                    $Yb = (($Y2 - $Y1) / $Distance) * ($i + $Ticks) + $Y1;
917
918                    $Points = [];
919                    $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $Xa;
920                    $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Ya;
921                    $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $Xa;
922                    $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Ya;
923                    $Points[] = cos(deg2rad($Angle + 90)) * $Weight + $Xb;
924                    $Points[] = sin(deg2rad($Angle + 90)) * $Weight + $Yb;
925                    $Points[] = cos(deg2rad($Angle - 90)) * $Weight + $Xb;
926                    $Points[] = sin(deg2rad($Angle - 90)) * $Weight + $Yb;
927
928                    $this->drawPolygon($Points, $PolySettings);
929                }
930            }
931
932            return 1;
933        }
934
935        $XStep = ($X2 - $X1) / $Distance;
936        $YStep = ($Y2 - $Y1) / $Distance;
937
938        for ($i = 0; $i <= $Distance; $i++) {
939            $X = $i * $XStep + $X1;
940            $Y = $i * $YStep + $Y1;
941
942            $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
943
944            if ($Threshold != null) {
945                foreach ($Threshold as $Key => $Parameters) {
946                    if ($Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"]) {
947                        if (isset($Parameters["R"])) {
948                            $RT = $Parameters["R"];
949                        } else {
950                            $RT = 0;
951                        }
952                        if (isset($Parameters["G"])) {
953                            $GT = $Parameters["G"];
954                        } else {
955                            $GT = 0;
956                        }
957                        if (isset($Parameters["B"])) {
958                            $BT = $Parameters["B"];
959                        } else {
960                            $BT = 0;
961                        }
962                        if (isset($Parameters["Alpha"])) {
963                            $AlphaT = $Parameters["Alpha"];
964                        } else {
965                            $AlphaT = 0;
966                        }
967                        $Color = ["R" => $RT, "G" => $GT, "B" => $BT, "Alpha" => $AlphaT];
968                    }
969                }
970            }
971
972            if ($Ticks != null) {
973                if ($Cpt % $Ticks == 0) {
974                    $Cpt = 0;
975                    if ($Mode == 1) {
976                        $Mode = 0;
977                    } else {
978                        $Mode = 1;
979                    }
980                }
981
982                if ($Mode == 1) {
983                    $this->drawAntialiasPixel($X, $Y, $Color);
984                }
985                $Cpt++;
986            } else {
987                $this->drawAntialiasPixel($X, $Y, $Color);
988            }
989        }
990
991        return [$Cpt, $Mode];
992    }
993
994    /**
995     * Draw a circle
996     * @param int $Xc
997     * @param int $Yc
998     * @param int|float $Height
999     * @param int|float $Width
1000     * @param array $Format
1001     */
1002    public function drawCircle($Xc, $Yc, $Height, $Width, array $Format = [])
1003    {
1004        $R = isset($Format["R"]) ? $Format["R"] : 0;
1005        $G = isset($Format["G"]) ? $Format["G"] : 0;
1006        $B = isset($Format["B"]) ? $Format["B"] : 0;
1007        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1008        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
1009
1010        $Height = abs($Height);
1011        $Width = abs($Width);
1012
1013        if ($Height == 0) {
1014            $Height = 1;
1015        }
1016        if ($Width == 0) {
1017            $Width = 1;
1018        }
1019        $Xc = floor($Xc);
1020        $Yc = floor($Yc);
1021
1022        $RestoreShadow = $this->Shadow;
1023        if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1024            $this->Shadow = false;
1025            $this->drawCircle(
1026                $Xc + $this->ShadowX,
1027                $Yc + $this->ShadowY,
1028                $Height,
1029                $Width,
1030                [
1031                    "R" => $this->ShadowR,
1032                    "G" => $this->ShadowG,
1033                    "B" => $this->ShadowB,
1034                    "Alpha" => $this->Shadowa,
1035                    "Ticks" => $Ticks
1036                ]
1037            );
1038        }
1039
1040        if ($Width == 0) {
1041            $Width = $Height;
1042        }
1043        if ($R < 0) {
1044            $R = 0;
1045        } if ($R > 255) {
1046            $R = 255;
1047        }
1048        if ($G < 0) {
1049            $G = 0;
1050        } if ($G > 255) {
1051            $G = 255;
1052        }
1053        if ($B < 0) {
1054            $B = 0;
1055        } if ($B > 255) {
1056            $B = 255;
1057        }
1058
1059        $Step = 360 / (2 * PI * max($Width, $Height));
1060        $Mode = 1;
1061        $Cpt = 1;
1062        for ($i = 0; $i <= 360; $i = $i + $Step) {
1063            $X = cos($i * PI / 180) * $Height + $Xc;
1064            $Y = sin($i * PI / 180) * $Width + $Yc;
1065
1066            if ($Ticks != null) {
1067                if ($Cpt % $Ticks == 0) {
1068                    $Cpt = 0;
1069                    if ($Mode == 1) {
1070                        $Mode = 0;
1071                    } else {
1072                        $Mode = 1;
1073                    }
1074                }
1075
1076                if ($Mode == 1) {
1077                    $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
1078                }
1079                $Cpt++;
1080            } else {
1081                $this->drawAntialiasPixel($X, $Y, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
1082            }
1083        }
1084        $this->Shadow = $RestoreShadow;
1085    }
1086
1087    /**
1088     * Draw a filled circle
1089     * @param int $X
1090     * @param int $Y
1091     * @param int|float $Radius
1092     * @param array $Format
1093     */
1094    public function drawFilledCircle($X, $Y, $Radius, array $Format = [])
1095    {
1096        $R = isset($Format["R"]) ? $Format["R"] : 0;
1097        $G = isset($Format["G"]) ? $Format["G"] : 0;
1098        $B = isset($Format["B"]) ? $Format["B"] : 0;
1099        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1100        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
1101        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
1102        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
1103        $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha;
1104        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
1105        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
1106
1107        if ($Radius == 0) {
1108            $Radius = 1;
1109        }
1110        if ($Surrounding != null) {
1111            $BorderR = $R + $Surrounding;
1112            $BorderG = $G + $Surrounding;
1113            $BorderB = $B + $Surrounding;
1114        }
1115        $X = floor($X);
1116        $Y = floor($Y);
1117
1118        $Radius = abs($Radius);
1119
1120        $RestoreShadow = $this->Shadow;
1121        if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1122            $this->Shadow = false;
1123            $this->drawFilledCircle(
1124                $X + $this->ShadowX,
1125                $Y + $this->ShadowY,
1126                $Radius,
1127                [
1128                    "R" => $this->ShadowR,
1129                    "G" => $this->ShadowG,
1130                    "B" => $this->ShadowB,
1131                    "Alpha" => $this->Shadowa,
1132                    "Ticks" => $Ticks
1133                ]
1134            );
1135        }
1136
1137        $this->Mask = [];
1138        $Color = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
1139        for ($i = 0; $i <= $Radius * 2; $i++) {
1140            $Slice = sqrt($Radius * $Radius - ($Radius - $i) * ($Radius - $i));
1141            $XPos = floor($Slice);
1142            $YPos = $Y + $i - $Radius;
1143            $AAlias = $Slice - floor($Slice);
1144
1145            $this->Mask[$X - $XPos][$YPos] = true;
1146            $this->Mask[$X + $XPos][$YPos] = true;
1147            imageline($this->Picture, $X - $XPos, $YPos, $X + $XPos, $YPos, $Color);
1148        }
1149        if ($this->Antialias) {
1150            $this->drawCircle(
1151                $X,
1152                $Y,
1153                $Radius,
1154                $Radius,
1155                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks]
1156            );
1157        }
1158        $this->Mask = [];
1159
1160        if ($BorderR != -1) {
1161            $this->drawCircle(
1162                $X,
1163                $Y,
1164                $Radius,
1165                $Radius,
1166                [
1167                    "R" => $BorderR,
1168                    "G" => $BorderG,
1169                    "B" => $BorderB,
1170                    "Alpha" => $BorderAlpha,
1171                    "Ticks" => $Ticks
1172                ]
1173            );
1174        }
1175        $this->Shadow = $RestoreShadow;
1176    }
1177
1178    /**
1179     * Write text
1180     * @param int|float $X
1181     * @param int|float $Y
1182     * @param string $Text
1183     * @param array $Format
1184     * @return array
1185     */
1186    public function drawText($X, $Y, $Text, array $Format = [])
1187    {
1188        $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR;
1189        $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG;
1190        $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB;
1191        $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0;
1192        $Align = isset($Format["Align"]) ? $Format["Align"] : TEXT_ALIGN_BOTTOMLEFT;
1193        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->FontColorA;
1194        $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName;
1195        $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
1196        $ShowOrigine = isset($Format["ShowOrigine"]) ? $Format["ShowOrigine"] : false;
1197        $TOffset = isset($Format["TOffset"]) ? $Format["TOffset"] : 2;
1198        $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : false;
1199        $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 6;
1200        $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : false;
1201        $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 6;
1202        $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 255;
1203        $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 255;
1204        $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 255;
1205        $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50;
1206        $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
1207        $BoxBorderR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
1208        $BoxBorderG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
1209        $BoxBorderB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
1210        $BoxBorderAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50;
1211        $NoShadow = isset($Format["NoShadow"]) ? $Format["NoShadow"] : false;
1212
1213        $Shadow = $this->Shadow;
1214        if ($NoShadow) {
1215            $this->Shadow = false;
1216        }
1217
1218        if ($BoxSurrounding != "") {
1219            $BoxBorderR = $BoxR - $BoxSurrounding;
1220            $BoxBorderG = $BoxG - $BoxSurrounding;
1221            $BoxBorderB = $BoxB - $BoxSurrounding;
1222            $BoxBorderAlpha = $BoxAlpha;
1223        }
1224
1225        if ($ShowOrigine) {
1226            $MyMarkerSettings = [
1227                "R" => 255,
1228                "G" => 0,
1229                "B" => 0,
1230                "BorderR" => 255,
1231                "BorderB" => 255,
1232                "BorderG" => 255,
1233                "Size" => 4
1234            ];
1235            $this->drawRectangleMarker($X, $Y, $MyMarkerSettings);
1236        }
1237
1238        $TxtPos = $this->getTextBox($X, $Y, $FontName, $FontSize, $Angle, $Text);
1239
1240        if ($DrawBox && ($Angle == 0 || $Angle == 90 || $Angle == 180 || $Angle == 270)) {
1241            $T[0]["X"] = 0;
1242            $T[0]["Y"] = 0;
1243            $T[1]["X"] = 0;
1244            $T[1]["Y"] = 0;
1245            $T[2]["X"] = 0;
1246            $T[2]["Y"] = 0;
1247            $T[3]["X"] = 0;
1248            $T[3]["Y"] = 0;
1249            if ($Angle == 0) {
1250                $T[0]["X"] = -$TOffset;
1251                $T[0]["Y"] = $TOffset;
1252                $T[1]["X"] = $TOffset;
1253                $T[1]["Y"] = $TOffset;
1254                $T[2]["X"] = $TOffset;
1255                $T[2]["Y"] = -$TOffset;
1256                $T[3]["X"] = -$TOffset;
1257                $T[3]["Y"] = -$TOffset;
1258            }
1259
1260            $X1 = min($TxtPos[0]["X"], $TxtPos[1]["X"], $TxtPos[2]["X"], $TxtPos[3]["X"]) - $BorderOffset + 3;
1261            $Y1 = min($TxtPos[0]["Y"], $TxtPos[1]["Y"], $TxtPos[2]["Y"], $TxtPos[3]["Y"]) - $BorderOffset;
1262            $X2 = max($TxtPos[0]["X"], $TxtPos[1]["X"], $TxtPos[2]["X"], $TxtPos[3]["X"]) + $BorderOffset + 3;
1263            $Y2 = max($TxtPos[0]["Y"], $TxtPos[1]["Y"], $TxtPos[2]["Y"], $TxtPos[3]["Y"]) + $BorderOffset - 3;
1264
1265            $X1 = $X1 - $TxtPos[$Align]["X"] + $X + $T[0]["X"];
1266            $Y1 = $Y1 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"];
1267            $X2 = $X2 - $TxtPos[$Align]["X"] + $X + $T[0]["X"];
1268            $Y2 = $Y2 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"];
1269
1270            $Settings = [
1271                "R" => $BoxR,
1272                "G" => $BoxG,
1273                "B" => $BoxB,
1274                "Alpha" => $BoxAlpha,
1275                "BorderR" => $BoxBorderR,
1276                "BorderG" => $BoxBorderG,
1277                "BorderB" => $BoxBorderB,
1278                "BorderAlpha" => $BoxBorderAlpha
1279            ];
1280
1281            if ($BoxRounded) {
1282                $this->drawRoundedFilledRectangle($X1, $Y1, $X2, $Y2, $RoundedRadius, $Settings);
1283            } else {
1284                $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Settings);
1285            }
1286        }
1287
1288        $X = $X - $TxtPos[$Align]["X"] + $X;
1289        $Y = $Y - $TxtPos[$Align]["Y"] + $Y;
1290
1291        if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1292            $C_ShadowColor = $this->allocateColor(
1293                $this->Picture,
1294                $this->ShadowR,
1295                $this->ShadowG,
1296                $this->ShadowB,
1297                $this->Shadowa
1298            );
1299            imagettftext(
1300                $this->Picture,
1301                $FontSize,
1302                $Angle,
1303                $X + $this->ShadowX,
1304                $Y + $this->ShadowY,
1305                $C_ShadowColor,
1306                $FontName,
1307                $Text
1308            );
1309        }
1310
1311        $C_TextColor = $this->AllocateColor($this->Picture, $R, $G, $B, $Alpha);
1312        imagettftext($this->Picture, $FontSize, $Angle, $X, $Y, $C_TextColor, $FontName, $Text);
1313
1314        $this->Shadow = $Shadow;
1315
1316        return $TxtPos;
1317    }
1318
1319    /**
1320     * Draw a gradient within a defined area
1321     * @param int $X1
1322     * @param int $Y1
1323     * @param int $X2
1324     * @param int $Y2
1325     * @param int $Direction
1326     * @param array $Format
1327     * @return null|integer
1328     */
1329    public function drawGradientArea($X1, $Y1, $X2, $Y2, $Direction, array $Format = [])
1330    {
1331        $StartR = isset($Format["StartR"]) ? $Format["StartR"] : 90;
1332        $StartG = isset($Format["StartG"]) ? $Format["StartG"] : 90;
1333        $StartB = isset($Format["StartB"]) ? $Format["StartB"] : 90;
1334        $EndR = isset($Format["EndR"]) ? $Format["EndR"] : 0;
1335        $EndG = isset($Format["EndG"]) ? $Format["EndG"] : 0;
1336        $EndB = isset($Format["EndB"]) ? $Format["EndB"] : 0;
1337        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1338        $Levels = isset($Format["Levels"]) ? $Format["Levels"] : null;
1339
1340        $Shadow = $this->Shadow;
1341        $this->Shadow = false;
1342
1343        if ($StartR == $EndR && $StartG == $EndG && $StartB == $EndB) {
1344            $this->drawFilledRectangle(
1345                $X1,
1346                $Y1,
1347                $X2,
1348                $Y2,
1349                ["R" => $StartR, "G" => $StartG, "B" => $StartB, "Alpha" => $Alpha]
1350            );
1351            return 0;
1352        }
1353
1354        if ($Levels != null) {
1355            $EndR = $StartR + $Levels;
1356            $EndG = $StartG + $Levels;
1357            $EndB = $StartB + $Levels;
1358        }
1359
1360        if ($X1 > $X2) {
1361            list($X1, $X2) = [$X2, $X1];
1362        }
1363        if ($Y1 > $Y2) {
1364            list($Y1, $Y2) = [$Y2, $Y1];
1365        }
1366
1367        if ($Direction == DIRECTION_VERTICAL) {
1368            $Width = abs($Y2 - $Y1);
1369        }
1370        if ($Direction == DIRECTION_HORIZONTAL) {
1371            $Width = abs($X2 - $X1);
1372        }
1373
1374        $Step = max(abs($EndR - $StartR), abs($EndG - $StartG), abs($EndB - $StartB));
1375        $StepSize = $Width / $Step;
1376        $RStep = ($EndR - $StartR) / $Step;
1377        $GStep = ($EndG - $StartG) / $Step;
1378        $BStep = ($EndB - $StartB) / $Step;
1379
1380        $R = $StartR;
1381        $G = $StartG;
1382        $B = $StartB;
1383        switch ($Direction) {
1384            case DIRECTION_VERTICAL:
1385                $StartY = $Y1;
1386                $EndY = floor($Y2) + 1;
1387                $LastY2 = $StartY;
1388                for ($i = 0; $i <= $Step; $i++) {
1389                    $Y2 = floor($StartY + ($i * $StepSize));
1390
1391                    if ($Y2 > $EndY) {
1392                        $Y2 = $EndY;
1393                    }
1394                    if (($Y1 != $Y2 && $Y1 < $Y2) || $Y2 == $EndY) {
1395                        $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
1396                        $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Color);
1397                        $LastY2 = max($LastY2, $Y2);
1398                        $Y1 = $Y2 + 1;
1399                    }
1400                    $R = $R + $RStep;
1401                    $G = $G + $GStep;
1402                    $B = $B + $BStep;
1403                }
1404                if ($LastY2 < $EndY && isset($Color)) {
1405                    for ($i = $LastY2 + 1; $i <= $EndY; $i++) {
1406                        $this->drawLine($X1, $i, $X2, $i, $Color);
1407                    }
1408                }
1409                break;
1410
1411            case DIRECTION_HORIZONTAL:
1412                $StartX = $X1;
1413                $EndX = $X2;
1414                for ($i = 0; $i <= $Step; $i++) {
1415                    $X2 = floor($StartX + ($i * $StepSize));
1416
1417                    if ($X2 > $EndX) {
1418                        $X2 = $EndX;
1419                    }
1420                    if (($X1 != $X2 && $X1 < $X2) || $X2 == $EndX) {
1421                        $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
1422                        $this->drawFilledRectangle($X1, $Y1, $X2, $Y2, $Color);
1423                        $X1 = $X2 + 1;
1424                    }
1425                    $R = $R + $RStep;
1426                    $G = $G + $GStep;
1427                    $B = $B + $BStep;
1428                }
1429                if ($X2 < $EndX && isset($Color)) {
1430                    $this->drawFilledRectangle($X2, $Y1, $EndX, $Y2, $Color);
1431                }
1432                break;
1433        }
1434
1435        $this->Shadow = $Shadow;
1436    }
1437
1438    /**
1439     * Draw an aliased pixel
1440     * @param int $X
1441     * @param int $Y
1442     * @param array $Format
1443     * @return int|null
1444     */
1445    public function drawAntialiasPixel($X, $Y, array $Format = [])
1446    {
1447        $R = isset($Format["R"]) ? $Format["R"] : 0;
1448        $G = isset($Format["G"]) ? $Format["G"] : 0;
1449        $B = isset($Format["B"]) ? $Format["B"] : 0;
1450        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1451
1452        if ($X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize) {
1453            return -1;
1454        }
1455
1456        if ($R < 0) {
1457            $R = 0;
1458        } if ($R > 255) {
1459            $R = 255;
1460        }
1461        if ($G < 0) {
1462            $G = 0;
1463        } if ($G > 255) {
1464            $G = 255;
1465        }
1466        if ($B < 0) {
1467            $B = 0;
1468        } if ($B > 255) {
1469            $B = 255;
1470        }
1471
1472        if (!$this->Antialias) {
1473            if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1474                $ShadowColor = $this->allocateColor(
1475                    $this->Picture,
1476                    $this->ShadowR,
1477                    $this->ShadowG,
1478                    $this->ShadowB,
1479                    $this->Shadowa
1480                );
1481                imagesetpixel($this->Picture, $X + $this->ShadowX, $Y + $this->ShadowY, $ShadowColor);
1482            }
1483
1484            $PlotColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
1485            imagesetpixel($this->Picture, $X, $Y, $PlotColor);
1486
1487            return 0;
1488        }
1489
1490        $Xi = floor($X);
1491        $Yi = floor($Y);
1492
1493        if ($Xi == $X && $Yi == $Y) {
1494            if ($Alpha == 100) {
1495                $this->drawAlphaPixel($X, $Y, 100, $R, $G, $B);
1496            } else {
1497                $this->drawAlphaPixel($X, $Y, $Alpha, $R, $G, $B);
1498            }
1499        } else {
1500            $Alpha1 = (((1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
1501            if ($Alpha1 > $this->AntialiasQuality) {
1502                $this->drawAlphaPixel($Xi, $Yi, $Alpha1, $R, $G, $B);
1503            }
1504
1505            $Alpha2 = ((($X - floor($X)) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
1506            if ($Alpha2 > $this->AntialiasQuality) {
1507                $this->drawAlphaPixel($Xi + 1, $Yi, $Alpha2, $R, $G, $B);
1508            }
1509
1510            $Alpha3 = (((1 - ($X - floor($X))) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
1511            if ($Alpha3 > $this->AntialiasQuality) {
1512                $this->drawAlphaPixel($Xi, $Yi + 1, $Alpha3, $R, $G, $B);
1513            }
1514
1515            $Alpha4 = ((($X - floor($X)) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
1516            if ($Alpha4 > $this->AntialiasQuality) {
1517                $this->drawAlphaPixel($Xi + 1, $Yi + 1, $Alpha4, $R, $G, $B);
1518            }
1519        }
1520    }
1521
1522    /**
1523     * Draw a semi-transparent pixel
1524     * @param int $X
1525     * @param int $Y
1526     * @param int $Alpha
1527     * @param int $R
1528     * @param int $G
1529     * @param int $B
1530     * @return null|integer
1531     */
1532    public function drawAlphaPixel($X, $Y, $Alpha, $R, $G, $B)
1533    {
1534        if (isset($this->Mask[$X]) && isset($this->Mask[$X][$Y])) {
1535            return 0;
1536        }
1537
1538        if ($X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize) {
1539            return -1;
1540        }
1541        if ($R < 0) {
1542            $R = 0;
1543        } if ($R > 255) {
1544            $R = 255;
1545        }
1546        if ($G < 0) {
1547            $G = 0;
1548        } if ($G > 255) {
1549            $G = 255;
1550        }
1551        if ($B < 0) {
1552            $B = 0;
1553        } if ($B > 255) {
1554            $B = 255;
1555        }
1556
1557        if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1558            $AlphaFactor = floor(($Alpha / 100) * $this->Shadowa);
1559            $ShadowColor = $this->allocateColor(
1560                $this->Picture,
1561                $this->ShadowR,
1562                $this->ShadowG,
1563                $this->ShadowB,
1564                $AlphaFactor
1565            );
1566            imagesetpixel($this->Picture, $X + $this->ShadowX, $Y + $this->ShadowY, $ShadowColor);
1567        }
1568
1569        $C_Aliased = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
1570        imagesetpixel($this->Picture, $X, $Y, $C_Aliased);
1571    }
1572
1573    /**
1574     * Load a PNG file and draw it over the chart
1575     * @param int $X
1576     * @param int $Y
1577     * @param string $FileName
1578     */
1579    public function drawFromPNG($X, $Y, $FileName)
1580    {
1581        $this->drawFromPicture(1, $FileName, $X, $Y);
1582    }
1583
1584    /**
1585     * Load a GIF file and draw it over the chart
1586     * @param int $X
1587     * @param int $Y
1588     * @param string $FileName
1589     */
1590    public function drawFromGIF($X, $Y, $FileName)
1591    {
1592        $this->drawFromPicture(2, $FileName, $X, $Y);
1593    }
1594
1595    /**
1596     * Load a JPEG file and draw it over the chart
1597     * @param int $X
1598     * @param int $Y
1599     * @param string $FileName
1600     */
1601    public function drawFromJPG($X, $Y, $FileName)
1602    {
1603        $this->drawFromPicture(3, $FileName, $X, $Y);
1604    }
1605
1606    /**
1607     * Generic loader public function for external pictures
1608     * @param int $PicType
1609     * @param string $FileName
1610     * @param int $X
1611     * @param int $Y
1612     * @return null|integer
1613     */
1614    public function drawFromPicture($PicType, $FileName, $X, $Y)
1615    {
1616        if (file_exists($FileName)) {
1617            list($Width, $Height) = $this->getPicInfo($FileName);
1618
1619            if ($PicType == 1) {
1620                $Raster = imagecreatefrompng($FileName);
1621            } elseif ($PicType == 2) {
1622                $Raster = imagecreatefromgif($FileName);
1623            } elseif ($PicType == 3) {
1624                $Raster = imagecreatefromjpeg($FileName);
1625            } else {
1626                return 0;
1627            }
1628
1629            $RestoreShadow = $this->Shadow;
1630            if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1631                $this->Shadow = false;
1632                if ($PicType == 3) {
1633                    $this->drawFilledRectangle(
1634                        $X + $this->ShadowX,
1635                        $Y + $this->ShadowY,
1636                        $X + $Width + $this->ShadowX,
1637                        $Y + $Height + $this->ShadowY,
1638                        [
1639                            "R" => $this->ShadowR,
1640                            "G" => $this->ShadowG,
1641                            "B" => $this->ShadowB,
1642                            "Alpha" => $this->Shadowa
1643                        ]
1644                    );
1645                } else {
1646                    $TranparentID = imagecolortransparent($Raster);
1647                    for ($Xc = 0; $Xc <= $Width - 1; $Xc++) {
1648                        for ($Yc = 0; $Yc <= $Height - 1; $Yc++) {
1649                            $RGBa = imagecolorat($Raster, $Xc, $Yc);
1650                            $Values = imagecolorsforindex($Raster, $RGBa);
1651                            if ($Values["alpha"] < 120) {
1652                                $AlphaFactor = floor(
1653                                    ($this->Shadowa / 100) * ((100 / 127) * (127 - $Values["alpha"]))
1654                                );
1655                                $this->drawAlphaPixel(
1656                                    $X + $Xc + $this->ShadowX,
1657                                    $Y + $Yc + $this->ShadowY,
1658                                    $AlphaFactor,
1659                                    $this->ShadowR,
1660                                    $this->ShadowG,
1661                                    $this->ShadowB
1662                                );
1663                            }
1664                        }
1665                    }
1666                }
1667            }
1668            $this->Shadow = $RestoreShadow;
1669
1670            imagecopy($this->Picture, $Raster, $X, $Y, 0, 0, $Width, $Height);
1671            imagedestroy($Raster);
1672        }
1673    }
1674
1675    /**
1676     * Draw an arrow
1677     * @param int $X1
1678     * @param int $Y1
1679     * @param int $X2
1680     * @param int $Y2
1681     * @param array $Format
1682     */
1683    public function drawArrow($X1, $Y1, $X2, $Y2, array $Format = [])
1684    {
1685        $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0;
1686        $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0;
1687        $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0;
1688        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR;
1689        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG;
1690        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB;
1691        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1692        $Size = isset($Format["Size"]) ? $Format["Size"] : 10;
1693        $Ratio = isset($Format["Ratio"]) ? $Format["Ratio"] : .5;
1694        $TwoHeads = isset($Format["TwoHeads"]) ? $Format["TwoHeads"] : false;
1695        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : false;
1696
1697        /* Calculate the line angle */
1698        $Angle = $this->getAngle($X1, $Y1, $X2, $Y2);
1699
1700        /* Override Shadow support, this will be managed internally */
1701        $RestoreShadow = $this->Shadow;
1702        if ($this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0) {
1703            $this->Shadow = false;
1704            $this->drawArrow(
1705                $X1 + $this->ShadowX,
1706                $Y1 + $this->ShadowY,
1707                $X2 + $this->ShadowX,
1708                $Y2 + $this->ShadowY,
1709                [
1710                    "FillR" => $this->ShadowR,
1711                    "FillG" => $this->ShadowG,
1712                    "FillB" => $this->ShadowB,
1713                    "Alpha" => $this->Shadowa,
1714                    "Size" => $Size,
1715                    "Ratio" => $Ratio,
1716                    "TwoHeads" => $TwoHeads,
1717                    "Ticks" => $Ticks
1718                ]
1719            );
1720        }
1721
1722        /* Draw the 1st Head */
1723        $TailX = cos(($Angle - 180) * PI / 180) * $Size + $X2;
1724        $TailY = sin(($Angle - 180) * PI / 180) * $Size + $Y2;
1725
1726        $Points = [];
1727        $Points[] = $X2;
1728        $Points[] = $Y2;
1729        $Points[] = cos(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailX;
1730        $Points[] = sin(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailY;
1731        $Points[] = cos(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailX;
1732        $Points[] = sin(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailY;
1733        $Points[] = $X2;
1734        $Points[] = $Y2;
1735
1736        /* Visual correction */
1737        if ($Angle == 180 || $Angle == 360) {
1738            $Points[4] = $Points[2];
1739        }
1740        if ($Angle == 90 || $Angle == 270) {
1741            $Points[5] = $Points[3];
1742        }
1743
1744        $ArrowColor = $this->allocateColor($this->Picture, $FillR, $FillG, $FillB, $Alpha);
1745        ImageFilledPolygon($this->Picture, $Points, 4, $ArrowColor);
1746
1747        $this->drawLine(
1748            $Points[0],
1749            $Points[1],
1750            $Points[2],
1751            $Points[3],
1752            ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]
1753        );
1754        $this->drawLine(
1755            $Points[2],
1756            $Points[3],
1757            $Points[4],
1758            $Points[5],
1759            ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]
1760        );
1761        $this->drawLine(
1762            $Points[0],
1763            $Points[1],
1764            $Points[4],
1765            $Points[5],
1766            ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]
1767        );
1768
1769        /* Draw the second head */
1770        if ($TwoHeads) {
1771            $Angle = $this->getAngle($X2, $Y2, $X1, $Y1);
1772
1773            $TailX2 = cos(($Angle - 180) * PI / 180) * $Size + $X1;
1774            $TailY2 = sin(($Angle - 180) * PI / 180) * $Size + $Y1;
1775
1776            $Points = [];
1777            $Points[] = $X1;
1778            $Points[] = $Y1;
1779            $Points[] = cos(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailX2;
1780            $Points[] = sin(($Angle - 90) * PI / 180) * $Size * $Ratio + $TailY2;
1781            $Points[] = cos(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailX2;
1782            $Points[] = sin(($Angle - 270) * PI / 180) * $Size * $Ratio + $TailY2;
1783            $Points[] = $X1;
1784            $Points[] = $Y1;
1785
1786            /* Visual correction */
1787            if ($Angle == 180 || $Angle == 360) {
1788                $Points[4] = $Points[2];
1789            }
1790            if ($Angle == 90 || $Angle == 270) {
1791                $Points[5] = $Points[3];
1792            }
1793
1794            $ArrowColor = $this->allocateColor($this->Picture, $FillR, $FillG, $FillB, $Alpha);
1795            ImageFilledPolygon($this->Picture, $Points, 4, $ArrowColor);
1796
1797            $this->drawLine(
1798                $Points[0],
1799                $Points[1],
1800                $Points[2],
1801                $Points[3],
1802                ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]
1803            );
1804            $this->drawLine(
1805                $Points[2],
1806                $Points[3],
1807                $Points[4],
1808                $Points[5],
1809                ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]
1810            );
1811            $this->drawLine(
1812                $Points[0],
1813                $Points[1],
1814                $Points[4],
1815                $Points[5],
1816                ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]
1817            );
1818
1819            $this->drawLine(
1820                $TailX,
1821                $TailY,
1822                $TailX2,
1823                $TailY2,
1824                ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks]
1825            );
1826        } else {
1827            $this->drawLine(
1828                $X1,
1829                $Y1,
1830                $TailX,
1831                $TailY,
1832                ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks]
1833            );
1834        }
1835        /* Re-enable shadows */
1836        $this->Shadow = $RestoreShadow;
1837    }
1838
1839    /**
1840     * Draw a label with associated arrow
1841     * @param int $X1
1842     * @param int $Y1
1843     * @param string $Text
1844     * @param array $Format
1845     */
1846    public function drawArrowLabel($X1, $Y1, $Text, array $Format = [])
1847    {
1848        $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0;
1849        $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0;
1850        $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0;
1851        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR;
1852        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG;
1853        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB;
1854        $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName;
1855        $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
1856        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
1857        $Length = isset($Format["Length"]) ? $Format["Length"] : 50;
1858        $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 315;
1859        $Size = isset($Format["Size"]) ? $Format["Size"] : 10;
1860        $Position = isset($Format["Position"]) ? $Format["Position"] : POSITION_TOP;
1861        $RoundPos = isset($Format["RoundPos"]) ? $Format["RoundPos"] : false;
1862        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
1863
1864        $Angle = $Angle % 360;
1865
1866        $X2 = sin(($Angle + 180) * PI / 180) * $Length + $X1;
1867        $Y2 = cos(($Angle + 180) * PI / 180) * $Length + $Y1;
1868
1869        if ($RoundPos && $Angle > 0 && $Angle < 180) {
1870            $Y2 = ceil($Y2);
1871        }
1872        if ($RoundPos && $Angle > 180) {
1873            $Y2 = floor($Y2);
1874        }
1875
1876        $this->drawArrow($X2, $Y2, $X1, $Y1, $Format);
1877
1878        $Size = imagettfbbox($FontSize, 0, $FontName, $Text);
1879        $TxtWidth = max(abs($Size[2] - $Size[0]), abs($Size[0] - $Size[6]));
1880
1881        if ($Angle > 0 && $Angle < 180) {
1882            $this->drawLine(
1883                $X2,
1884                $Y2,
1885                $X2 - $TxtWidth,
1886                $Y2,
1887                ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks]
1888            );
1889            if ($Position == POSITION_TOP) {
1890                $this->drawText(
1891                    $X2,
1892                    $Y2 - 2,
1893                    $Text,
1894                    [
1895                        "R" => $BorderR,
1896                        "G" => $BorderG,
1897                        "B" => $BorderB,
1898                        "Alpha" => $Alpha,
1899                        "Align" => TEXT_ALIGN_BOTTOMRIGHT
1900                    ]
1901                );
1902            } else {
1903                $this->drawText(
1904                    $X2,
1905                    $Y2 + 4,
1906                    $Text,
1907                    [
1908                        "R" => $BorderR,
1909                        "G" => $BorderG,
1910                        "B" => $BorderB,
1911                        "Alpha" => $Alpha,
1912                        "Align" => TEXT_ALIGN_TOPRIGHT
1913                    ]
1914                );
1915            }
1916        } else {
1917            $this->drawLine(
1918                $X2,
1919                $Y2,
1920                $X2 + $TxtWidth,
1921                $Y2,
1922                ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha, "Ticks" => $Ticks]
1923            );
1924            if ($Position == POSITION_TOP) {
1925                $this->drawText(
1926                    $X2,
1927                    $Y2 - 2,
1928                    $Text,
1929                    ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $Alpha]
1930                );
1931            } else {
1932                $this->drawText(
1933                    $X2,
1934                    $Y2 + 4,
1935                    $Text,
1936                    [
1937                        "R" => $BorderR,
1938                        "G" => $BorderG,
1939                        "B" => $BorderB,
1940                        "Alpha" => $Alpha,
1941                        "Align" => TEXT_ALIGN_TOPLEFT
1942                    ]
1943                );
1944            }
1945        }
1946    }
1947
1948    /**
1949     * Draw a progress bar filled with specified %
1950     * @param int $X
1951     * @param int $Y
1952     * @param int|float $Percent
1953     * @param array $Format
1954     */
1955    public function drawProgress($X, $Y, $Percent, array $Format = [])
1956    {
1957        if ($Percent > 100) {
1958            $Percent = 100;
1959        }
1960        if ($Percent < 0) {
1961            $Percent = 0;
1962        }
1963
1964        $Width = isset($Format["Width"]) ? $Format["Width"] : 200;
1965        $Height = isset($Format["Height"]) ? $Format["Height"] : 20;
1966        $Orientation = isset($Format["Orientation"]) ? $Format["Orientation"] : ORIENTATION_HORIZONTAL;
1967        $ShowLabel = isset($Format["ShowLabel"]) ? $Format["ShowLabel"] : false;
1968        $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : LABEL_POS_INSIDE;
1969        $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 10;
1970        $R = isset($Format["R"]) ? $Format["R"] : 130;
1971        $G = isset($Format["G"]) ? $Format["G"] : 130;
1972        $B = isset($Format["B"]) ? $Format["B"] : 130;
1973        $RFade = isset($Format["RFade"]) ? $Format["RFade"] : -1;
1974        $GFade = isset($Format["GFade"]) ? $Format["GFade"] : -1;
1975        $BFade = isset($Format["BFade"]) ? $Format["BFade"] : -1;
1976        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
1977        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
1978        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
1979        $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 0;
1980        $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 0;
1981        $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 0;
1982        $BoxBackR = isset($Format["BoxBackR"]) ? $Format["BoxBackR"] : 255;
1983        $BoxBackG = isset($Format["BoxBackG"]) ? $Format["BoxBackG"] : 255;
1984        $BoxBackB = isset($Format["BoxBackB"]) ? $Format["BoxBackB"] : 255;
1985        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
1986        $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : null;
1987        $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : false;
1988
1989        if ($RFade != -1 && $GFade != -1 && $BFade != -1) {
1990            $RFade = (($RFade - $R) / 100) * $Percent + $R;
1991            $GFade = (($GFade - $G) / 100) * $Percent + $G;
1992            $BFade = (($BFade - $B) / 100) * $Percent + $B;
1993        }
1994
1995        if ($Surrounding != null) {
1996            $BorderR = $R + $Surrounding;
1997            $BorderG = $G + $Surrounding;
1998            $BorderB = $B + $Surrounding;
1999        }
2000        if ($BoxSurrounding != null) {
2001            $BoxBorderR = $BoxBackR + $Surrounding;
2002            $BoxBorderG = $BoxBackG + $Surrounding;
2003            $BoxBorderB = $BoxBackB + $Surrounding;
2004        }
2005
2006        if ($Orientation == ORIENTATION_VERTICAL) {
2007            $InnerHeight = (($Height - 2) / 100) * $Percent;
2008            $this->drawFilledRectangle(
2009                $X,
2010                $Y,
2011                $X + $Width,
2012                $Y - $Height,
2013                [
2014                    "R" => $BoxBackR,
2015                    "G" => $BoxBackG,
2016                    "B" => $BoxBackB,
2017                    "BorderR" => $BoxBorderR,
2018                    "BorderG" => $BoxBorderG,
2019                    "BorderB" => $BoxBorderB,
2020                    "NoAngle" => $NoAngle
2021                ]
2022            );
2023
2024            $RestoreShadow = $this->Shadow;
2025            $this->Shadow = false;
2026            if ($RFade != -1 && $GFade != -1 && $BFade != -1) {
2027                $GradientOptions = [
2028                    "StartR" => $RFade,
2029                    "StartG" => $GFade,
2030                    "StartB" => $BFade,
2031                    "EndR" => $R,
2032                    "EndG" => $G,
2033                    "EndB" => $B
2034                ];
2035                $this->drawGradientArea(
2036                    $X + 1,
2037                    $Y - 1,
2038                    $X + $Width - 1,
2039                    $Y - $InnerHeight,
2040                    DIRECTION_VERTICAL,
2041                    $GradientOptions
2042                );
2043
2044                if ($Surrounding) {
2045                    $this->drawRectangle(
2046                        $X + 1,
2047                        $Y - 1,
2048                        $X + $Width - 1,
2049                        $Y - $InnerHeight,
2050                        ["R" => 255, "G" => 255, "B" => 255, "Alpha" => $Surrounding]
2051                    );
2052                }
2053            } else {
2054                $this->drawFilledRectangle(
2055                    $X + 1,
2056                    $Y - 1,
2057                    $X + $Width - 1,
2058                    $Y - $InnerHeight,
2059                    [
2060                        "R" => $R,
2061                        "G" => $G,
2062                        "B" => $B,
2063                        "BorderR" => $BorderR,
2064                        "BorderG" => $BorderG,
2065                        "BorderB" => $BorderB
2066                    ]
2067                );
2068            }
2069            $this->Shadow = $RestoreShadow;
2070
2071            if ($ShowLabel && $LabelPos == LABEL_POS_BOTTOM) {
2072                $this->drawText(
2073                    $X + ($Width / 2),
2074                    $Y + $Margin,
2075                    $Percent . "%",
2076                    ["Align" => TEXT_ALIGN_TOPMIDDLE]
2077                );
2078            }
2079            if ($ShowLabel && $LabelPos == LABEL_POS_TOP) {
2080                $this->drawText(
2081                    $X + ($Width / 2),
2082                    $Y - $Height - $Margin,
2083                    $Percent . "%",
2084                    ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]
2085                );
2086            }
2087            if ($ShowLabel && $LabelPos == LABEL_POS_INSIDE) {
2088                $this->drawText(
2089                    $X + ($Width / 2),
2090                    $Y - $InnerHeight - $Margin,
2091                    $Percent . "%",
2092                    ["Align" => TEXT_ALIGN_MIDDLELEFT, "Angle" => 90]
2093                );
2094            }
2095            if ($ShowLabel && $LabelPos == LABEL_POS_CENTER) {
2096                $this->drawText(
2097                    $X + ($Width / 2),
2098                    $Y - ($Height / 2),
2099                    $Percent . "%",
2100                    ["Align" => TEXT_ALIGN_MIDDLEMIDDLE, "Angle" => 90]
2101                );
2102            }
2103        } else {
2104            if ($Percent == 100) {
2105                $InnerWidth = $Width - 1;
2106            } else {
2107                $InnerWidth = (($Width - 2) / 100) * $Percent;
2108            }
2109            $this->drawFilledRectangle(
2110                $X,
2111                $Y,
2112                $X + $Width,
2113                $Y + $Height,
2114                [
2115                    "R" => $BoxBackR,
2116                    "G" => $BoxBackG,
2117                    "B" => $BoxBackB,
2118                    "BorderR" => $BoxBorderR,
2119                    "BorderG" => $BoxBorderG,
2120                    "BorderB" => $BoxBorderB,
2121                    "NoAngle" => $NoAngle
2122                ]
2123            );
2124
2125            $RestoreShadow = $this->Shadow;
2126            $this->Shadow = false;
2127            if ($RFade != -1 && $GFade != -1 && $BFade != -1) {
2128                $GradientOptions = [
2129                    "StartR" => $R,
2130                    "StartG" => $G,
2131                    "StartB" => $B,
2132                    "EndR" => $RFade,
2133                    "EndG" => $GFade,
2134                    "EndB" => $BFade
2135                ];
2136                $this->drawGradientArea(
2137                    $X + 1,
2138                    $Y + 1,
2139                    $X + $InnerWidth,
2140                    $Y + $Height - 1,
2141                    DIRECTION_HORIZONTAL,
2142                    $GradientOptions
2143                );
2144
2145                if ($Surrounding) {
2146                    $this->drawRectangle(
2147                        $X + 1,
2148                        $Y + 1,
2149                        $X + $InnerWidth,
2150                        $Y + $Height - 1,
2151                        ["R" => 255, "G" => 255, "B" => 255, "Alpha" => $Surrounding]
2152                    );
2153                }
2154            } else {
2155                $this->drawFilledRectangle(
2156                    $X + 1,
2157                    $Y + 1,
2158                    $X + $InnerWidth,
2159                    $Y + $Height - 1,
2160                    [
2161                        "R" => $R,
2162                        "G" => $G,
2163                        "B" => $B,
2164                        "BorderR" => $BorderR, "BorderG" => $BorderG, "BorderB" => $BorderB
2165                    ]
2166                );
2167            }
2168            $this->Shadow = $RestoreShadow;
2169
2170            if ($ShowLabel && $LabelPos == LABEL_POS_LEFT) {
2171                $this->drawText(
2172                    $X - $Margin,
2173                    $Y + ($Height / 2),
2174                    $Percent . "%",
2175                    ["Align" => TEXT_ALIGN_MIDDLERIGHT]
2176                );
2177            }
2178            if ($ShowLabel && $LabelPos == LABEL_POS_RIGHT) {
2179                $this->drawText(
2180                    $X + $Width + $Margin,
2181                    $Y + ($Height / 2),
2182                    $Percent . "%",
2183                    ["Align" => TEXT_ALIGN_MIDDLELEFT]
2184                );
2185            }
2186            if ($ShowLabel && $LabelPos == LABEL_POS_CENTER) {
2187                $this->drawText(
2188                    $X + ($Width / 2),
2189                    $Y + ($Height / 2),
2190                    $Percent . "%",
2191                    ["Align" => TEXT_ALIGN_MIDDLEMIDDLE]
2192                );
2193            }
2194            if ($ShowLabel && $LabelPos == LABEL_POS_INSIDE) {
2195                $this->drawText(
2196                    $X + $InnerWidth + $Margin,
2197                    $Y + ($Height / 2),
2198                    $Percent . "%",
2199                    ["Align" => TEXT_ALIGN_MIDDLELEFT]
2200                );
2201            }
2202        }
2203    }
2204
2205    /**
2206     * Draw the legend of the active series
2207     * @param int $X
2208     * @param int $Y
2209     * @param array $Format
2210     */
2211    public function drawLegend($X, $Y, array $Format = [])
2212    {
2213        $Family = isset($Format["Family"]) ? $Format["Family"] : LEGEND_FAMILY_BOX;
2214        $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName;
2215        $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
2216        $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->FontColorR;
2217        $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->FontColorG;
2218        $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->FontColorB;
2219        $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5;
2220        $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5;
2221        $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth;
2222        $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight;
2223        $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5;
2224        $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5;
2225        $R = isset($Format["R"]) ? $Format["R"] : 200;
2226        $G = isset($Format["G"]) ? $Format["G"] : 200;
2227        $B = isset($Format["B"]) ? $Format["B"] : 200;
2228        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
2229        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255;
2230        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255;
2231        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255;
2232        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
2233        $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND;
2234        $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL;
2235
2236        if ($Surrounding != null) {
2237            $BorderR = $R + $Surrounding;
2238            $BorderG = $G + $Surrounding;
2239            $BorderB = $B + $Surrounding;
2240        }
2241
2242        $Data = $this->DataSet->getData();
2243
2244        foreach ($Data["Series"] as $SerieName => $Serie) {
2245            if ($Serie["isDrawable"] == true
2246                && $SerieName != $Data["Abscissa"]
2247                && isset($Serie["Picture"])
2248            ) {
2249                list($PicWidth, $PicHeight) = $this->getPicInfo($Serie["Picture"]);
2250                if ($IconAreaWidth < $PicWidth) {
2251                    $IconAreaWidth = $PicWidth;
2252                }
2253                if ($IconAreaHeight < $PicHeight) {
2254                    $IconAreaHeight = $PicHeight;
2255                }
2256            }
2257        }
2258
2259        $YStep = max($this->FontSize, $IconAreaHeight) + 5;
2260        $XStep = $IconAreaWidth + 5;
2261        $XStep = $XSpacing;
2262
2263        $Boundaries = [];
2264        $Boundaries["L"] = $X;
2265        $Boundaries["T"] = $Y;
2266        $Boundaries["R"] = 0;
2267        $Boundaries["B"] = 0;
2268        $vY = $Y;
2269        $vX = $X;
2270        foreach ($Data["Series"] as $SerieName => $Serie) {
2271            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
2272                if ($Mode == LEGEND_VERTICAL) {
2273                    $BoxArray = $this->getTextBox(
2274                        $vX + $IconAreaWidth + 4,
2275                        $vY + $IconAreaHeight / 2,
2276                        $FontName,
2277                        $FontSize,
2278                        0,
2279                        $Serie["Description"]
2280                    );
2281
2282                    if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) {
2283                        $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2;
2284                    }
2285                    if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
2286                        $Boundaries["R"] = $BoxArray[1]["X"] + 2;
2287                    }
2288                    if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) {
2289                        $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2;
2290                    }
2291
2292                    $Lines = preg_split("/\n/", $Serie["Description"]);
2293                    $vY = $vY + max($this->FontSize * count($Lines), $IconAreaHeight) + 5;
2294                } elseif ($Mode == LEGEND_HORIZONTAL) {
2295                    $Lines = preg_split("/\n/", $Serie["Description"]);
2296                    $Width = [];
2297                    foreach ($Lines as $Key => $Value) {
2298                        $BoxArray = $this->getTextBox(
2299                            $vX + $IconAreaWidth + 6,
2300                            $Y + $IconAreaHeight / 2 + (($this->FontSize + 3) * $Key),
2301                            $FontName,
2302                            $FontSize,
2303                            0,
2304                            $Value
2305                        );
2306
2307                        if ($Boundaries["T"] > $BoxArray[2]["Y"] + $IconAreaHeight / 2) {
2308                            $Boundaries["T"] = $BoxArray[2]["Y"] + $IconAreaHeight / 2;
2309                        }
2310                        if ($Boundaries["R"] < $BoxArray[1]["X"] + 2) {
2311                            $Boundaries["R"] = $BoxArray[1]["X"] + 2;
2312                        }
2313                        if ($Boundaries["B"] < $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2) {
2314                            $Boundaries["B"] = $BoxArray[1]["Y"] + 2 + $IconAreaHeight / 2;
2315                        }
2316
2317                        $Width[] = $BoxArray[1]["X"];
2318                    }
2319
2320                    $vX = max($Width) + $XStep;
2321                }
2322            }
2323        }
2324        $vY = $vY - $YStep;
2325        $vX = $vX - $XStep;
2326
2327        $TopOffset = $Y - $Boundaries["T"];
2328        if ($Boundaries["B"] - ($vY + $IconAreaHeight) < $TopOffset) {
2329            $Boundaries["B"] = $vY + $IconAreaHeight + $TopOffset;
2330        }
2331
2332        if ($Style == LEGEND_ROUND) {
2333            $this->drawRoundedFilledRectangle(
2334                $Boundaries["L"] - $Margin,
2335                $Boundaries["T"] - $Margin,
2336                $Boundaries["R"] + $Margin,
2337                $Boundaries["B"] + $Margin,
2338                $Margin,
2339                [
2340                    "R" => $R,
2341                    "G" => $G,
2342                    "B" => $B,
2343                    "Alpha" => $Alpha,
2344                    "BorderR" => $BorderR,
2345                    "BorderG" => $BorderG,
2346                    "BorderB" => $BorderB
2347                ]
2348            );
2349        } elseif ($Style == LEGEND_BOX) {
2350            $this->drawFilledRectangle(
2351                $Boundaries["L"] - $Margin,
2352                $Boundaries["T"] - $Margin,
2353                $Boundaries["R"] + $Margin,
2354                $Boundaries["B"] + $Margin,
2355                [
2356                    "R" => $R,
2357                    "G" => $G,
2358                    "B" => $B,
2359                    "Alpha" => $Alpha,
2360                    "BorderR" => $BorderR,
2361                    "BorderG" => $BorderG,
2362                    "BorderB" => $BorderB
2363                ]
2364            );
2365        }
2366
2367        $RestoreShadow = $this->Shadow;
2368        $this->Shadow = false;
2369        foreach ($Data["Series"] as $SerieName => $Serie) {
2370            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
2371                $R = $Serie["Color"]["R"];
2372                $G = $Serie["Color"]["G"];
2373                $B = $Serie["Color"]["B"];
2374                $Ticks = $Serie["Ticks"];
2375                $Weight = $Serie["Weight"];
2376
2377                if (isset($Serie["Picture"])) {
2378                    $Picture = $Serie["Picture"];
2379                    list($PicWidth, $PicHeight) = $this->getPicInfo($Picture);
2380                    $PicX = $X + $IconAreaWidth / 2;
2381                    $PicY = $Y + $IconAreaHeight / 2;
2382
2383                    $this->drawFromPNG($PicX - $PicWidth / 2, $PicY - $PicHeight / 2, $Picture);
2384                } else {
2385                    if ($Family == LEGEND_FAMILY_BOX) {
2386                        $XOffset = 0;
2387                        if ($BoxWidth != $IconAreaWidth) {
2388                            $XOffset = floor(($IconAreaWidth - $BoxWidth) / 2);
2389                        }
2390                        $YOffset = 0;
2391                        if ($BoxHeight != $IconAreaHeight) {
2392                            $YOffset = floor(($IconAreaHeight - $BoxHeight) / 2);
2393                        }
2394
2395                        $this->drawFilledRectangle(
2396                            $X + 1 + $XOffset,
2397                            $Y + 1 + $YOffset,
2398                            $X + $BoxWidth + $XOffset + 1,
2399                            $Y + $BoxHeight + 1 + $YOffset,
2400                            ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]
2401                        );
2402                        $this->drawFilledRectangle(
2403                            $X + $XOffset,
2404                            $Y + $YOffset,
2405                            $X + $BoxWidth + $XOffset,
2406                            $Y + $BoxHeight + $YOffset,
2407                            ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20]
2408                        );
2409                    } elseif ($Family == LEGEND_FAMILY_CIRCLE) {
2410                        $this->drawFilledCircle(
2411                            $X + 1 + $IconAreaWidth / 2,
2412                            $Y + 1 + $IconAreaHeight / 2,
2413                            min($IconAreaHeight / 2, $IconAreaWidth / 2),
2414                            ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20]
2415                        );
2416                        $this->drawFilledCircle(
2417                            $X + $IconAreaWidth / 2,
2418                            $Y + $IconAreaHeight / 2,
2419                            min($IconAreaHeight / 2, $IconAreaWidth / 2),
2420                            ["R" => $R, "G" => $G, "B" => $B, "Surrounding" => 20]
2421                        );
2422                    } elseif ($Family == LEGEND_FAMILY_LINE) {
2423                        $this->drawLine(
2424                            $X + 1,
2425                            $Y + 1 + $IconAreaHeight / 2,
2426                            $X + 1 + $IconAreaWidth,
2427                            $Y + 1 + $IconAreaHeight / 2,
2428                            ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20, "Ticks" => $Ticks, "Weight" => $Weight]
2429                        );
2430                        $this->drawLine(
2431                            $X,
2432                            $Y + $IconAreaHeight / 2,
2433                            $X + $IconAreaWidth,
2434                            $Y + $IconAreaHeight / 2,
2435                            ["R" => $R, "G" => $G, "B" => $B, "Ticks" => $Ticks, "Weight" => $Weight]
2436                        );
2437                    }
2438                }
2439
2440                if ($Mode == LEGEND_VERTICAL) {
2441                    $Lines = preg_split("/\n/", $Serie["Description"]);
2442                    foreach ($Lines as $Key => $Value) {
2443                        $this->drawText(
2444                            $X + $IconAreaWidth + 4,
2445                            $Y + $IconAreaHeight / 2 + (($this->FontSize + 3) * $Key),
2446                            $Value,
2447                            [
2448                                "R" => $FontR,
2449                                "G" => $FontG,
2450                                "B" => $FontB,
2451                                "Align" => TEXT_ALIGN_MIDDLELEFT,
2452                                "FontSize" => $FontSize,
2453                                "FontName" => $FontName
2454                            ]
2455                        );
2456                    }
2457                    $Y = $Y + max($this->FontSize * count($Lines), $IconAreaHeight) + 5;
2458                } elseif ($Mode == LEGEND_HORIZONTAL) {
2459                    $Lines = preg_split("/\n/", $Serie["Description"]);
2460                    $Width = [];
2461                    foreach ($Lines as $Key => $Value) {
2462                        $BoxArray = $this->drawText(
2463                            $X + $IconAreaWidth + 4,
2464                            $Y + $IconAreaHeight / 2 + (($this->FontSize + 3) * $Key),
2465                            $Value,
2466                            [
2467                                "R" => $FontR,
2468                                "G" => $FontG,
2469                                "B" => $FontB,
2470                                "Align" => TEXT_ALIGN_MIDDLELEFT,
2471                                "FontSize" => $FontSize,
2472                                "FontName" => $FontName
2473                            ]
2474                        );
2475                        $Width[] = $BoxArray[1]["X"];
2476                    }
2477                    $X = max($Width) + 2 + $XStep;
2478                }
2479            }
2480        }
2481
2482
2483        $this->Shadow = $RestoreShadow;
2484    }
2485
2486    /**
2487     * @param array $Format
2488     * @throws Exception
2489     */
2490    public function drawScale(array $Format = [])
2491    {
2492        $FloatingOffset = 0;
2493        $Pos = isset($Format["Pos"]) ? $Format["Pos"] : SCALE_POS_LEFTRIGHT;
2494        $Floating = isset($Format["Floating"]) ? $Format["Floating"] : false;
2495        $Mode = isset($Format["Mode"]) ? $Format["Mode"] : SCALE_MODE_FLOATING;
2496        $RemoveXAxis = isset($Format["RemoveXAxis"]) ? $Format["RemoveXAxis"] : false;
2497        $RemoveYAxis = isset($Format["RemoveYAxis"]) ? $Format["RemoveYAxis"] : false;
2498        $RemoveYAxiValues = isset($Format["RemoveYAxisValues"]) ? $Format["RemoveYAxisValues"] : false;
2499        $MinDivHeight = isset($Format["MinDivHeight"]) ? $Format["MinDivHeight"] : 20;
2500        $Factors = isset($Format["Factors"]) ? $Format["Factors"] : [1, 2, 5];
2501        $ManualScale = isset($Format["ManualScale"])
2502            ? $Format["ManualScale"] : ["0" => ["Min" => -100, "Max" => 100]]
2503        ;
2504        $XMargin = isset($Format["XMargin"]) ? $Format["XMargin"] : AUTO;
2505        $YMargin = isset($Format["YMargin"]) ? $Format["YMargin"] : 0;
2506        $ScaleSpacing = isset($Format["ScaleSpacing"]) ? $Format["ScaleSpacing"] : 15;
2507        $InnerTickWidth = isset($Format["InnerTickWidth"]) ? $Format["InnerTickWidth"] : 2;
2508        $OuterTickWidth = isset($Format["OuterTickWidth"]) ? $Format["OuterTickWidth"] : 2;
2509        $DrawXLines = isset($Format["DrawXLines"]) ? $Format["DrawXLines"] : true;
2510        $DrawYLines = isset($Format["DrawYLines"]) ? $Format["DrawYLines"] : ALL;
2511        $GridTicks = isset($Format["GridTicks"]) ? $Format["GridTicks"] : 4;
2512        $GridR = isset($Format["GridR"]) ? $Format["GridR"] : 255;
2513        $GridG = isset($Format["GridG"]) ? $Format["GridG"] : 255;
2514        $GridB = isset($Format["GridB"]) ? $Format["GridB"] : 255;
2515        $GridAlpha = isset($Format["GridAlpha"]) ? $Format["GridAlpha"] : 40;
2516        $AxisRo = isset($Format["AxisR"]) ? $Format["AxisR"] : 0;
2517        $AxisGo = isset($Format["AxisG"]) ? $Format["AxisG"] : 0;
2518        $AxisBo = isset($Format["AxisB"]) ? $Format["AxisB"] : 0;
2519        $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 100;
2520        $TickRo = isset($Format["TickR"]) ? $Format["TickR"] : 0;
2521        $TickGo = isset($Format["TickG"]) ? $Format["TickG"] : 0;
2522        $TickBo = isset($Format["TickB"]) ? $Format["TickB"] : 0;
2523        $TickAlpha = isset($Format["TickAlpha"]) ? $Format["TickAlpha"] : 100;
2524        $DrawSubTicks = isset($Format["DrawSubTicks"]) ? $Format["DrawSubTicks"] : false;
2525        $InnerSubTickWidth = isset($Format["InnerSubTickWidth"]) ? $Format["InnerSubTickWidth"] : 0;
2526        $OuterSubTickWidth = isset($Format["OuterSubTickWidth"]) ? $Format["OuterSubTickWidth"] : 2;
2527        $SubTickR = isset($Format["SubTickR"]) ? $Format["SubTickR"] : 255;
2528        $SubTickG = isset($Format["SubTickG"]) ? $Format["SubTickG"] : 0;
2529        $SubTickB = isset($Format["SubTickB"]) ? $Format["SubTickB"] : 0;
2530        $SubTickAlpha = isset($Format["SubTickAlpha"]) ? $Format["SubTickAlpha"] : 100;
2531        $AutoAxisLabels = isset($Format["AutoAxisLabels"]) ? $Format["AutoAxisLabels"] : true;
2532        $XReleasePercent = isset($Format["XReleasePercent"]) ? $Format["XReleasePercent"] : 1;
2533        $DrawArrows = isset($Format["DrawArrows"]) ? $Format["DrawArrows"] : false;
2534        $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 8;
2535        $CycleBackground = isset($Format["CycleBackground"]) ? $Format["CycleBackground"] : false;
2536        $BackgroundR1 = isset($Format["BackgroundR1"]) ? $Format["BackgroundR1"] : 255;
2537        $BackgroundG1 = isset($Format["BackgroundG1"]) ? $Format["BackgroundG1"] : 255;
2538        $BackgroundB1 = isset($Format["BackgroundB1"]) ? $Format["BackgroundB1"] : 255;
2539        $BackgroundAlpha1 = isset($Format["BackgroundAlpha1"]) ? $Format["BackgroundAlpha1"] : 20;
2540        $BackgroundR2 = isset($Format["BackgroundR2"]) ? $Format["BackgroundR2"] : 230;
2541        $BackgroundG2 = isset($Format["BackgroundG2"]) ? $Format["BackgroundG2"] : 230;
2542        $BackgroundB2 = isset($Format["BackgroundB2"]) ? $Format["BackgroundB2"] : 230;
2543        $BackgroundAlpha2 = isset($Format["BackgroundAlpha2"]) ? $Format["BackgroundAlpha2"] : 20;
2544        $LabelingMethod = isset($Format["LabelingMethod"]) ? $Format["LabelingMethod"] : LABELING_ALL;
2545        $LabelSkip = isset($Format["LabelSkip"]) ? $Format["LabelSkip"] : 0;
2546        $LabelRotation = isset($Format["LabelRotation"]) ? $Format["LabelRotation"] : 0;
2547        $RemoveSkippedAxis = isset($Format["RemoveSkippedAxis"]) ? $Format["RemoveSkippedAxis"] : false;
2548        $SkippedAxisTicks = isset($Format["SkippedAxisTicks"]) ? $Format["SkippedAxisTicks"] : $GridTicks + 2;
2549        $SkippedAxisR = isset($Format["SkippedAxisR"]) ? $Format["SkippedAxisR"] : $GridR;
2550        $SkippedAxisG = isset($Format["SkippedAxisG"]) ? $Format["SkippedAxisG"] : $GridG;
2551        $SkippedAxisB = isset($Format["SkippedAxisB"]) ? $Format["SkippedAxisB"] : $GridB;
2552        $SkippedAxisAlpha = isset($Format["SkippedAxisAlpha"]) ? $Format["SkippedAxisAlpha"] : $GridAlpha - 30;
2553        $SkippedTickR = isset($Format["SkippedTickR"]) ? $Format["SkippedTickR"] : $TickRo;
2554        $SkippedTickG = isset($Format["SkippedTickG"]) ? $Format["SkippedTickG"] : $TickGo;
2555        $SkippedTickB = isset($Format["SkippedTicksB"]) ? $Format["SkippedTickB"] : $TickBo;
2556        $SkippedTickAlpha = isset($Format["SkippedTickAlpha"]) ? $Format["SkippedTickAlpha"] : $TickAlpha - 80;
2557        $SkippedInnerTickWidth = isset($Format["SkippedInnerTickWidth"]) ? $Format["SkippedInnerTickWidth"] : 0;
2558        $SkippedOuterTickWidth = isset($Format["SkippedOuterTickWidth"]) ? $Format["SkippedOuterTickWidth"] : 2;
2559
2560        /* Floating scale require X & Y margins to be set manually */
2561        if ($Floating && ($XMargin == AUTO || $YMargin == 0)) {
2562            $Floating = false;
2563        }
2564
2565        /* Skip a NOTICE event in case of an empty array */
2566        if ($DrawYLines == NONE || $DrawYLines == false) {
2567            $DrawYLines = ["zarma" => "31"];
2568        }
2569
2570        /* Define the color for the skipped elements */
2571        $SkippedAxisColor = [
2572            "R" => $SkippedAxisR,
2573            "G" => $SkippedAxisG,
2574            "B" => $SkippedAxisB,
2575            "Alpha" => $SkippedAxisAlpha,
2576            "Ticks" => $SkippedAxisTicks
2577        ];
2578        $SkippedTickColor = [
2579            "R" => $SkippedTickR,
2580            "G" => $SkippedTickG,
2581            "B" => $SkippedTickB,
2582            "Alpha" => $SkippedTickAlpha
2583        ];
2584
2585        $Data = $this->DataSet->getData();
2586        $Abscissa = null;
2587        if (isset($Data["Abscissa"])) {
2588            $Abscissa = $Data["Abscissa"];
2589        }
2590
2591        /* Unset the abscissa axis, needed if we display multiple charts on the same picture */
2592        if ($Abscissa != null) {
2593            foreach ($Data["Axis"] as $AxisID => $Parameters) {
2594                if ($Parameters["Identity"] == AXIS_X) {
2595                    unset($Data["Axis"][$AxisID]);
2596                }
2597            }
2598        }
2599
2600        /* Build the scale settings */
2601        $GotXAxis = false;
2602        foreach ($Data["Axis"] as $AxisID => $AxisParameter) {
2603            if ($AxisParameter["Identity"] == AXIS_X) {
2604                $GotXAxis = true;
2605            }
2606
2607            if ($Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_Y) {
2608                $Height = $this->GraphAreaY2 - $this->GraphAreaY1 - $YMargin * 2;
2609            } elseif ($Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_X) {
2610                $Height = $this->GraphAreaX2 - $this->GraphAreaX1;
2611            } elseif ($Pos == SCALE_POS_TOPBOTTOM && $AxisParameter["Identity"] == AXIS_Y) {
2612                $Height = $this->GraphAreaX2 - $this->GraphAreaX1 - $YMargin * 2;
2613                ;
2614            } else {
2615                $Height = $this->GraphAreaY2 - $this->GraphAreaY1;
2616            }
2617
2618            $AxisMin = ABSOLUTE_MAX;
2619            $AxisMax = OUT_OF_SIGHT;
2620            if ($Mode == SCALE_MODE_FLOATING || $Mode == SCALE_MODE_START0) {
2621                foreach ($Data["Series"] as $SerieID => $SerieParameter) {
2622                    if ($SerieParameter["Axis"] == $AxisID
2623                        && $Data["Series"][$SerieID]["isDrawable"]
2624                        && $Data["Abscissa"] != $SerieID
2625                    ) {
2626                        $AxisMax = max($AxisMax, $Data["Series"][$SerieID]["Max"]);
2627                        $AxisMin = min($AxisMin, $Data["Series"][$SerieID]["Min"]);
2628                    }
2629                }
2630                $AutoMargin = (($AxisMax - $AxisMin) / 100) * $XReleasePercent;
2631
2632                $Data["Axis"][$AxisID]["Min"] = $AxisMin - $AutoMargin;
2633                $Data["Axis"][$AxisID]["Max"] = $AxisMax + $AutoMargin;
2634                if ($Mode == SCALE_MODE_START0) {
2635                    $Data["Axis"][$AxisID]["Min"] = 0;
2636                }
2637            } elseif ($Mode == SCALE_MODE_MANUAL) {
2638                if (isset($ManualScale[$AxisID]["Min"]) && isset($ManualScale[$AxisID]["Max"])) {
2639                    $Data["Axis"][$AxisID]["Min"] = $ManualScale[$AxisID]["Min"];
2640                    $Data["Axis"][$AxisID]["Max"] = $ManualScale[$AxisID]["Max"];
2641                } else {
2642                    throw new Exception("Manual scale boundaries not set.");
2643                }
2644            } elseif ($Mode == SCALE_MODE_ADDALL || $Mode == SCALE_MODE_ADDALL_START0) {
2645                $Series = [];
2646                foreach ($Data["Series"] as $SerieID => $SerieParameter) {
2647                    if ($SerieParameter["Axis"] == $AxisID
2648                        && $SerieParameter["isDrawable"]
2649                        && $Data["Abscissa"] != $SerieID
2650                    ) {
2651                        $Series[$SerieID] = count($Data["Series"][$SerieID]["Data"]);
2652                    }
2653                }
2654
2655                for ($ID = 0; $ID <= max($Series) - 1; $ID++) {
2656                    $PointMin = 0;
2657                    $PointMax = 0;
2658                    foreach ($Series as $SerieID => $ValuesCount) {
2659                        if (isset($Data["Series"][$SerieID]["Data"][$ID])
2660                            && $Data["Series"][$SerieID]["Data"][$ID] != null
2661                        ) {
2662                            $Value = $Data["Series"][$SerieID]["Data"][$ID];
2663                            if ($Value > 0) {
2664                                $PointMax = $PointMax + $Value;
2665                            } else {
2666                                $PointMin = $PointMin + $Value;
2667                            }
2668                        }
2669                    }
2670                    $AxisMax = max($AxisMax, $PointMax);
2671                    $AxisMin = min($AxisMin, $PointMin);
2672                }
2673                $AutoMargin = (($AxisMax - $AxisMin) / 100) * $XReleasePercent;
2674                $Data["Axis"][$AxisID]["Min"] = $AxisMin - $AutoMargin;
2675                $Data["Axis"][$AxisID]["Max"] = $AxisMax + $AutoMargin;
2676            }
2677            $MaxDivs = floor($Height / $MinDivHeight);
2678
2679            if ($Mode == SCALE_MODE_ADDALL_START0) {
2680                $Data["Axis"][$AxisID]["Min"] = 0;
2681            }
2682
2683            $Scale = $this->computeScale(
2684                $Data["Axis"][$AxisID]["Min"],
2685                $Data["Axis"][$AxisID]["Max"],
2686                $MaxDivs,
2687                $Factors,
2688                $AxisID
2689            );
2690
2691            $Data["Axis"][$AxisID]["Margin"] = $AxisParameter["Identity"] == AXIS_X ? $XMargin : $YMargin;
2692            $Data["Axis"][$AxisID]["ScaleMin"] = $Scale["XMin"];
2693            $Data["Axis"][$AxisID]["ScaleMax"] = $Scale["XMax"];
2694            $Data["Axis"][$AxisID]["Rows"] = $Scale["Rows"];
2695            $Data["Axis"][$AxisID]["RowHeight"] = $Scale["RowHeight"];
2696
2697            if (isset($Scale["Format"])) {
2698                $Data["Axis"][$AxisID]["Format"] = $Scale["Format"];
2699            }
2700            if (!isset($Data["Axis"][$AxisID]["Display"])) {
2701                $Data["Axis"][$AxisID]["Display"] = null;
2702            }
2703            if (!isset($Data["Axis"][$AxisID]["Format"])) {
2704                $Data["Axis"][$AxisID]["Format"] = null;
2705            }
2706            if (!isset($Data["Axis"][$AxisID]["Unit"])) {
2707                $Data["Axis"][$AxisID]["Unit"] = null;
2708            }
2709        }
2710
2711        /* Still no X axis */
2712        if ($GotXAxis == false) {
2713            if ($Abscissa != null) {
2714                $Points = count($Data["Series"][$Abscissa]["Data"]);
2715                $AxisName = null;
2716                if ($AutoAxisLabels) {
2717                    $AxisName = isset($Data["Series"][$Abscissa]["Description"])
2718                        ? $Data["Series"][$Abscissa]["Description"] : null
2719                    ;
2720                }
2721            } else {
2722                $Points = 0;
2723                $AxisName = isset($Data["XAxisName"]) ? $Data["XAxisName"] : null;
2724                foreach ($Data["Series"] as $SerieID => $SerieParameter) {
2725                    if ($SerieParameter["isDrawable"]) {
2726                        $Points = max($Points, count($SerieParameter["Data"]));
2727                    }
2728                }
2729            }
2730
2731            $AxisID = count($Data["Axis"]);
2732            $Data["Axis"][$AxisID]["Identity"] = AXIS_X;
2733            if ($Pos == SCALE_POS_LEFTRIGHT) {
2734                $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_BOTTOM;
2735            } else {
2736                $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT;
2737            }
2738            if (isset($Data["AbscissaName"])) {
2739                $Data["Axis"][$AxisID]["Name"] = $Data["AbscissaName"];
2740            }
2741            if ($XMargin == AUTO) {
2742                if ($Pos == SCALE_POS_LEFTRIGHT) {
2743                    $Height = $this->GraphAreaX2 - $this->GraphAreaX1;
2744                } else {
2745                    $Height = $this->GraphAreaY2 - $this->GraphAreaY1;
2746                }
2747
2748                if ($Points == 0 || $Points == 1) {
2749                    $Data["Axis"][$AxisID]["Margin"] = $Height / 2;
2750                } else {
2751                    $Data["Axis"][$AxisID]["Margin"] = ($Height / $Points) / 2;
2752                }
2753            } else {
2754                $Data["Axis"][$AxisID]["Margin"] = $XMargin;
2755            }
2756            $Data["Axis"][$AxisID]["Rows"] = $Points - 1;
2757            if (!isset($Data["Axis"][$AxisID]["Display"])) {
2758                $Data["Axis"][$AxisID]["Display"] = null;
2759            }
2760            if (!isset($Data["Axis"][$AxisID]["Format"])) {
2761                $Data["Axis"][$AxisID]["Format"] = null;
2762            }
2763            if (!isset($Data["Axis"][$AxisID]["Unit"])) {
2764                $Data["Axis"][$AxisID]["Unit"] = null;
2765            }
2766        }
2767
2768        /* Do we need to reverse the abscissa position? */
2769        if ($Pos != SCALE_POS_LEFTRIGHT) {
2770            $Data["AbsicssaPosition"] = AXIS_POSITION_RIGHT;
2771            if ($Data["AbsicssaPosition"] == AXIS_POSITION_BOTTOM) {
2772                $Data["AbsicssaPosition"] = AXIS_POSITION_LEFT;
2773            }
2774        }
2775        $Data["Axis"][$AxisID]["Position"] = $Data["AbsicssaPosition"];
2776
2777        $this->DataSet->saveOrientation($Pos);
2778        $this->DataSet->saveAxisConfig($Data["Axis"]);
2779        $this->DataSet->saveYMargin($YMargin);
2780
2781        $FontColorRo = $this->FontColorR;
2782        $FontColorGo = $this->FontColorG;
2783        $FontColorBo = $this->FontColorB;
2784
2785        $AxisPos["L"] = $this->GraphAreaX1;
2786        $AxisPos["R"] = $this->GraphAreaX2;
2787        $AxisPos["T"] = $this->GraphAreaY1;
2788        $AxisPos["B"] = $this->GraphAreaY2;
2789        foreach ($Data["Axis"] as $AxisID => $Parameters) {
2790            if (isset($Parameters["Color"])) {
2791                $AxisR = $Parameters["Color"]["R"];
2792                $AxisG = $Parameters["Color"]["G"];
2793                $AxisB = $Parameters["Color"]["B"];
2794                $TickR = $Parameters["Color"]["R"];
2795                $TickG = $Parameters["Color"]["G"];
2796                $TickB = $Parameters["Color"]["B"];
2797                $this->setFontProperties(
2798                    [
2799                        "R" => $Parameters["Color"]["R"],
2800                        "G" => $Parameters["Color"]["G"],
2801                        "B" => $Parameters["Color"]["B"]
2802                    ]
2803                );
2804            } else {
2805                $AxisR = $AxisRo;
2806                $AxisG = $AxisGo;
2807                $AxisB = $AxisBo;
2808                $TickR = $TickRo;
2809                $TickG = $TickGo;
2810                $TickB = $TickBo;
2811                $this->setFontProperties(["R" => $FontColorRo, "G" => $FontColorGo, "B" => $FontColorBo]);
2812            }
2813
2814            $LastValue = "w00t";
2815            $ID = 1;
2816            if ($Parameters["Identity"] == AXIS_X) {
2817                if ($Pos == SCALE_POS_LEFTRIGHT) {
2818                    if ($Parameters["Position"] == AXIS_POSITION_BOTTOM) {
2819                        if ($LabelRotation == 0) {
2820                            $LabelAlign = TEXT_ALIGN_TOPMIDDLE;
2821                            $YLabelOffset = 2;
2822                        }
2823                        if ($LabelRotation > 0 && $LabelRotation < 190) {
2824                            $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
2825                            $YLabelOffset = 5;
2826                        }
2827                        if ($LabelRotation == 180) {
2828                            $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE;
2829                            $YLabelOffset = 5;
2830                        }
2831                        if ($LabelRotation > 180 && $LabelRotation < 360) {
2832                            $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
2833                            $YLabelOffset = 2;
2834                        }
2835
2836                        if (!$RemoveXAxis) {
2837                            if ($Floating) {
2838                                $FloatingOffset = $YMargin;
2839                                $this->drawLine(
2840                                    $this->GraphAreaX1 + $Parameters["Margin"],
2841                                    $AxisPos["B"],
2842                                    $this->GraphAreaX2 - $Parameters["Margin"],
2843                                    $AxisPos["B"],
2844                                    ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
2845                                );
2846                            } else {
2847                                $FloatingOffset = 0;
2848                                $this->drawLine(
2849                                    $this->GraphAreaX1,
2850                                    $AxisPos["B"],
2851                                    $this->GraphAreaX2,
2852                                    $AxisPos["B"],
2853                                    ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
2854                                );
2855                            }
2856
2857                            if ($DrawArrows) {
2858                                $this->drawArrow(
2859                                    $this->GraphAreaX2 - $Parameters["Margin"],
2860                                    $AxisPos["B"],
2861                                    $this->GraphAreaX2 + ($ArrowSize * 2),
2862                                    $AxisPos["B"],
2863                                    ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]
2864                                );
2865                            }
2866                        }
2867
2868                        $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"] * 2;
2869
2870                        if ($Parameters["Rows"] == 0) {
2871                            $Step = $Width;
2872                        } else {
2873                            $Step = $Width / ($Parameters["Rows"]);
2874                        }
2875
2876                        $MaxBottom = $AxisPos["B"];
2877                        for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
2878                            $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i;
2879                            $YPos = $AxisPos["B"];
2880
2881                            if ($Abscissa != null) {
2882                                $Value = "";
2883                                if (isset($Data["Series"][$Abscissa]["Data"][$i])) {
2884                                    $Value = $this->scaleFormat(
2885                                        $Data["Series"][$Abscissa]["Data"][$i],
2886                                        $Data["XAxisDisplay"],
2887                                        $Data["XAxisFormat"],
2888                                        $Data["XAxisUnit"]
2889                                    );
2890                                }
2891                            } else {
2892                                $Value = $i;
2893                                if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) {
2894                                    $Value = $this->scaleFormat(
2895                                        $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i,
2896                                        $Data["XAxisDisplay"],
2897                                        $Data["XAxisFormat"],
2898                                        $Data["XAxisUnit"]
2899                                    );
2900                                }
2901                            }
2902
2903                            $ID++;
2904                            $Skipped = true;
2905                            if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip)
2906                                && !$RemoveXAxis
2907                            ) {
2908                                $Bounds = $this->drawText(
2909                                    $XPos,
2910                                    $YPos + $OuterTickWidth + $YLabelOffset,
2911                                    $Value,
2912                                    ["Angle" => $LabelRotation, "Align" => $LabelAlign]
2913                                );
2914                                $TxtBottom = $YPos + $OuterTickWidth + 2 + ($Bounds[0]["Y"] - $Bounds[2]["Y"]);
2915                                $MaxBottom = max($MaxBottom, $TxtBottom);
2916                                $LastValue = $Value;
2917                                $Skipped = false;
2918                            }
2919
2920                            if ($RemoveXAxis) {
2921                                $Skipped = false;
2922                            }
2923
2924                            if ($Skipped) {
2925                                if ($DrawXLines && !$RemoveSkippedAxis) {
2926                                    $this->drawLine(
2927                                        $XPos,
2928                                        $this->GraphAreaY1 + $FloatingOffset,
2929                                        $XPos,
2930                                        $this->GraphAreaY2 - $FloatingOffset,
2931                                        $SkippedAxisColor
2932                                    );
2933                                }
2934                                if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0)
2935                                    && !$RemoveXAxis
2936                                    && !$RemoveSkippedAxis
2937                                ) {
2938                                    $this->drawLine(
2939                                        $XPos,
2940                                        $YPos - $SkippedInnerTickWidth,
2941                                        $XPos,
2942                                        $YPos + $SkippedOuterTickWidth,
2943                                        $SkippedTickColor
2944                                    );
2945                                }
2946                            } else {
2947                                if ($DrawXLines
2948                                    && ($XPos != $this->GraphAreaX1 && $XPos != $this->GraphAreaX2)
2949                                ) {
2950                                    $this->drawLine(
2951                                        $XPos,
2952                                        $this->GraphAreaY1 + $FloatingOffset,
2953                                        $XPos,
2954                                        $this->GraphAreaY2 - $FloatingOffset,
2955                                        [
2956                                            "R" => $GridR,
2957                                            "G" => $GridG,
2958                                            "B" => $GridB,
2959                                            "Alpha" => $GridAlpha,
2960                                            "Ticks" => $GridTicks
2961                                        ]
2962                                    );
2963                                }
2964                                if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) {
2965                                    $this->drawLine(
2966                                        $XPos,
2967                                        $YPos - $InnerTickWidth,
2968                                        $XPos,
2969                                        $YPos + $OuterTickWidth,
2970                                        ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]
2971                                    );
2972                                }
2973                            }
2974                        }
2975
2976                        if (isset($Parameters["Name"]) && !$RemoveXAxis) {
2977                            $YPos = $MaxBottom + 2;
2978                            $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2;
2979                            $Bounds = $this->drawText(
2980                                $XPos,
2981                                $YPos,
2982                                $Parameters["Name"],
2983                                ["Align" => TEXT_ALIGN_TOPMIDDLE]
2984                            );
2985                            $MaxBottom = $Bounds[0]["Y"];
2986
2987                            $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize;
2988                        }
2989
2990                        $AxisPos["B"] = $MaxBottom + $ScaleSpacing;
2991                    } elseif ($Parameters["Position"] == AXIS_POSITION_TOP) {
2992                        if ($LabelRotation == 0) {
2993                            $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE;
2994                            $YLabelOffset = 2;
2995                        }
2996                        if ($LabelRotation > 0 && $LabelRotation < 190) {
2997                            $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
2998                            $YLabelOffset = 2;
2999                        }
3000                        if ($LabelRotation == 180) {
3001                            $LabelAlign = TEXT_ALIGN_TOPMIDDLE;
3002                            $YLabelOffset = 5;
3003                        }
3004                        if ($LabelRotation > 180 && $LabelRotation < 360) {
3005                            $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
3006                            $YLabelOffset = 5;
3007                        }
3008
3009                        if (!$RemoveXAxis) {
3010                            if ($Floating) {
3011                                $FloatingOffset = $YMargin;
3012                                $this->drawLine(
3013                                    $this->GraphAreaX1 + $Parameters["Margin"],
3014                                    $AxisPos["T"],
3015                                    $this->GraphAreaX2 - $Parameters["Margin"],
3016                                    $AxisPos["T"],
3017                                    ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3018                                );
3019                            } else {
3020                                $FloatingOffset = 0;
3021                                $this->drawLine(
3022                                    $this->GraphAreaX1,
3023                                    $AxisPos["T"],
3024                                    $this->GraphAreaX2,
3025                                    $AxisPos["T"],
3026                                    ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3027                                );
3028                            }
3029
3030                            if ($DrawArrows) {
3031                                $this->drawArrow(
3032                                    $this->GraphAreaX2 - $Parameters["Margin"],
3033                                    $AxisPos["T"],
3034                                    $this->GraphAreaX2 + ($ArrowSize * 2),
3035                                    $AxisPos["T"],
3036                                    ["FillR" => $AxisR, "FillG" => $AxisG, "FillB" => $AxisB, "Size" => $ArrowSize]
3037                                );
3038                            }
3039                        }
3040
3041                        $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"] * 2;
3042
3043                        if ($Parameters["Rows"] == 0) {
3044                            $Step = $Width;
3045                        } else {
3046                            $Step = $Width / $Parameters["Rows"];
3047                        }
3048
3049                        $MinTop = $AxisPos["T"];
3050                        for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
3051                            $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i;
3052                            $YPos = $AxisPos["T"];
3053
3054                            if ($Abscissa != null) {
3055                                $Value = "";
3056                                if (isset($Data["Series"][$Abscissa]["Data"][$i])) {
3057                                    $Value = $this->scaleFormat(
3058                                        $Data["Series"][$Abscissa]["Data"][$i],
3059                                        $Data["XAxisDisplay"],
3060                                        $Data["XAxisFormat"],
3061                                        $Data["XAxisUnit"]
3062                                    );
3063                                }
3064                            } else {
3065                                $Value = $i;
3066                                if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) {
3067                                    $Value = $this->scaleFormat(
3068                                        $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i,
3069                                        $Data["XAxisDisplay"],
3070                                        $Data["XAxisFormat"],
3071                                        $Data["XAxisUnit"]
3072                                    );
3073                                }
3074                            }
3075
3076                            $ID++;
3077                            $Skipped = true;
3078                            if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip)
3079                                && !$RemoveXAxis
3080                            ) {
3081                                $Bounds = $this->drawText(
3082                                    $XPos,
3083                                    $YPos - $OuterTickWidth - $YLabelOffset,
3084                                    $Value,
3085                                    ["Angle" => $LabelRotation, "Align" => $LabelAlign]
3086                                );
3087                                $TxtBox = $YPos - $OuterTickWidth - 2 - ($Bounds[0]["Y"] - $Bounds[2]["Y"]);
3088                                $MinTop = min($MinTop, $TxtBox);
3089                                $LastValue = $Value;
3090                                $Skipped = false;
3091                            }
3092
3093                            if ($RemoveXAxis) {
3094                                $Skipped = false;
3095                            }
3096
3097                            if ($Skipped) {
3098                                if ($DrawXLines && !$RemoveSkippedAxis) {
3099                                    $this->drawLine(
3100                                        $XPos,
3101                                        $this->GraphAreaY1 + $FloatingOffset,
3102                                        $XPos,
3103                                        $this->GraphAreaY2 - $FloatingOffset,
3104                                        $SkippedAxisColor
3105                                    );
3106                                }
3107                                if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0)
3108                                    && !$RemoveXAxis
3109                                    && !$RemoveSkippedAxis
3110                                ) {
3111                                    $this->drawLine(
3112                                        $XPos,
3113                                        $YPos + $SkippedInnerTickWidth,
3114                                        $XPos,
3115                                        $YPos - $SkippedOuterTickWidth,
3116                                        $SkippedTickColor
3117                                    );
3118                                }
3119                            } else {
3120                                if ($DrawXLines) {
3121                                    $this->drawLine(
3122                                        $XPos,
3123                                        $this->GraphAreaY1 + $FloatingOffset,
3124                                        $XPos,
3125                                        $this->GraphAreaY2 - $FloatingOffset,
3126                                        [
3127                                            "R" => $GridR,
3128                                            "G" => $GridG,
3129                                            "B" => $GridB,
3130                                            "Alpha" => $GridAlpha,
3131                                            "Ticks" => $GridTicks
3132                                        ]
3133                                    );
3134                                }
3135                                if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) {
3136                                    $this->drawLine(
3137                                        $XPos,
3138                                        $YPos + $InnerTickWidth,
3139                                        $XPos,
3140                                        $YPos - $OuterTickWidth,
3141                                        [
3142                                            "R" => $TickR,
3143                                            "G" => $TickG,
3144                                            "B" => $TickB,
3145                                            "Alpha" => $TickAlpha
3146                                        ]
3147                                    );
3148                                }
3149                            }
3150                        }
3151
3152                        if (isset($Parameters["Name"]) && !$RemoveXAxis) {
3153                            $YPos = $MinTop - 2;
3154                            $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2;
3155                            $Bounds = $this->drawText(
3156                                $XPos,
3157                                $YPos,
3158                                $Parameters["Name"],
3159                                ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]
3160                            );
3161                            $MinTop = $Bounds[2]["Y"];
3162
3163                            $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop;
3164                        }
3165
3166                        $AxisPos["T"] = $MinTop - $ScaleSpacing;
3167                    }
3168                } elseif ($Pos == SCALE_POS_TOPBOTTOM) {
3169                    if ($Parameters["Position"] == AXIS_POSITION_LEFT) {
3170                        if ($LabelRotation == 0) {
3171                            $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
3172                            $XLabelOffset = -2;
3173                        }
3174                        if ($LabelRotation > 0 && $LabelRotation < 190) {
3175                            $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
3176                            $XLabelOffset = -6;
3177                        }
3178                        if ($LabelRotation == 180) {
3179                            $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
3180                            $XLabelOffset = -2;
3181                        }
3182                        if ($LabelRotation > 180 && $LabelRotation < 360) {
3183                            $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
3184                            $XLabelOffset = -5;
3185                        }
3186
3187                        if (!$RemoveXAxis) {
3188                            if ($Floating) {
3189                                $FloatingOffset = $YMargin;
3190                                $this->drawLine(
3191                                    $AxisPos["L"],
3192                                    $this->GraphAreaY1 + $Parameters["Margin"],
3193                                    $AxisPos["L"],
3194                                    $this->GraphAreaY2 - $Parameters["Margin"],
3195                                    ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3196                                );
3197                            } else {
3198                                $FloatingOffset = 0;
3199                                $this->drawLine(
3200                                    $AxisPos["L"],
3201                                    $this->GraphAreaY1,
3202                                    $AxisPos["L"],
3203                                    $this->GraphAreaY2,
3204                                    ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3205                                );
3206                            }
3207
3208                            if ($DrawArrows) {
3209                                $this->drawArrow(
3210                                    $AxisPos["L"],
3211                                    $this->GraphAreaY2 - $Parameters["Margin"],
3212                                    $AxisPos["L"],
3213                                    $this->GraphAreaY2 + ($ArrowSize * 2),
3214                                    [
3215                                        "FillR" => $AxisR,
3216                                        "FillG" => $AxisG,
3217                                        "FillB" => $AxisB,
3218                                        "Size" => $ArrowSize
3219                                    ]
3220                                );
3221                            }
3222                        }
3223
3224                        $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"] * 2;
3225
3226                        if ($Parameters["Rows"] == 0) {
3227                            $Step = $Height;
3228                        } else {
3229                            $Step = $Height / $Parameters["Rows"];
3230                        }
3231
3232                        $MinLeft = $AxisPos["L"];
3233                        for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
3234                            $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step * $i;
3235                            $XPos = $AxisPos["L"];
3236
3237                            if ($Abscissa != null) {
3238                                $Value = "";
3239                                if (isset($Data["Series"][$Abscissa]["Data"][$i])) {
3240                                    $Value = $this->scaleFormat(
3241                                        $Data["Series"][$Abscissa]["Data"][$i],
3242                                        $Data["XAxisDisplay"],
3243                                        $Data["XAxisFormat"],
3244                                        $Data["XAxisUnit"]
3245                                    );
3246                                }
3247                            } else {
3248                                $Value = $i;
3249                                if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) {
3250                                    $Value = $this->scaleFormat(
3251                                        $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i,
3252                                        $Data["XAxisDisplay"],
3253                                        $Data["XAxisFormat"],
3254                                        $Data["XAxisUnit"]
3255                                    );
3256                                }
3257                            }
3258
3259                            $ID++;
3260                            $Skipped = true;
3261                            if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip)
3262                                && !$RemoveXAxis
3263                            ) {
3264                                $Bounds = $this->drawText(
3265                                    $XPos - $OuterTickWidth + $XLabelOffset,
3266                                    $YPos,
3267                                    $Value,
3268                                    ["Angle" => $LabelRotation, "Align" => $LabelAlign]
3269                                );
3270                                $TxtBox = $XPos - $OuterTickWidth - 2 - ($Bounds[1]["X"] - $Bounds[0]["X"]);
3271                                $MinLeft = min($MinLeft, $TxtBox);
3272                                $LastValue = $Value;
3273                                $Skipped = false;
3274                            }
3275
3276                            if ($RemoveXAxis) {
3277                                $Skipped = false;
3278                            }
3279
3280                            if ($Skipped) {
3281                                if ($DrawXLines && !$RemoveSkippedAxis) {
3282                                    $this->drawLine(
3283                                        $this->GraphAreaX1 + $FloatingOffset,
3284                                        $YPos,
3285                                        $this->GraphAreaX2 - $FloatingOffset,
3286                                        $YPos,
3287                                        $SkippedAxisColor
3288                                    );
3289                                }
3290                                if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0)
3291                                    && !$RemoveXAxis
3292                                    && !$RemoveSkippedAxis
3293                                ) {
3294                                    $this->drawLine(
3295                                        $XPos - $SkippedOuterTickWidth,
3296                                        $YPos,
3297                                        $XPos + $SkippedInnerTickWidth,
3298                                        $YPos,
3299                                        $SkippedTickColor
3300                                    );
3301                                }
3302                            } else {
3303                                if ($DrawXLines &&
3304                                    ($YPos != $this->GraphAreaY1 && $YPos != $this->GraphAreaY2)
3305                                ) {
3306                                    $this->drawLine(
3307                                        $this->GraphAreaX1 + $FloatingOffset,
3308                                        $YPos,
3309                                        $this->GraphAreaX2 - $FloatingOffset,
3310                                        $YPos,
3311                                        [
3312                                            "R" => $GridR,
3313                                            "G" => $GridG,
3314                                            "B" => $GridB,
3315                                            "Alpha" => $GridAlpha,
3316                                            "Ticks" => $GridTicks
3317                                        ]
3318                                    );
3319                                }
3320                                if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) {
3321                                    $this->drawLine(
3322                                        $XPos - $OuterTickWidth,
3323                                        $YPos,
3324                                        $XPos + $InnerTickWidth,
3325                                        $YPos,
3326                                        ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]
3327                                    );
3328                                }
3329                            }
3330                        }
3331                        if (isset($Parameters["Name"]) && !$RemoveXAxis) {
3332                            $XPos = $MinLeft - 2;
3333                            $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2;
3334                            $Bounds = $this->drawText(
3335                                $XPos,
3336                                $YPos,
3337                                $Parameters["Name"],
3338                                ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 90]
3339                            );
3340                            $MinLeft = $Bounds[0]["X"];
3341
3342                            $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft;
3343                        }
3344
3345                        $AxisPos["L"] = $MinLeft - $ScaleSpacing;
3346                    } elseif ($Parameters["Position"] == AXIS_POSITION_RIGHT) {
3347                        if ($LabelRotation == 0) {
3348                            $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
3349                            $XLabelOffset = 2;
3350                        }
3351                        if ($LabelRotation > 0 && $LabelRotation < 190) {
3352                            $LabelAlign = TEXT_ALIGN_MIDDLELEFT;
3353                            $XLabelOffset = 6;
3354                        }
3355                        if ($LabelRotation == 180) {
3356                            $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
3357                            $XLabelOffset = 5;
3358                        }
3359                        if ($LabelRotation > 180 && $LabelRotation < 360) {
3360                            $LabelAlign = TEXT_ALIGN_MIDDLERIGHT;
3361                            $XLabelOffset = 7;
3362                        }
3363
3364                        if (!$RemoveXAxis) {
3365                            if ($Floating) {
3366                                $FloatingOffset = $YMargin;
3367                                $this->drawLine(
3368                                    $AxisPos["R"],
3369                                    $this->GraphAreaY1 + $Parameters["Margin"],
3370                                    $AxisPos["R"],
3371                                    $this->GraphAreaY2 - $Parameters["Margin"],
3372                                    ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3373                                );
3374                            } else {
3375                                $FloatingOffset = 0;
3376                                $this->drawLine(
3377                                    $AxisPos["R"],
3378                                    $this->GraphAreaY1,
3379                                    $AxisPos["R"],
3380                                    $this->GraphAreaY2,
3381                                    ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3382                                );
3383                            }
3384
3385                            if ($DrawArrows) {
3386                                $this->drawArrow(
3387                                    $AxisPos["R"],
3388                                    $this->GraphAreaY2 - $Parameters["Margin"],
3389                                    $AxisPos["R"],
3390                                    $this->GraphAreaY2 + ($ArrowSize * 2),
3391                                    [
3392                                        "FillR" => $AxisR,
3393                                        "FillG" => $AxisG,
3394                                        "FillB" => $AxisB,
3395                                        "Size" => $ArrowSize
3396                                    ]
3397                                );
3398                            }
3399                        }
3400
3401                        $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"] * 2;
3402
3403                        if ($Parameters["Rows"] == 0) {
3404                            $Step = $Height;
3405                        } else {
3406                            $Step = $Height / $Parameters["Rows"];
3407                        }
3408
3409                        $MaxRight = $AxisPos["R"];
3410                        for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
3411                            $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step * $i;
3412                            $XPos = $AxisPos["R"];
3413
3414                            if ($Abscissa != null) {
3415                                $Value = "";
3416                                if (isset($Data["Series"][$Abscissa]["Data"][$i])) {
3417                                    $Value = $this->scaleFormat(
3418                                        $Data["Series"][$Abscissa]["Data"][$i],
3419                                        $Data["XAxisDisplay"],
3420                                        $Data["XAxisFormat"],
3421                                        $Data["XAxisUnit"]
3422                                    );
3423                                }
3424                            } else {
3425                                $Value = $i;
3426                                if (isset($Parameters["ScaleMin"]) && isset($Parameters["RowHeight"])) {
3427                                    $Value = $this->scaleFormat(
3428                                        $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i,
3429                                        $Data["XAxisDisplay"],
3430                                        $Data["XAxisFormat"],
3431                                        $Data["XAxisUnit"]
3432                                    );
3433                                }
3434                            }
3435
3436                            $ID++;
3437                            $Skipped = true;
3438                            if ($this->isValidLabel($Value, $LastValue, $LabelingMethod, $ID, $LabelSkip)
3439                                && !$RemoveXAxis
3440                            ) {
3441                                $Bounds = $this->drawText(
3442                                    $XPos + $OuterTickWidth + $XLabelOffset,
3443                                    $YPos,
3444                                    $Value,
3445                                    ["Angle" => $LabelRotation, "Align" => $LabelAlign]
3446                                );
3447                                $TxtBox = $XPos + $OuterTickWidth + 2 + ($Bounds[1]["X"] - $Bounds[0]["X"]);
3448                                $MaxRight = max($MaxRight, $TxtBox);
3449                                $LastValue = $Value;
3450                                $Skipped = false;
3451                            }
3452
3453                            if ($RemoveXAxis) {
3454                                $Skipped = false;
3455                            }
3456
3457                            if ($Skipped) {
3458                                if ($DrawXLines && !$RemoveSkippedAxis) {
3459                                    $this->drawLine(
3460                                        $this->GraphAreaX1 + $FloatingOffset,
3461                                        $YPos,
3462                                        $this->GraphAreaX2 - $FloatingOffset,
3463                                        $YPos,
3464                                        $SkippedAxisColor
3465                                    );
3466                                }
3467                                if (($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0)
3468                                    && !$RemoveXAxis
3469                                    && !$RemoveSkippedAxis
3470                                ) {
3471                                    $this->drawLine(
3472                                        $XPos + $SkippedOuterTickWidth,
3473                                        $YPos,
3474                                        $XPos - $SkippedInnerTickWidth,
3475                                        $YPos,
3476                                        $SkippedTickColor
3477                                    );
3478                                }
3479                            } else {
3480                                if ($DrawXLines) {
3481                                    $this->drawLine(
3482                                        $this->GraphAreaX1 + $FloatingOffset,
3483                                        $YPos,
3484                                        $this->GraphAreaX2 - $FloatingOffset,
3485                                        $YPos,
3486                                        [
3487                                            "R" => $GridR,
3488                                            "G" => $GridG,
3489                                            "B" => $GridB,
3490                                            "Alpha" => $GridAlpha,
3491                                            "Ticks" => $GridTicks
3492                                        ]
3493                                    );
3494                                }
3495                                if (($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis) {
3496                                    $this->drawLine(
3497                                        $XPos + $OuterTickWidth,
3498                                        $YPos,
3499                                        $XPos - $InnerTickWidth,
3500                                        $YPos,
3501                                        [
3502                                            "R" => $TickR,
3503                                            "G" => $TickG,
3504                                            "B" => $TickB,
3505                                            "Alpha" => $TickAlpha
3506                                        ]
3507                                    );
3508                                }
3509                            }
3510                        }
3511
3512                        if (isset($Parameters["Name"]) && !$RemoveXAxis) {
3513                            $XPos = $MaxRight + 4;
3514                            $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2;
3515                            $Bounds = $this->drawText(
3516                                $XPos,
3517                                $YPos,
3518                                $Parameters["Name"],
3519                                ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 270]
3520                            );
3521                            $MaxRight = $Bounds[1]["X"];
3522
3523                            $this->DataSet->Data["GraphArea"]["X2"] = $MaxRight + $this->FontSize;
3524                        }
3525
3526                        $AxisPos["R"] = $MaxRight + $ScaleSpacing;
3527                    }
3528                }
3529            }
3530
3531            if ($Parameters["Identity"] == AXIS_Y && !$RemoveYAxis) {
3532                if ($Pos == SCALE_POS_LEFTRIGHT) {
3533                    if ($Parameters["Position"] == AXIS_POSITION_LEFT) {
3534                        if ($Floating) {
3535                            $FloatingOffset = $XMargin;
3536                            $this->drawLine(
3537                                $AxisPos["L"],
3538                                $this->GraphAreaY1 + $Parameters["Margin"],
3539                                $AxisPos["L"],
3540                                $this->GraphAreaY2 - $Parameters["Margin"],
3541                                ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3542                            );
3543                        } else {
3544                            $FloatingOffset = 0;
3545                            $this->drawLine(
3546                                $AxisPos["L"],
3547                                $this->GraphAreaY1,
3548                                $AxisPos["L"],
3549                                $this->GraphAreaY2,
3550                                ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3551                            );
3552                        }
3553
3554                        if ($DrawArrows) {
3555                            $this->drawArrow(
3556                                $AxisPos["L"],
3557                                $this->GraphAreaY1 + $Parameters["Margin"],
3558                                $AxisPos["L"],
3559                                $this->GraphAreaY1 - ($ArrowSize * 2),
3560                                [
3561                                    "FillR" => $AxisR,
3562                                    "FillG" => $AxisG,
3563                                    "FillB" => $AxisB,
3564                                    "Size" => $ArrowSize
3565                                ]
3566                            );
3567                        }
3568
3569                        $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"] * 2;
3570                        $Step = $Height / $Parameters["Rows"];
3571                        $SubTicksSize = $Step / 2;
3572                        $MinLeft = $AxisPos["L"];
3573                        $LastY = null;
3574                        for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
3575                            $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step * $i;
3576                            $XPos = $AxisPos["L"];
3577                            $Value = $this->scaleFormat(
3578                                $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i,
3579                                $Parameters["Display"],
3580                                $Parameters["Format"],
3581                                $Parameters["Unit"]
3582                            );
3583
3584                            if ($i % 2 == 1) {
3585                                $BGColor = [
3586                                    "R" => $BackgroundR1,
3587                                    "G" => $BackgroundG1,
3588                                    "B" => $BackgroundB1,
3589                                    "Alpha" => $BackgroundAlpha1
3590                                ];
3591                            } else {
3592                                $BGColor = [
3593                                    "R" => $BackgroundR2,
3594                                    "G" => $BackgroundG2,
3595                                    "B" => $BackgroundB2,
3596                                    "Alpha" => $BackgroundAlpha2
3597                                ];
3598                            }
3599                            if ($LastY != null
3600                                && $CycleBackground
3601                                && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines))
3602                            ) {
3603                                $this->drawFilledRectangle(
3604                                    $this->GraphAreaX1 + $FloatingOffset,
3605                                    $LastY,
3606                                    $this->GraphAreaX2 - $FloatingOffset,
3607                                    $YPos,
3608                                    $BGColor
3609                                );
3610                            }
3611
3612                            if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) {
3613                                $this->drawLine(
3614                                    $this->GraphAreaX1 + $FloatingOffset,
3615                                    $YPos,
3616                                    $this->GraphAreaX2 - $FloatingOffset,
3617                                    $YPos,
3618                                    [
3619                                        "R" => $GridR,
3620                                        "G" => $GridG,
3621                                        "B" => $GridB,
3622                                        "Alpha" => $GridAlpha,
3623                                        "Ticks" => $GridTicks
3624                                    ]
3625                                );
3626                            }
3627
3628                            if ($DrawSubTicks && $i != $Parameters["Rows"]) {
3629                                $this->drawLine(
3630                                    $XPos - $OuterSubTickWidth,
3631                                    $YPos - $SubTicksSize,
3632                                    $XPos + $InnerSubTickWidth,
3633                                    $YPos - $SubTicksSize,
3634                                    [
3635                                        "R" => $SubTickR,
3636                                        "G" => $SubTickG,
3637                                        "B" => $SubTickB,
3638                                        "Alpha" => $SubTickAlpha
3639                                    ]
3640                                );
3641                            }
3642                            if (!$RemoveYAxiValues) {
3643                                $this->drawLine(
3644                                    $XPos - $OuterTickWidth,
3645                                    $YPos,
3646                                    $XPos + $InnerTickWidth,
3647                                    $YPos,
3648                                    ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]
3649                                );
3650                                $Bounds = $this->drawText(
3651                                    $XPos - $OuterTickWidth - 2,
3652                                    $YPos,
3653                                    $Value,
3654                                    ["Align" => TEXT_ALIGN_MIDDLERIGHT]
3655                                );
3656                                $TxtLeft = $XPos - $OuterTickWidth - 2 - ($Bounds[1]["X"] - $Bounds[0]["X"]);
3657                                $MinLeft = min($MinLeft, $TxtLeft);
3658                            }
3659
3660                            $LastY = $YPos;
3661                        }
3662
3663                        if (isset($Parameters["Name"])) {
3664                            $XPos = $MinLeft - 2;
3665                            $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2;
3666                            $Bounds = $this->drawText(
3667                                $XPos,
3668                                $YPos,
3669                                $Parameters["Name"],
3670                                ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 90]
3671                            );
3672                            $MinLeft = $Bounds[2]["X"];
3673
3674                            $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft;
3675                        }
3676
3677                        $AxisPos["L"] = $MinLeft - $ScaleSpacing;
3678                    } elseif ($Parameters["Position"] == AXIS_POSITION_RIGHT) {
3679                        if ($Floating) {
3680                            $FloatingOffset = $XMargin;
3681                            $this->drawLine(
3682                                $AxisPos["R"],
3683                                $this->GraphAreaY1 + $Parameters["Margin"],
3684                                $AxisPos["R"],
3685                                $this->GraphAreaY2 - $Parameters["Margin"],
3686                                ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3687                            );
3688                        } else {
3689                            $FloatingOffset = 0;
3690                            $this->drawLine(
3691                                $AxisPos["R"],
3692                                $this->GraphAreaY1,
3693                                $AxisPos["R"],
3694                                $this->GraphAreaY2,
3695                                ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3696                            );
3697                        }
3698
3699                        if ($DrawArrows) {
3700                            $this->drawArrow(
3701                                $AxisPos["R"],
3702                                $this->GraphAreaY1 + $Parameters["Margin"],
3703                                $AxisPos["R"],
3704                                $this->GraphAreaY1 - ($ArrowSize * 2),
3705                                [
3706                                    "FillR" => $AxisR,
3707                                    "FillG" => $AxisG,
3708                                    "FillB" => $AxisB,
3709                                    "Size" => $ArrowSize
3710                                ]
3711                            );
3712                        }
3713
3714                        $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"] * 2;
3715                        $Step = $Height / $Parameters["Rows"];
3716                        $SubTicksSize = $Step / 2;
3717                        $MaxLeft = $AxisPos["R"];
3718                        $LastY = null;
3719                        for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
3720                            $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step * $i;
3721                            $XPos = $AxisPos["R"];
3722                            $Value = $this->scaleFormat(
3723                                $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i,
3724                                $Parameters["Display"],
3725                                $Parameters["Format"],
3726                                $Parameters["Unit"]
3727                            );
3728
3729                            if ($i % 2 == 1) {
3730                                $BGColor = [
3731                                    "R" => $BackgroundR1,
3732                                    "G" => $BackgroundG1,
3733                                    "B" => $BackgroundB1,
3734                                    "Alpha" => $BackgroundAlpha1
3735                                ];
3736                            } else {
3737                                $BGColor = [
3738                                    "R" => $BackgroundR2,
3739                                    "G" => $BackgroundG2,
3740                                    "B" => $BackgroundB2,
3741                                    "Alpha" => $BackgroundAlpha2
3742                                ];
3743                            }
3744                            if ($LastY != null
3745                                && $CycleBackground
3746                                && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines))
3747                            ) {
3748                                $this->drawFilledRectangle(
3749                                    $this->GraphAreaX1 + $FloatingOffset,
3750                                    $LastY,
3751                                    $this->GraphAreaX2 - $FloatingOffset,
3752                                    $YPos,
3753                                    $BGColor
3754                                );
3755                            }
3756
3757                            if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) {
3758                                $this->drawLine(
3759                                    $this->GraphAreaX1 + $FloatingOffset,
3760                                    $YPos,
3761                                    $this->GraphAreaX2 - $FloatingOffset,
3762                                    $YPos,
3763                                    [
3764                                        "R" => $GridR,
3765                                        "G" => $GridG,
3766                                        "B" => $GridB,
3767                                        "Alpha" => $GridAlpha,
3768                                        "Ticks" => $GridTicks
3769                                    ]
3770                                );
3771                            }
3772
3773                            if ($DrawSubTicks && $i != $Parameters["Rows"]) {
3774                                $this->drawLine(
3775                                    $XPos - $OuterSubTickWidth,
3776                                    $YPos - $SubTicksSize,
3777                                    $XPos + $InnerSubTickWidth,
3778                                    $YPos - $SubTicksSize,
3779                                    [
3780                                        "R" => $SubTickR,
3781                                        "G" => $SubTickG,
3782                                        "B" => $SubTickB,
3783                                        "Alpha" => $SubTickAlpha
3784                                    ]
3785                                );
3786                            }
3787                            $this->drawLine(
3788                                $XPos - $InnerTickWidth,
3789                                $YPos,
3790                                $XPos + $OuterTickWidth,
3791                                $YPos,
3792                                ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]
3793                            );
3794                            $Bounds = $this->drawText(
3795                                $XPos + $OuterTickWidth + 2,
3796                                $YPos,
3797                                $Value,
3798                                ["Align" => TEXT_ALIGN_MIDDLELEFT]
3799                            );
3800                            $TxtLeft = $XPos + $OuterTickWidth + 2 + ($Bounds[1]["X"] - $Bounds[0]["X"]);
3801                            $MaxLeft = max($MaxLeft, $TxtLeft);
3802
3803                            $LastY = $YPos;
3804                        }
3805
3806                        if (isset($Parameters["Name"])) {
3807                            $XPos = $MaxLeft + 6;
3808                            $YPos = $this->GraphAreaY1 + ($this->GraphAreaY2 - $this->GraphAreaY1) / 2;
3809                            $Bounds = $this->drawText(
3810                                $XPos,
3811                                $YPos,
3812                                $Parameters["Name"],
3813                                ["Align" => TEXT_ALIGN_BOTTOMMIDDLE, "Angle" => 270]
3814                            );
3815                            $MaxLeft = $Bounds[2]["X"];
3816
3817                            $this->DataSet->Data["GraphArea"]["X2"] = $MaxLeft + $this->FontSize;
3818                        }
3819                        $AxisPos["R"] = $MaxLeft + $ScaleSpacing;
3820                    }
3821                } elseif ($Pos == SCALE_POS_TOPBOTTOM) {
3822                    if ($Parameters["Position"] == AXIS_POSITION_TOP) {
3823                        if ($Floating) {
3824                            $FloatingOffset = $XMargin;
3825                            $this->drawLine(
3826                                $this->GraphAreaX1 + $Parameters["Margin"],
3827                                $AxisPos["T"],
3828                                $this->GraphAreaX2 - $Parameters["Margin"],
3829                                $AxisPos["T"],
3830                                ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3831                            );
3832                        } else {
3833                            $FloatingOffset = 0;
3834                            $this->drawLine(
3835                                $this->GraphAreaX1,
3836                                $AxisPos["T"],
3837                                $this->GraphAreaX2,
3838                                $AxisPos["T"],
3839                                ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3840                            );
3841                        }
3842
3843                        if ($DrawArrows) {
3844                            $this->drawArrow(
3845                                $this->GraphAreaX2 - $Parameters["Margin"],
3846                                $AxisPos["T"],
3847                                $this->GraphAreaX2 + ($ArrowSize * 2),
3848                                $AxisPos["T"],
3849                                [
3850                                    "FillR" => $AxisR,
3851                                    "FillG" => $AxisG,
3852                                    "FillB" => $AxisB,
3853                                    "Size" => $ArrowSize
3854                                ]
3855                            );
3856                        }
3857
3858                        $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"] * 2;
3859                        $Step = $Width / $Parameters["Rows"];
3860                        $SubTicksSize = $Step / 2;
3861                        $MinTop = $AxisPos["T"];
3862                        $LastX = null;
3863                        for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
3864                            $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i;
3865                            $YPos = $AxisPos["T"];
3866                            $Value = $this->scaleFormat(
3867                                $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i,
3868                                $Parameters["Display"],
3869                                $Parameters["Format"],
3870                                $Parameters["Unit"]
3871                            );
3872
3873                            if ($i % 2 == 1) {
3874                                $BGColor = [
3875                                    "R" => $BackgroundR1,
3876                                    "G" => $BackgroundG1,
3877                                    "B" => $BackgroundB1,
3878                                    "Alpha" => $BackgroundAlpha1
3879                                ];
3880                            } else {
3881                                $BGColor = [
3882                                    "R" => $BackgroundR2,
3883                                    "G" => $BackgroundG2,
3884                                    "B" => $BackgroundB2,
3885                                    "Alpha" => $BackgroundAlpha2
3886                                ];
3887                            }
3888                            if ($LastX != null
3889                                && $CycleBackground
3890                                && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines))
3891                            ) {
3892                                $this->drawFilledRectangle(
3893                                    $LastX,
3894                                    $this->GraphAreaY1 + $FloatingOffset,
3895                                    $XPos,
3896                                    $this->GraphAreaY2 - $FloatingOffset,
3897                                    $BGColor
3898                                );
3899                            }
3900
3901                            if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) {
3902                                $this->drawLine(
3903                                    $XPos,
3904                                    $this->GraphAreaY1 + $FloatingOffset,
3905                                    $XPos,
3906                                    $this->GraphAreaY2 - $FloatingOffset,
3907                                    [
3908                                        "R" => $GridR,
3909                                        "G" => $GridG,
3910                                        "B" => $GridB,
3911                                        "Alpha" => $GridAlpha,
3912                                        "Ticks" => $GridTicks
3913                                    ]
3914                                );
3915                            }
3916
3917                            if ($DrawSubTicks && $i != $Parameters["Rows"]) {
3918                                $this->drawLine(
3919                                    $XPos + $SubTicksSize,
3920                                    $YPos - $OuterSubTickWidth,
3921                                    $XPos + $SubTicksSize,
3922                                    $YPos + $InnerSubTickWidth,
3923                                    [
3924                                        "R" => $SubTickR,
3925                                        "G" => $SubTickG,
3926                                        "B" => $SubTickB,
3927                                        "Alpha" => $SubTickAlpha
3928                                    ]
3929                                );
3930                            }
3931                            $this->drawLine(
3932                                $XPos,
3933                                $YPos - $OuterTickWidth,
3934                                $XPos,
3935                                $YPos + $InnerTickWidth,
3936                                ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]
3937                            );
3938                            $Bounds = $this->drawText(
3939                                $XPos,
3940                                $YPos - $OuterTickWidth - 2,
3941                                $Value,
3942                                ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]
3943                            );
3944                            $TxtHeight = $YPos - $OuterTickWidth - 2 - ($Bounds[1]["Y"] - $Bounds[2]["Y"]);
3945                            $MinTop = min($MinTop, $TxtHeight);
3946
3947                            $LastX = $XPos;
3948                        }
3949
3950                        if (isset($Parameters["Name"])) {
3951                            $YPos = $MinTop - 2;
3952                            $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2;
3953                            $Bounds = $this->drawText(
3954                                $XPos,
3955                                $YPos,
3956                                $Parameters["Name"],
3957                                ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]
3958                            );
3959                            $MinTop = $Bounds[2]["Y"];
3960
3961                            $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop;
3962                        }
3963
3964                        $AxisPos["T"] = $MinTop - $ScaleSpacing;
3965                    } elseif ($Parameters["Position"] == AXIS_POSITION_BOTTOM) {
3966                        if ($Floating) {
3967                            $FloatingOffset = $XMargin;
3968                            $this->drawLine(
3969                                $this->GraphAreaX1 + $Parameters["Margin"],
3970                                $AxisPos["B"],
3971                                $this->GraphAreaX2 - $Parameters["Margin"],
3972                                $AxisPos["B"],
3973                                ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3974                            );
3975                        } else {
3976                            $FloatingOffset = 0;
3977                            $this->drawLine(
3978                                $this->GraphAreaX1,
3979                                $AxisPos["B"],
3980                                $this->GraphAreaX2,
3981                                $AxisPos["B"],
3982                                ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha]
3983                            );
3984                        }
3985
3986                        if ($DrawArrows) {
3987                            $this->drawArrow(
3988                                $this->GraphAreaX2 - $Parameters["Margin"],
3989                                $AxisPos["B"],
3990                                $this->GraphAreaX2 + ($ArrowSize * 2),
3991                                $AxisPos["B"],
3992                                [
3993                                    "FillR" => $AxisR,
3994                                    "FillG" => $AxisG,
3995                                    "FillB" => $AxisB,
3996                                    "Size" => $ArrowSize
3997                                ]
3998                            );
3999                        }
4000
4001                        $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"] * 2;
4002                        $Step = $Width / $Parameters["Rows"];
4003                        $SubTicksSize = $Step / 2;
4004                        $MaxBottom = $AxisPos["B"];
4005                        $LastX = null;
4006                        for ($i = 0; $i <= $Parameters["Rows"]; $i++) {
4007                            $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step * $i;
4008                            $YPos = $AxisPos["B"];
4009                            $Value = $this->scaleFormat(
4010                                $Parameters["ScaleMin"] + $Parameters["RowHeight"] * $i,
4011                                $Parameters["Display"],
4012                                $Parameters["Format"],
4013                                $Parameters["Unit"]
4014                            );
4015
4016                            if ($i % 2 == 1) {
4017                                $BGColor = [
4018                                    "R" => $BackgroundR1,
4019                                    "G" => $BackgroundG1,
4020                                    "B" => $BackgroundB1,
4021                                    "Alpha" => $BackgroundAlpha1
4022                                ];
4023                            } else {
4024                                $BGColor = [
4025                                    "R" => $BackgroundR2,
4026                                    "G" => $BackgroundG2,
4027                                    "B" => $BackgroundB2,
4028                                    "Alpha" => $BackgroundAlpha2
4029                                ];
4030                            }
4031                            if ($LastX != null
4032                                && $CycleBackground
4033                                && ($DrawYLines == ALL || in_array($AxisID, $DrawYLines))
4034                            ) {
4035                                $this->drawFilledRectangle(
4036                                    $LastX,
4037                                    $this->GraphAreaY1 + $FloatingOffset,
4038                                    $XPos,
4039                                    $this->GraphAreaY2 - $FloatingOffset,
4040                                    $BGColor
4041                                );
4042                            }
4043
4044                            if ($DrawYLines == ALL || in_array($AxisID, $DrawYLines)) {
4045                                $this->drawLine(
4046                                    $XPos,
4047                                    $this->GraphAreaY1 + $FloatingOffset,
4048                                    $XPos,
4049                                    $this->GraphAreaY2 - $FloatingOffset,
4050                                    [
4051                                        "R" => $GridR,
4052                                        "G" => $GridG,
4053                                        "B" => $GridB,
4054                                        "Alpha" => $GridAlpha,
4055                                        "Ticks" => $GridTicks
4056                                    ]
4057                                );
4058                            }
4059
4060                            if ($DrawSubTicks && $i != $Parameters["Rows"]) {
4061                                $this->drawLine(
4062                                    $XPos + $SubTicksSize,
4063                                    $YPos - $OuterSubTickWidth,
4064                                    $XPos + $SubTicksSize,
4065                                    $YPos + $InnerSubTickWidth,
4066                                    [
4067                                        "R" => $SubTickR,
4068                                        "G" => $SubTickG,
4069                                        "B" => $SubTickB,
4070                                        "Alpha" => $SubTickAlpha
4071                                    ]
4072                                );
4073                            }
4074                            $this->drawLine(
4075                                $XPos,
4076                                $YPos - $OuterTickWidth,
4077                                $XPos,
4078                                $YPos + $InnerTickWidth,
4079                                ["R" => $TickR, "G" => $TickG, "B" => $TickB, "Alpha" => $TickAlpha]
4080                            );
4081                            $Bounds = $this->drawText(
4082                                $XPos,
4083                                $YPos + $OuterTickWidth + 2,
4084                                $Value,
4085                                ["Align" => TEXT_ALIGN_TOPMIDDLE]
4086                            );
4087                            $TxtHeight = $YPos + $OuterTickWidth + 2 + ($Bounds[1]["Y"] - $Bounds[2]["Y"]);
4088                            $MaxBottom = max($MaxBottom, $TxtHeight);
4089
4090                            $LastX = $XPos;
4091                        }
4092
4093                        if (isset($Parameters["Name"])) {
4094                            $YPos = $MaxBottom + 2;
4095                            $XPos = $this->GraphAreaX1 + ($this->GraphAreaX2 - $this->GraphAreaX1) / 2;
4096                            $Bounds = $this->drawText(
4097                                $XPos,
4098                                $YPos,
4099                                $Parameters["Name"],
4100                                ["Align" => TEXT_ALIGN_TOPMIDDLE]
4101                            );
4102                            $MaxBottom = $Bounds[0]["Y"];
4103
4104                            $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize;
4105                        }
4106
4107                        $AxisPos["B"] = $MaxBottom + $ScaleSpacing;
4108                    }
4109                }
4110            }
4111        }
4112    }
4113
4114    /**
4115     * Draw an X threshold
4116     * @param mixed $Value
4117     * @param boolean $Format
4118     * @return array|null|integer
4119     */
4120    public function drawXThreshold($Value, array $Format = [])
4121    {
4122        $R = isset($Format["R"]) ? $Format["R"] : 255;
4123        $G = isset($Format["G"]) ? $Format["G"] : 0;
4124        $B = isset($Format["B"]) ? $Format["B"] : 0;
4125        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50;
4126        $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
4127        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6;
4128        $Wide = isset($Format["Wide"]) ? $Format["Wide"] : false;
4129        $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5;
4130        $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : false;
4131        $Caption = isset($Format["Caption"]) ? $Format["Caption"] : null;
4132        $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP;
4133        $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 5;
4134        $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255;
4135        $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255;
4136        $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255;
4137        $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100;
4138        $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : true;
4139        $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : false;
4140        $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 3;
4141        $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : true;
4142        $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3;
4143        $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
4144        $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
4145        $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
4146        $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 30;
4147        $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
4148        $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255;
4149        $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255;
4150        $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255;
4151        $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100;
4152        $ValueIsLabel = isset($Format["ValueIsLabel"]) ? $Format["ValueIsLabel"] : false;
4153
4154        $Data = $this->DataSet->getData();
4155        $AbscissaMargin = $this->getAbscissaMargin($Data);
4156        $XScale = $this->scaleGetXSettings();
4157
4158        if (is_array($Value)) {
4159            foreach ($Value as $Key => $ID) {
4160                $this->drawXThreshold($ID, $Format);
4161            }
4162            return 0;
4163        }
4164
4165        if ($ValueIsLabel) {
4166            $Format["ValueIsLabel"] = false;
4167            foreach ($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $SerieValue) {
4168                if ($SerieValue == $Value) {
4169                    $this->drawXThreshold($Key, $Format);
4170                }
4171            }
4172            return 0;
4173        }
4174
4175        $CaptionSettings = [
4176            "DrawBox" => $DrawBox,
4177            "DrawBoxBorder" => $DrawBoxBorder,
4178            "BorderOffset" => $BorderOffset,
4179            "BoxRounded" => $BoxRounded,
4180            "RoundedRadius" => $RoundedRadius,
4181            "BoxR" => $BoxR,
4182            "BoxG" => $BoxG,
4183            "BoxB" => $BoxB,
4184            "BoxAlpha" => $BoxAlpha,
4185            "BoxSurrounding" => $BoxSurrounding,
4186            "BoxBorderR" => $BoxBorderR,
4187            "BoxBorderG" => $BoxBorderG,
4188            "BoxBorderB" => $BoxBorderB,
4189            "BoxBorderAlpha" => $BoxBorderAlpha,
4190            "R" => $CaptionR,
4191            "G" => $CaptionG,
4192            "B" => $CaptionB,
4193            "Alpha" => $CaptionAlpha
4194        ];
4195
4196        if ($Caption == null) {
4197            $Caption = $Value;
4198            if (isset($Data["Abscissa"])
4199                && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Value])
4200            ) {
4201                $Caption = $Data["Series"][$Data["Abscissa"]]["Data"][$Value];
4202            }
4203        }
4204
4205        if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
4206            $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] * 2) / $XScale[1];
4207            $XPos = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value;
4208            $YPos1 = $this->GraphAreaY1 + $Data["YMargin"];
4209            $YPos2 = $this->GraphAreaY2 - $Data["YMargin"];
4210
4211            if ($XPos >= $this->GraphAreaX1 + $AbscissaMargin
4212                && $XPos <= $this->GraphAreaX2 - $AbscissaMargin
4213            ) {
4214                $this->drawLine(
4215                    $XPos,
4216                    $YPos1,
4217                    $XPos,
4218                    $YPos2,
4219                    [
4220                        "R" => $R,
4221                        "G" => $G,
4222                        "B" => $B,
4223                        "Alpha" => $Alpha,
4224                        "Ticks" => $Ticks,
4225                        "Weight" => $Weight
4226                    ]
4227                );
4228
4229                if ($Wide) {
4230                    $this->drawLine(
4231                        $XPos - 1,
4232                        $YPos1,
4233                        $XPos - 1,
4234                        $YPos2,
4235                        [
4236                            "R" => $R,
4237                            "G" => $G,
4238                            "B" => $B,
4239                            "Alpha" => $Alpha / $WideFactor,
4240                            "Ticks" => $Ticks
4241                        ]
4242                    );
4243                    $this->drawLine(
4244                        $XPos + 1,
4245                        $YPos1,
4246                        $XPos + 1,
4247                        $YPos2,
4248                        [
4249                            "R" => $R,
4250                            "G" => $G,
4251                            "B" => $B,
4252                            "Alpha" => $Alpha / $WideFactor,
4253                            "Ticks" => $Ticks
4254                        ]
4255                    );
4256                }
4257
4258                if ($WriteCaption) {
4259                    if ($CaptionAlign == CAPTION_LEFT_TOP) {
4260                        $Y = $YPos1 + $CaptionOffset;
4261                        $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE;
4262                    } else {
4263                        $Y = $YPos2 - $CaptionOffset;
4264                        $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE;
4265                    }
4266                    $this->drawText($XPos, $Y, $Caption, $CaptionSettings);
4267                }
4268
4269                return ["X" => $XPos];
4270            }
4271        } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
4272            $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] * 2) / $XScale[1];
4273            $XPos = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value;
4274            $YPos1 = $this->GraphAreaX1 + $Data["YMargin"];
4275            $YPos2 = $this->GraphAreaX2 - $Data["YMargin"];
4276
4277            if ($XPos >= $this->GraphAreaY1 + $AbscissaMargin
4278                && $XPos <= $this->GraphAreaY2 - $AbscissaMargin
4279            ) {
4280                $this->drawLine(
4281                    $YPos1,
4282                    $XPos,
4283                    $YPos2,
4284                    $XPos,
4285                    [
4286                        "R" => $R,
4287                        "G" => $G,
4288                        "B" => $B,
4289                        "Alpha" => $Alpha,
4290                        "Ticks" => $Ticks,
4291                        "Weight" => $Weight
4292                    ]
4293                );
4294
4295                if ($Wide) {
4296                    $this->drawLine(
4297                        $YPos1,
4298                        $XPos - 1,
4299                        $YPos2,
4300                        $XPos - 1,
4301                        [
4302                            "R" => $R,
4303                            "G" => $G,
4304                            "B" => $B,
4305                            "Alpha" => $Alpha / $WideFactor,
4306                            "Ticks" => $Ticks
4307                        ]
4308                    );
4309                    $this->drawLine(
4310                        $YPos1,
4311                        $XPos + 1,
4312                        $YPos2,
4313                        $XPos + 1,
4314                        [
4315                            "R" => $R,
4316                            "G" => $G,
4317                            "B" => $B,
4318                            "Alpha" => $Alpha / $WideFactor,
4319                            "Ticks" => $Ticks
4320                        ]
4321                    );
4322                }
4323
4324                if ($WriteCaption) {
4325                    if ($CaptionAlign == CAPTION_LEFT_TOP) {
4326                        $Y = $YPos1 + $CaptionOffset;
4327                        $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT;
4328                    } else {
4329                        $Y = $YPos2 - $CaptionOffset;
4330                        $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT;
4331                    }
4332
4333                    $this->drawText($Y, $XPos, $Caption, $CaptionSettings);
4334                }
4335
4336                return ["X" => $XPos];
4337            }
4338        }
4339    }
4340
4341    /**
4342     * Draw an X threshold area
4343     * @param mixed $Value1
4344     * @param mixed $Value2
4345     * @param array $Format
4346     * @return array|null
4347     */
4348    public function drawXThresholdArea($Value1, $Value2, array $Format = [])
4349    {
4350        $R = isset($Format["R"]) ? $Format["R"] : 255;
4351        $G = isset($Format["G"]) ? $Format["G"] : 0;
4352        $B = isset($Format["B"]) ? $Format["B"] : 0;
4353        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20;
4354        $Border = isset($Format["Border"]) ? $Format["Border"] : true;
4355        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
4356        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
4357        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
4358        $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20;
4359        $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2;
4360        $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : null;
4361        $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO;
4362        $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255;
4363        $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255;
4364        $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255;
4365        $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100;
4366        $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : true;
4367
4368        $RestoreShadow = $this->Shadow;
4369        if ($DisableShadowOnArea && $this->Shadow) {
4370            $this->Shadow = false;
4371        }
4372
4373        if ($BorderAlpha > 100) {
4374            $BorderAlpha = 100;
4375        }
4376
4377        $Data = $this->DataSet->getData();
4378        $XScale = $this->scaleGetXSettings();
4379
4380        if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
4381            $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] * 2) / $XScale[1];
4382            $XPos1 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value1;
4383            $XPos2 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value2;
4384            $YPos1 = $this->GraphAreaY1 + $Data["YMargin"];
4385            $YPos2 = $this->GraphAreaY2 - $Data["YMargin"];
4386
4387            if ($XPos1 < $this->GraphAreaX1 + $XScale[0]) {
4388                $XPos1 = $this->GraphAreaX1 + $XScale[0];
4389            }
4390            if ($XPos1 > $this->GraphAreaX2 - $XScale[0]) {
4391                $XPos1 = $this->GraphAreaX2 - $XScale[0];
4392            }
4393            if ($XPos2 < $this->GraphAreaX1 + $XScale[0]) {
4394                $XPos2 = $this->GraphAreaX1 + $XScale[0];
4395            }
4396            if ($XPos2 > $this->GraphAreaX2 - $XScale[0]) {
4397                $XPos2 = $this->GraphAreaX2 - $XScale[0];
4398            }
4399
4400            $this->drawFilledRectangle(
4401                $XPos1,
4402                $YPos1,
4403                $XPos2,
4404                $YPos2,
4405                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
4406            );
4407
4408            if ($Border) {
4409                $this->drawLine(
4410                    $XPos1,
4411                    $YPos1,
4412                    $XPos1,
4413                    $YPos2,
4414                    [
4415                        "R" => $BorderR,
4416                        "G" => $BorderG,
4417                        "B" => $BorderB,
4418                        "Alpha" => $BorderAlpha,
4419                        "Ticks" => $BorderTicks
4420                    ]
4421                );
4422                $this->drawLine(
4423                    $XPos2,
4424                    $YPos1,
4425                    $XPos2,
4426                    $YPos2,
4427                    [
4428                        "R" => $BorderR,
4429                        "G" => $BorderG,
4430                        "B" => $BorderB,
4431                        "Alpha" => $BorderAlpha,
4432                        "Ticks" => $BorderTicks
4433                    ]
4434                );
4435            }
4436
4437            if ($AreaName != null) {
4438                $XPos = ($XPos2 - $XPos1) / 2 + $XPos1;
4439                $YPos = ($YPos2 - $YPos1) / 2 + $YPos1;
4440
4441                if ($NameAngle == ZONE_NAME_ANGLE_AUTO) {
4442                    $TxtPos = $this->getTextBox(
4443                        $XPos,
4444                        $YPos,
4445                        $this->FontName,
4446                        $this->FontSize,
4447                        0,
4448                        $AreaName
4449                    );
4450                    $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"];
4451                    $NameAngle = 90;
4452                    if (abs($XPos2 - $XPos1) > $TxtWidth) {
4453                        $NameAngle = 0;
4454                    }
4455                }
4456                $this->Shadow = $RestoreShadow;
4457                $this->drawText(
4458                    $XPos,
4459                    $YPos,
4460                    $AreaName,
4461                    [
4462                        "R" => $NameR,
4463                        "G" => $NameG,
4464                        "B" => $NameB,
4465                        "Alpha" => $NameAlpha,
4466                        "Angle" => $NameAngle,
4467                        "Align" => TEXT_ALIGN_MIDDLEMIDDLE
4468                    ]
4469                );
4470                if ($DisableShadowOnArea) {
4471                    $this->Shadow = false;
4472                }
4473            }
4474
4475            $this->Shadow = $RestoreShadow;
4476            return ["X1" => $XPos1, "X2" => $XPos2];
4477        } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
4478            $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] * 2) / $XScale[1];
4479            $XPos1 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value1;
4480            $XPos2 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value2;
4481            $YPos1 = $this->GraphAreaX1 + $Data["YMargin"];
4482            $YPos2 = $this->GraphAreaX2 - $Data["YMargin"];
4483
4484            if ($XPos1 < $this->GraphAreaY1 + $XScale[0]) {
4485                $XPos1 = $this->GraphAreaY1 + $XScale[0];
4486            }
4487            if ($XPos1 > $this->GraphAreaY2 - $XScale[0]) {
4488                $XPos1 = $this->GraphAreaY2 - $XScale[0];
4489            }
4490            if ($XPos2 < $this->GraphAreaY1 + $XScale[0]) {
4491                $XPos2 = $this->GraphAreaY1 + $XScale[0];
4492            }
4493            if ($XPos2 > $this->GraphAreaY2 - $XScale[0]) {
4494                $XPos2 = $this->GraphAreaY2 - $XScale[0];
4495            }
4496
4497            $this->drawFilledRectangle(
4498                $YPos1,
4499                $XPos1,
4500                $YPos2,
4501                $XPos2,
4502                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
4503            );
4504
4505            if ($Border) {
4506                $this->drawLine(
4507                    $YPos1,
4508                    $XPos1,
4509                    $YPos2,
4510                    $XPos1,
4511                    [
4512                        "R" => $BorderR,
4513                        "G" => $BorderG,
4514                        "B" => $BorderB,
4515                        "Alpha" => $BorderAlpha,
4516                        "Ticks" => $BorderTicks
4517                    ]
4518                );
4519                $this->drawLine(
4520                    $YPos1,
4521                    $XPos2,
4522                    $YPos2,
4523                    $XPos2,
4524                    [
4525                        "R" => $BorderR,
4526                        "G" => $BorderG,
4527                        "B" => $BorderB,
4528                        "Alpha" => $BorderAlpha,
4529                        "Ticks" => $BorderTicks
4530                    ]
4531                );
4532            }
4533
4534            if ($AreaName != null) {
4535                $XPos = ($XPos2 - $XPos1) / 2 + $XPos1;
4536                $YPos = ($YPos2 - $YPos1) / 2 + $YPos1;
4537
4538                $this->Shadow = $RestoreShadow;
4539                $this->drawText(
4540                    $YPos,
4541                    $XPos,
4542                    $AreaName,
4543                    [
4544                        "R" => $NameR,
4545                        "G" => $NameG,
4546                        "B" => $NameB,
4547                        "Alpha" => $NameAlpha,
4548                        "Angle" => 0,
4549                        "Align" => TEXT_ALIGN_MIDDLEMIDDLE
4550                    ]
4551                );
4552                if ($DisableShadowOnArea) {
4553                    $this->Shadow = false;
4554                }
4555            }
4556
4557            $this->Shadow = $RestoreShadow;
4558            return ["X1" => $XPos1, "X2" => $XPos2];
4559        }
4560    }
4561
4562    /**
4563     * Draw an Y threshold with the computed scale
4564     * @param mixed $Value
4565     * @param array $Format
4566     * @return array|int
4567     */
4568    public function drawThreshold($Value, array $Format = [])
4569    {
4570        $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0;
4571        $R = isset($Format["R"]) ? $Format["R"] : 255;
4572        $G = isset($Format["G"]) ? $Format["G"] : 0;
4573        $B = isset($Format["B"]) ? $Format["B"] : 0;
4574        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50;
4575        $Weight = isset($Format["Weight"]) ? $Format["Weight"] : null;
4576        $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6;
4577        $Wide = isset($Format["Wide"]) ? $Format["Wide"] : false;
4578        $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5;
4579        $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : false;
4580        $Caption = isset($Format["Caption"]) ? $Format["Caption"] : null;
4581        $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP;
4582        $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 10;
4583        $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255;
4584        $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255;
4585        $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255;
4586        $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100;
4587        $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : true;
4588        $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : false;
4589        $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5;
4590        $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : true;
4591        $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3;
4592        $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
4593        $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
4594        $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
4595        $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20;
4596        $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
4597        $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255;
4598        $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255;
4599        $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255;
4600        $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100;
4601        $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : false;
4602
4603        if (is_array($Value)) {
4604            foreach ($Value as $Key => $ID) {
4605                $this->drawThreshold($ID, $Format);
4606            }
4607            return 0;
4608        }
4609
4610        $CaptionSettings = [
4611            "DrawBox" => $DrawBox,
4612            "DrawBoxBorder" => $DrawBoxBorder,
4613            "BorderOffset" => $BorderOffset,
4614            "BoxRounded" => $BoxRounded,
4615            "RoundedRadius" => $RoundedRadius,
4616            "BoxR" => $BoxR,
4617            "BoxG" => $BoxG,
4618            "BoxB" => $BoxB,
4619            "BoxAlpha" => $BoxAlpha,
4620            "BoxSurrounding" => $BoxSurrounding,
4621            "BoxBorderR" => $BoxBorderR,
4622            "BoxBorderG" => $BoxBorderG,
4623            "BoxBorderB" => $BoxBorderB,
4624            "BoxBorderAlpha" => $BoxBorderAlpha,
4625            "R" => $CaptionR,
4626            "G" => $CaptionG,
4627            "B" => $CaptionB,
4628            "Alpha" => $CaptionAlpha
4629        ];
4630
4631        $Data = $this->DataSet->getData();
4632        $AbscissaMargin = $this->getAbscissaMargin($Data);
4633
4634        if ($NoMargin) {
4635            $AbscissaMargin = 0;
4636        }
4637        if (!isset($Data["Axis"][$AxisID])) {
4638            return -1;
4639        }
4640        if ($Caption == null) {
4641            $Caption = $Value;
4642        }
4643
4644        if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
4645            $YPos = $this->scaleComputeY($Value, ["AxisID" => $AxisID]);
4646            if ($YPos >= $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]
4647                && $YPos <= $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]
4648            ) {
4649                $X1 = $this->GraphAreaX1 + $AbscissaMargin;
4650                $X2 = $this->GraphAreaX2 - $AbscissaMargin;
4651
4652                $this->drawLine(
4653                    $X1,
4654                    $YPos,
4655                    $X2,
4656                    $YPos,
4657                    [
4658                        "R" => $R,
4659                        "G" => $G,
4660                        "B" => $B,
4661                        "Alpha" => $Alpha,
4662                        "Ticks" => $Ticks,
4663                        "Weight" => $Weight
4664                    ]
4665                );
4666
4667                if ($Wide) {
4668                    $this->drawLine(
4669                        $X1,
4670                        $YPos - 1,
4671                        $X2,
4672                        $YPos - 1,
4673                        [
4674                            "R" => $R,
4675                            "G" => $G,
4676                            "B" => $B,
4677                            "Alpha" => $Alpha / $WideFactor,
4678                            "Ticks" => $Ticks
4679                        ]
4680                    );
4681                    $this->drawLine(
4682                        $X1,
4683                        $YPos + 1,
4684                        $X2,
4685                        $YPos + 1,
4686                        [
4687                            "R" => $R,
4688                            "G" => $G,
4689                            "B" => $B,
4690                            "Alpha" => $Alpha / $WideFactor,
4691                            "Ticks" => $Ticks
4692                        ]
4693                    );
4694                }
4695
4696                if ($WriteCaption) {
4697                    if ($CaptionAlign == CAPTION_LEFT_TOP) {
4698                        $X = $X1 + $CaptionOffset;
4699                        $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT;
4700                    } else {
4701                        $X = $X2 - $CaptionOffset;
4702                        $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT;
4703                    }
4704
4705                    $this->drawText($X, $YPos, $Caption, $CaptionSettings);
4706                }
4707            }
4708
4709            return ["Y" => $YPos];
4710        }
4711
4712        if ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
4713            $XPos = $this->scaleComputeY($Value, ["AxisID" => $AxisID]);
4714            if ($XPos >= $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]
4715                    && $XPos <= $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]
4716            ) {
4717                $Y1 = $this->GraphAreaY1 + $AbscissaMargin;
4718                $Y2 = $this->GraphAreaY2 - $AbscissaMargin;
4719
4720                $this->drawLine(
4721                    $XPos,
4722                    $Y1,
4723                    $XPos,
4724                    $Y2,
4725                    [
4726                        "R" => $R,
4727                        "G" => $G,
4728                        "B" => $B,
4729                        "Alpha" => $Alpha,
4730                        "Ticks" => $Ticks,
4731                        "Weight" => $Weight
4732                    ]
4733                );
4734
4735                if ($Wide) {
4736                    $this->drawLine(
4737                        $XPos - 1,
4738                        $Y1,
4739                        $XPos - 1,
4740                        $Y2,
4741                        [
4742                            "R" => $R,
4743                            "G" => $G,
4744                            "B" => $B,
4745                            "Alpha" => $Alpha / $WideFactor,
4746                            "Ticks" => $Ticks
4747                        ]
4748                    );
4749                    $this->drawLine(
4750                        $XPos + 1,
4751                        $Y1,
4752                        $XPos + 1,
4753                        $Y2,
4754                        [
4755                            "R" => $R,
4756                            "G" => $G,
4757                            "B" => $B,
4758                            "Alpha" => $Alpha / $WideFactor,
4759                            "Ticks" => $Ticks
4760                        ]
4761                    );
4762                }
4763
4764                if ($WriteCaption) {
4765                    if ($CaptionAlign == CAPTION_LEFT_TOP) {
4766                        $Y = $Y1 + $CaptionOffset;
4767                        $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE;
4768                    } else {
4769                        $Y = $Y2 - $CaptionOffset;
4770                        $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE;
4771                    }
4772
4773                    $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE;
4774                    $this->drawText($XPos, $Y, $Caption, $CaptionSettings);
4775                }
4776            }
4777
4778            return ["Y" => $XPos];
4779        }
4780    }
4781
4782    /**
4783     * Draw a threshold with the computed scale
4784     * @param mixed $Value1
4785     * @param mixed $Value2
4786     * @param array $Format
4787     * @return array|int|null
4788     */
4789    public function drawThresholdArea($Value1, $Value2, array $Format = [])
4790    {
4791        $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0;
4792        $R = isset($Format["R"]) ? $Format["R"] : 255;
4793        $G = isset($Format["G"]) ? $Format["G"] : 0;
4794        $B = isset($Format["B"]) ? $Format["B"] : 0;
4795        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20;
4796        $Border = isset($Format["Border"]) ? $Format["Border"] : true;
4797        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
4798        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
4799        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
4800        $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20;
4801        $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2;
4802        $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : null;
4803        $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO;
4804        $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255;
4805        $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255;
4806        $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255;
4807        $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100;
4808        $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : true;
4809        $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : false;
4810
4811        if ($Value1 > $Value2) {
4812            list($Value1, $Value2) = [$Value2, $Value1];
4813        }
4814
4815        $RestoreShadow = $this->Shadow;
4816        if ($DisableShadowOnArea && $this->Shadow) {
4817            $this->Shadow = false;
4818        }
4819
4820        if ($BorderAlpha > 100) {
4821            $BorderAlpha = 100;
4822        }
4823
4824        $Data = $this->DataSet->getData();
4825        $AbscissaMargin = $this->getAbscissaMargin($Data);
4826
4827        if ($NoMargin) {
4828            $AbscissaMargin = 0;
4829        }
4830        if (!isset($Data["Axis"][$AxisID])) {
4831            return -1;
4832        }
4833
4834        if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
4835            $XPos1 = $this->GraphAreaX1 + $AbscissaMargin;
4836            $XPos2 = $this->GraphAreaX2 - $AbscissaMargin;
4837            $YPos1 = $this->scaleComputeY($Value1, ["AxisID" => $AxisID]);
4838            $YPos2 = $this->scaleComputeY($Value2, ["AxisID" => $AxisID]);
4839
4840            if ($YPos1 < $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]) {
4841                $YPos1 = $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"];
4842            }
4843            if ($YPos1 > $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]) {
4844                $YPos1 = $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"];
4845            }
4846            if ($YPos2 < $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"]) {
4847                $YPos2 = $this->GraphAreaY1 + $Data["Axis"][$AxisID]["Margin"];
4848            }
4849            if ($YPos2 > $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"]) {
4850                $YPos2 = $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"];
4851            }
4852
4853            $this->drawFilledRectangle(
4854                $XPos1,
4855                $YPos1,
4856                $XPos2,
4857                $YPos2,
4858                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
4859            );
4860            if ($Border) {
4861                $this->drawLine(
4862                    $XPos1,
4863                    $YPos1,
4864                    $XPos2,
4865                    $YPos1,
4866                    [
4867                        "R" => $BorderR,
4868                        "G" => $BorderG,
4869                        "B" => $BorderB,
4870                        "Alpha" => $BorderAlpha,
4871                        "Ticks" => $BorderTicks
4872                    ]
4873                );
4874                $this->drawLine(
4875                    $XPos1,
4876                    $YPos2,
4877                    $XPos2,
4878                    $YPos2,
4879                    [
4880                        "R" => $BorderR,
4881                        "G" => $BorderG,
4882                        "B" => $BorderB,
4883                        "Alpha" => $BorderAlpha,
4884                        "Ticks" => $BorderTicks
4885                    ]
4886                );
4887            }
4888
4889            if ($AreaName != null) {
4890                $XPos = ($XPos2 - $XPos1) / 2 + $XPos1;
4891                $YPos = ($YPos2 - $YPos1) / 2 + $YPos1;
4892                $this->Shadow = $RestoreShadow;
4893                $this->drawText(
4894                    $XPos,
4895                    $YPos,
4896                    $AreaName,
4897                    [
4898                        "R" => $NameR,
4899                        "G" => $NameG,
4900                        "B" => $NameB,
4901                        "Alpha" => $NameAlpha,
4902                        "Angle" => 0,
4903                        "Align" => TEXT_ALIGN_MIDDLEMIDDLE
4904                    ]
4905                );
4906                if ($DisableShadowOnArea) {
4907                    $this->Shadow = false;
4908                }
4909            }
4910
4911            $this->Shadow = $RestoreShadow;
4912            return ["Y1" => $YPos1, "Y2" => $YPos2];
4913        } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
4914            $YPos1 = $this->GraphAreaY1 + $AbscissaMargin;
4915            $YPos2 = $this->GraphAreaY2 - $AbscissaMargin;
4916            $XPos1 = $this->scaleComputeY($Value1, ["AxisID" => $AxisID]);
4917            $XPos2 = $this->scaleComputeY($Value2, ["AxisID" => $AxisID]);
4918
4919            if ($XPos1 < $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]) {
4920                $XPos1 = $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"];
4921            }
4922            if ($XPos1 > $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]) {
4923                $XPos1 = $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"];
4924            }
4925            if ($XPos2 < $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"]) {
4926                $XPos2 = $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"];
4927            }
4928            if ($XPos2 > $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"]) {
4929                $XPos2 = $this->GraphAreaX2 - $Data["Axis"][$AxisID]["Margin"];
4930            }
4931
4932            $this->drawFilledRectangle(
4933                $XPos1,
4934                $YPos1,
4935                $XPos2,
4936                $YPos2,
4937                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
4938            );
4939            if ($Border) {
4940                $this->drawLine(
4941                    $XPos1,
4942                    $YPos1,
4943                    $XPos1,
4944                    $YPos2,
4945                    [
4946                        "R" => $BorderR,
4947                        "G" => $BorderG,
4948                        "B" => $BorderB,
4949                        "Alpha" => $BorderAlpha,
4950                        "Ticks" => $BorderTicks
4951                    ]
4952                );
4953                $this->drawLine(
4954                    $XPos2,
4955                    $YPos1,
4956                    $XPos2,
4957                    $YPos2,
4958                    [
4959                        "R" => $BorderR,
4960                        "G" => $BorderG,
4961                        "B" => $BorderB,
4962                        "Alpha" => $BorderAlpha,
4963                        "Ticks" => $BorderTicks
4964                    ]
4965                );
4966            }
4967
4968            if ($AreaName != null) {
4969                $XPos = ($YPos2 - $YPos1) / 2 + $YPos1;
4970                $YPos = ($XPos2 - $XPos1) / 2 + $XPos1;
4971
4972                if ($NameAngle == ZONE_NAME_ANGLE_AUTO) {
4973                    $TxtPos = $this->getTextBox(
4974                        $XPos,
4975                        $YPos,
4976                        $this->FontName,
4977                        $this->FontSize,
4978                        0,
4979                        $AreaName
4980                    );
4981                    $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"];
4982                    $NameAngle = 90;
4983                    if (abs($XPos2 - $XPos1) > $TxtWidth) {
4984                        $NameAngle = 0;
4985                    }
4986                }
4987                $this->Shadow = $RestoreShadow;
4988                $this->drawText(
4989                    $YPos,
4990                    $XPos,
4991                    $AreaName,
4992                    [
4993                        "R" => $NameR,
4994                        "G" => $NameG,
4995                        "B" => $NameB,
4996                        "Alpha" => $NameAlpha,
4997                        "Angle" => $NameAngle,
4998                        "Align" => TEXT_ALIGN_MIDDLEMIDDLE
4999                    ]
5000                );
5001                if ($DisableShadowOnArea) {
5002                    $this->Shadow = false;
5003                }
5004            }
5005
5006            $this->Shadow = $RestoreShadow;
5007            return ["Y1" => $XPos1, "Y2" => $XPos2];
5008        }
5009    }
5010
5011    /**
5012     * Draw a plot chart
5013     * @param array $Format
5014     */
5015    public function drawPlotChart(array $Format = [])
5016    {
5017        $PlotSize = isset($Format["PlotSize"]) ? $Format["PlotSize"] : null;
5018        $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : false;
5019        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 50;
5020        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 50;
5021        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 50;
5022        $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30;
5023        $BorderSize = isset($Format["BorderSize"]) ? $Format["BorderSize"] : 2;
5024        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
5025        $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false;
5026        $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4;
5027        $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
5028        $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
5029        $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
5030        $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
5031        $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
5032
5033        $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
5034
5035        $Data = $this->DataSet->getData();
5036        list($XMargin, $XDivs) = $this->scaleGetXSettings();
5037
5038        foreach ($Data["Series"] as $SerieName => $Serie) {
5039            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
5040                if (isset($Serie["Weight"])) {
5041                    $SerieWeight = $Serie["Weight"] + 2;
5042                } else {
5043                    $SerieWeight = 2;
5044                }
5045                if ($PlotSize != null) {
5046                    $SerieWeight = $PlotSize;
5047                }
5048
5049                $R = $Serie["Color"]["R"];
5050                $G = $Serie["Color"]["G"];
5051                $B = $Serie["Color"]["B"];
5052                $Alpha = (int) $Serie["Color"]["Alpha"];
5053                $Ticks = $Serie["Ticks"];
5054                if ($Surrounding != null) {
5055                    $BorderR = $R + $Surrounding;
5056                    $BorderG = $G + $Surrounding;
5057                    $BorderB = $B + $Surrounding;
5058                }
5059                if (isset($Serie["Picture"])) {
5060                    $Picture = $Serie["Picture"];
5061                    list($PicWidth, $PicHeight, $PicType) = $this->getPicInfo($Picture);
5062                } else {
5063                    $Picture = null;
5064                    $PicOffset = 0;
5065                }
5066
5067                if ($DisplayColor == DISPLAY_AUTO) {
5068                    $DisplayR = $R;
5069                    $DisplayG = $G;
5070                    $DisplayB = $B;
5071                }
5072
5073                $AxisID = $Serie["Axis"];
5074                $Shape = $Serie["Shape"];
5075                $Mode = $Data["Axis"][$AxisID]["Display"];
5076                $Format = $Data["Axis"][$AxisID]["Format"];
5077                $Unit = $Data["Axis"][$AxisID]["Unit"];
5078
5079                if (isset($Serie["Description"])) {
5080                    $SerieDescription = $Serie["Description"];
5081                } else {
5082                    $SerieDescription = $SerieName;
5083                }
5084
5085                $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
5086
5087                $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
5088
5089                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
5090                    if ($XDivs == 0) {
5091                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
5092                    } else {
5093                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
5094                    }
5095                    if ($Picture != null) {
5096                        $PicOffset = $PicHeight / 2;
5097                        $SerieWeight = 0;
5098                    }
5099                    $X = $this->GraphAreaX1 + $XMargin;
5100
5101                    if (!is_array($PosArray)) {
5102                        $Value = $PosArray;
5103                        $PosArray = [];
5104                        $PosArray[0] = $Value;
5105                    }
5106                    foreach ($PosArray as $Key => $Y) {
5107                        if ($DisplayValues) {
5108                            $this->drawText(
5109                                $X,
5110                                $Y - $DisplayOffset - $SerieWeight - $BorderSize - $PicOffset,
5111                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit),
5112                                [
5113                                    "R" => $DisplayR,
5114                                    "G" => $DisplayG,
5115                                    "B" => $DisplayB,
5116                                    "Align" => TEXT_ALIGN_BOTTOMMIDDLE
5117                                ]
5118                            );
5119                        }
5120                        if ($Y != VOID) {
5121                            if ($RecordImageMap) {
5122                                $this->addToImageMap(
5123                                    "CIRCLE",
5124                                    floor($X) . "," . floor($Y) . "," . $SerieWeight,
5125                                    $this->toHTMLColor($R, $G, $B),
5126                                    $SerieDescription,
5127                                    $this->scaleFormat(
5128                                        $Serie["Data"][$Key],
5129                                        $Mode,
5130                                        $Format,
5131                                        $Unit
5132                                    )
5133                                );
5134                            }
5135
5136                            if ($Picture != null) {
5137                                $this->drawFromPicture(
5138                                    $PicType,
5139                                    $Picture,
5140                                    $X - $PicWidth / 2,
5141                                    $Y - $PicHeight / 2
5142                                );
5143                            } else {
5144                                $this->drawShape(
5145                                    $X,
5146                                    $Y,
5147                                    $Shape,
5148                                    $SerieWeight,
5149                                    $PlotBorder,
5150                                    $BorderSize,
5151                                    $R,
5152                                    $G,
5153                                    $B,
5154                                    $Alpha,
5155                                    $BorderR,
5156                                    $BorderG,
5157                                    $BorderB,
5158                                    $BorderAlpha
5159                                );
5160                            }
5161                        }
5162                        $X = $X + $XStep;
5163                    }
5164                } else {
5165                    if ($XDivs == 0) {
5166                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
5167                    } else {
5168                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
5169                    }
5170                    if ($Picture != null) {
5171                        $PicOffset = $PicWidth / 2;
5172                        $SerieWeight = 0;
5173                    }
5174                    $Y = $this->GraphAreaY1 + $XMargin;
5175
5176                    if (!is_array($PosArray)) {
5177                        $Value = $PosArray;
5178                        $PosArray = [];
5179                        $PosArray[0] = $Value;
5180                    }
5181                    foreach ($PosArray as $Key => $X) {
5182                        if ($DisplayValues) {
5183                            $this->drawText(
5184                                $X + $DisplayOffset + $SerieWeight + $BorderSize + $PicOffset,
5185                                $Y,
5186                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit),
5187                                [
5188                                    "Angle" => 270,
5189                                    "R" => $DisplayR,
5190                                    "G" => $DisplayG,
5191                                    "B" => $DisplayB,
5192                                    "Align" => TEXT_ALIGN_BOTTOMMIDDLE
5193                                ]
5194                            );
5195                        }
5196                        if ($X != VOID) {
5197                            if ($RecordImageMap) {
5198                                $this->addToImageMap(
5199                                    "CIRCLE",
5200                                    floor($X) . "," . floor($Y) . "," . $SerieWeight,
5201                                    $this->toHTMLColor($R, $G, $B),
5202                                    $SerieDescription,
5203                                    $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
5204                                );
5205                            }
5206
5207                            if ($Picture != null) {
5208                                $this->drawFromPicture(
5209                                    $PicType,
5210                                    $Picture,
5211                                    $X - $PicWidth / 2,
5212                                    $Y - $PicHeight / 2
5213                                );
5214                            } else {
5215                                $this->drawShape(
5216                                    $X,
5217                                    $Y,
5218                                    $Shape,
5219                                    $SerieWeight,
5220                                    $PlotBorder,
5221                                    $BorderSize,
5222                                    $R,
5223                                    $G,
5224                                    $B,
5225                                    $Alpha,
5226                                    $BorderR,
5227                                    $BorderG,
5228                                    $BorderB,
5229                                    $BorderAlpha
5230                                );
5231                            }
5232                        }
5233                        $Y = $Y + $YStep;
5234                    }
5235                }
5236            }
5237        }
5238    }
5239
5240    /**
5241     * Draw a spline chart
5242     * @param array $Format
5243     */
5244    public function drawSplineChart(array $Format = [])
5245    {
5246        $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : true;
5247        $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4;
5248        $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : null; // 234
5249        $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : null; // 55
5250        $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : null; // 26
5251        $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false;
5252        $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
5253        $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
5254        $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
5255        $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
5256        $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
5257        $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
5258        $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5;
5259
5260        $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
5261
5262        $Data = $this->DataSet->getData();
5263        list($XMargin, $XDivs) = $this->scaleGetXSettings();
5264        foreach ($Data["Series"] as $SerieName => $Serie) {
5265            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
5266                $R = $Serie["Color"]["R"];
5267                $G = $Serie["Color"]["G"];
5268                $B = $Serie["Color"]["B"];
5269                $Alpha = $Serie["Color"]["Alpha"];
5270                $Ticks = $Serie["Ticks"];
5271                $Weight = $Serie["Weight"];
5272
5273                if ($BreakR == null) {
5274                    $BreakSettings = [
5275                        "R" => $R,
5276                        "G" => $G,
5277                        "B" => $B,
5278                        "Alpha" => $Alpha,
5279                        "Ticks" => $VoidTicks
5280                    ];
5281                } else {
5282                    $BreakSettings = [
5283                        "R" => $BreakR,
5284                        "G" => $BreakG,
5285                        "B" => $BreakB,
5286                        "Alpha" => $Alpha,
5287                        "Ticks" => $VoidTicks,
5288                        "Weight" => $Weight
5289                    ];
5290                }
5291                if ($DisplayColor == DISPLAY_AUTO) {
5292                    $DisplayR = $R;
5293                    $DisplayG = $G;
5294                    $DisplayB = $B;
5295                }
5296
5297                $AxisID = $Serie["Axis"];
5298                $Mode = $Data["Axis"][$AxisID]["Display"];
5299                $Format = $Data["Axis"][$AxisID]["Format"];
5300                $Unit = $Data["Axis"][$AxisID]["Unit"];
5301
5302                if (isset($Serie["Description"])) {
5303                    $SerieDescription = $Serie["Description"];
5304                } else {
5305                    $SerieDescription = $SerieName;
5306                }
5307
5308                $PosArray = $this->scaleComputeY(
5309                    $Serie["Data"],
5310                    ["AxisID" => $Serie["Axis"]]
5311                );
5312
5313                $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
5314
5315                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
5316                    if ($XDivs == 0) {
5317                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
5318                    } else {
5319                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
5320                    }
5321                    $X = $this->GraphAreaX1 + $XMargin;
5322                    $WayPoints = [];
5323                    $Force = $XStep / 5;
5324
5325                    if (!is_array($PosArray)) {
5326                        $Value = $PosArray;
5327                        $PosArray = [];
5328                        $PosArray[0] = $Value;
5329                    }
5330                    $LastGoodY = null;
5331                    $LastGoodX = null;
5332                    $LastX = 1;
5333                    $LastY = 1;
5334                    foreach ($PosArray as $Key => $Y) {
5335                        if ($DisplayValues) {
5336                            $this->drawText(
5337                                $X,
5338                                $Y - $DisplayOffset,
5339                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit),
5340                                [
5341                                    "R" => $DisplayR,
5342                                    "G" => $DisplayG,
5343                                    "B" => $DisplayB,
5344                                    "Align" => TEXT_ALIGN_BOTTOMMIDDLE
5345                                ]
5346                            );
5347                        }
5348                        if ($RecordImageMap && $Y != VOID) {
5349                            $this->addToImageMap(
5350                                "CIRCLE",
5351                                floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize,
5352                                $this->toHTMLColor($R, $G, $B),
5353                                $SerieDescription,
5354                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
5355                            );
5356                        }
5357
5358                        if ($Y == VOID && $LastY != null) {
5359                            $this->drawSpline(
5360                                $WayPoints,
5361                                [
5362                                    "Force" => $Force,
5363                                    "R" => $R,
5364                                    "G" => $G,
5365                                    "B" => $B,
5366                                    "Alpha" => $Alpha,
5367                                    "Ticks" => $Ticks,
5368                                    "Weight" => $Weight
5369                                ]
5370                            );
5371                            $WayPoints = [];
5372                        }
5373
5374                        if ($Y != VOID && $LastY == null && $LastGoodY != null && !$BreakVoid) {
5375                            $this->drawLine($LastGoodX, $LastGoodY, $X, $Y, $BreakSettings);
5376                        }
5377
5378                        if ($Y != VOID) {
5379                            $WayPoints[] = [$X, $Y];
5380                        }
5381                        if ($Y != VOID) {
5382                            $LastGoodY = $Y;
5383                            $LastGoodX = $X;
5384                        }
5385                        if ($Y == VOID) {
5386                            $Y = null;
5387                        }
5388
5389                        $LastX = $X;
5390                        $LastY = $Y;
5391                        $X = $X + $XStep;
5392                    }
5393                    $this->drawSpline(
5394                        $WayPoints,
5395                        [
5396                            "Force" => $Force,
5397                            "R" => $R,
5398                            "G" => $G,
5399                            "B" => $B,
5400                            "Alpha" => $Alpha,
5401                            "Ticks" => $Ticks,
5402                            "Weight" => $Weight
5403                        ]
5404                    );
5405                } else {
5406                    if ($XDivs == 0) {
5407                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
5408                    } else {
5409                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
5410                    }
5411                    $Y = $this->GraphAreaY1 + $XMargin;
5412                    $WayPoints = [];
5413                    $Force = $YStep / 5;
5414
5415                    if (!is_array($PosArray)) {
5416                        $Value = $PosArray;
5417                        $PosArray = [];
5418                        $PosArray[0] = $Value;
5419                    }
5420                    $LastGoodY = null;
5421                    $LastGoodX = null;
5422                    $LastX = 1;
5423                    $LastY = 1;
5424                    foreach ($PosArray as $Key => $X) {
5425                        if ($DisplayValues) {
5426                            $this->drawText(
5427                                $X + $DisplayOffset,
5428                                $Y,
5429                                $this->scaleFormat(
5430                                    $Serie["Data"][$Key],
5431                                    $Mode,
5432                                    $Format,
5433                                    $Unit
5434                                ),
5435                                [
5436                                    "Angle" => 270,
5437                                    "R" => $DisplayR,
5438                                    "G" => $DisplayG,
5439                                    "B" => $DisplayB,
5440                                    "Align" => TEXT_ALIGN_BOTTOMMIDDLE
5441                                ]
5442                            );
5443                        }
5444                        if ($RecordImageMap && $X != VOID) {
5445                            $this->addToImageMap(
5446                                "CIRCLE",
5447                                floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize,
5448                                $this->toHTMLColor($R, $G, $B),
5449                                $SerieDescription,
5450                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
5451                            );
5452                        }
5453
5454                        if ($X == VOID && $LastX != null) {
5455                            $this->drawSpline(
5456                                $WayPoints,
5457                                [
5458                                    "Force" => $Force,
5459                                    "R" => $R,
5460                                    "G" => $G,
5461                                    "B" => $B,
5462                                    "Alpha" => $Alpha,
5463                                    "Ticks" => $Ticks,
5464                                    "Weight" => $Weight
5465                                ]
5466                            );
5467                            $WayPoints = [];
5468                        }
5469
5470                        if ($X != VOID && $LastX == null && $LastGoodX != null && !$BreakVoid) {
5471                            $this->drawLine($LastGoodX, $LastGoodY, $X, $Y, $BreakSettings);
5472                        }
5473
5474                        if ($X != VOID) {
5475                            $WayPoints[] = [$X, $Y];
5476                        }
5477                        if ($X != VOID) {
5478                            $LastGoodX = $X;
5479                            $LastGoodY = $Y;
5480                        }
5481                        if ($X == VOID) {
5482                            $X = null;
5483                        }
5484
5485                        $LastX = $X;
5486                        $LastY = $Y;
5487                        $Y = $Y + $YStep;
5488                    }
5489                    $this->drawSpline(
5490                        $WayPoints,
5491                        [
5492                            "Force" => $Force,
5493                            "R" => $R,
5494                            "G" => $G,
5495                            "B" => $B,
5496                            "Alpha" => $Alpha,
5497                            "Ticks" => $Ticks,
5498                            "Weight" => $Weight
5499                        ]
5500                    );
5501                }
5502            }
5503        }
5504    }
5505
5506    /**
5507     * Draw a filled spline chart
5508     * @param array $Format
5509     */
5510    public function drawFilledSplineChart(array $Format = [])
5511    {
5512        $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false;
5513        $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
5514        $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
5515        $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
5516        $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
5517        $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
5518        $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : true;
5519        $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null;
5520
5521        $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
5522
5523        $Data = $this->DataSet->getData();
5524        list($XMargin, $XDivs) = $this->scaleGetXSettings();
5525        foreach ($Data["Series"] as $SerieName => $Serie) {
5526            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
5527                $R = $Serie["Color"]["R"];
5528                $G = $Serie["Color"]["G"];
5529                $B = $Serie["Color"]["B"];
5530                $Alpha = $Serie["Color"]["Alpha"];
5531                $Ticks = $Serie["Ticks"];
5532                if ($DisplayColor == DISPLAY_AUTO) {
5533                    $DisplayR = $R;
5534                    $DisplayG = $G;
5535                    $DisplayB = $B;
5536                }
5537
5538                $AxisID = $Serie["Axis"];
5539                $Mode = $Data["Axis"][$AxisID]["Display"];
5540                $Format = $Data["Axis"][$AxisID]["Format"];
5541                $Unit = $Data["Axis"][$AxisID]["Unit"];
5542
5543                $PosArray = $this->scaleComputeY(
5544                    $Serie["Data"],
5545                    ["AxisID" => $Serie["Axis"]]
5546                );
5547                if ($AroundZero) {
5548                    $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
5549                }
5550
5551                if ($Threshold != null) {
5552                    foreach ($Threshold as $Key => $Params) {
5553                        $Threshold[$Key]["MinX"] = $this->scaleComputeY(
5554                            $Params["Min"],
5555                            ["AxisID" => $Serie["Axis"]]
5556                        );
5557                        $Threshold[$Key]["MaxX"] = $this->scaleComputeY(
5558                            $Params["Max"],
5559                            ["AxisID" => $Serie["Axis"]]
5560                        );
5561                    }
5562                }
5563
5564                $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
5565
5566                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
5567                    if ($XDivs == 0) {
5568                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
5569                    } else {
5570                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
5571                    }
5572                    $X = $this->GraphAreaX1 + $XMargin;
5573                    $WayPoints = [];
5574                    $Force = $XStep / 5;
5575
5576                    if (!$AroundZero) {
5577                        $YZero = $this->GraphAreaY2 - 1;
5578                    }
5579                    if ($YZero > $this->GraphAreaY2 - 1) {
5580                        $YZero = $this->GraphAreaY2 - 1;
5581                    }
5582                    if ($YZero < $this->GraphAreaY1 + 1) {
5583                        $YZero = $this->GraphAreaY1 + 1;
5584                    }
5585
5586                    if (!is_array($PosArray)) {
5587                        $Value = $PosArray;
5588                        $PosArray = [];
5589                        $PosArray[0] = $Value;
5590                    }
5591                    foreach ($PosArray as $Key => $Y) {
5592                        if ($DisplayValues) {
5593                            $this->drawText(
5594                                $X,
5595                                $Y - $DisplayOffset,
5596                                $this->scaleFormat(
5597                                    $Serie["Data"][$Key],
5598                                    $Mode,
5599                                    $Format,
5600                                    $Unit
5601                                ),
5602                                [
5603                                    "R" => $DisplayR,
5604                                    "G" => $DisplayG,
5605                                    "B" => $DisplayB,
5606                                    "Align" => TEXT_ALIGN_BOTTOMMIDDLE
5607                                ]
5608                            );
5609                        }
5610                        if ($Y == VOID) {
5611                            $Area = $this->drawSpline(
5612                                $WayPoints,
5613                                ["Force" => $Force, "PathOnly" => true]
5614                            );
5615
5616                            if (count($Area)) {
5617                                foreach ($Area as $key => $Points) {
5618                                    $Corners = [];
5619                                    $Corners[] = $Area[$key][0]["X"];
5620                                    $Corners[] = $YZero;
5621                                    foreach ($Points as $subKey => $Point) {
5622                                        if ($subKey == count($Points) - 1) {
5623                                            $Corners[] = $Point["X"] - 1;
5624                                        } else {
5625                                            $Corners[] = $Point["X"];
5626                                        }
5627                                        $Corners[] = $Point["Y"] + 1;
5628                                    }
5629                                    $Corners[] = $Points[$subKey]["X"] - 1;
5630                                    $Corners[] = $YZero;
5631
5632                                    $this->drawPolygonChart(
5633                                        $Corners,
5634                                        [
5635                                            "R" => $R,
5636                                            "G" => $G,
5637                                            "B" => $B,
5638                                            "Alpha" => $Alpha / 2,
5639                                            "NoBorder" => true,
5640                                            "Threshold" => $Threshold
5641                                        ]
5642                                    );
5643                                }
5644                                $this->drawSpline(
5645                                    $WayPoints,
5646                                    [
5647                                        "Force" => $Force,
5648                                        "R" => $R,
5649                                        "G" => $G,
5650                                        "B" => $B,
5651                                        "Alpha" => $Alpha,
5652                                        "Ticks" => $Ticks
5653                                    ]
5654                                );
5655                            }
5656
5657                            $WayPoints = [];
5658                        } else {
5659                            $WayPoints[] = [$X, $Y - .5]; /* -.5 for AA visual fix */
5660                        }
5661                        $X = $X + $XStep;
5662                    }
5663                    $Area = $this->drawSpline($WayPoints, ["Force" => $Force, "PathOnly" => true]);
5664
5665                    if (count($Area)) {
5666                        foreach ($Area as $key => $Points) {
5667                            $Corners = [];
5668                            $Corners[] = $Area[$key][0]["X"];
5669                            $Corners[] = $YZero;
5670                            foreach ($Points as $subKey => $Point) {
5671                                if ($subKey == count($Points) - 1) {
5672                                    $Corners[] = $Point["X"] - 1;
5673                                } else {
5674                                    $Corners[] = $Point["X"];
5675                                }
5676                                $Corners[] = $Point["Y"] + 1;
5677                            }
5678                            $Corners[] = $Points[$subKey]["X"] - 1;
5679                            $Corners[] = $YZero;
5680
5681                            $this->drawPolygonChart(
5682                                $Corners,
5683                                [
5684                                    "R" => $R,
5685                                    "G" => $G,
5686                                    "B" => $B,
5687                                    "Alpha" => $Alpha / 2,
5688                                    "NoBorder" => true,
5689                                    "Threshold" => $Threshold
5690                                ]
5691                            );
5692                        }
5693                        $this->drawSpline(
5694                            $WayPoints,
5695                            [
5696                                "Force" => $Force,
5697                                "R" => $R,
5698                                "G" => $G,
5699                                "B" => $B,
5700                                "Alpha" => $Alpha,
5701                                "Ticks" => $Ticks
5702                            ]
5703                        );
5704                    }
5705                } else {
5706                    if ($XDivs == 0) {
5707                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
5708                    } else {
5709                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
5710                    }
5711                    $Y = $this->GraphAreaY1 + $XMargin;
5712                    $WayPoints = [];
5713                    $Force = $YStep / 5;
5714
5715                    if (!$AroundZero) {
5716                        $YZero = $this->GraphAreaX1 + 1;
5717                    }
5718                    if ($YZero > $this->GraphAreaX2 - 1) {
5719                        $YZero = $this->GraphAreaX2 - 1;
5720                    }
5721                    if ($YZero < $this->GraphAreaX1 + 1) {
5722                        $YZero = $this->GraphAreaX1 + 1;
5723                    }
5724
5725                    if (!is_array($PosArray)) {
5726                        $Value = $PosArray;
5727                        $PosArray = [];
5728                        $PosArray[0] = $Value;
5729                    }
5730                    foreach ($PosArray as $Key => $X) {
5731                        if ($DisplayValues) {
5732                            $this->drawText(
5733                                $X + $DisplayOffset,
5734                                $Y,
5735                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit),
5736                                [
5737                                    "Angle" => 270,
5738                                    "R" => $DisplayR,
5739                                    "G" => $DisplayG,
5740                                    "B" => $DisplayB,
5741                                    "Align" => TEXT_ALIGN_BOTTOMMIDDLE
5742                                ]
5743                            );
5744                        }
5745                        if ($X == VOID) {
5746                            $Area = $this->drawSpline(
5747                                $WayPoints,
5748                                ["Force" => $Force, "PathOnly" => true]
5749                            );
5750
5751                            if (count($Area)) {
5752                                foreach ($Area as $key => $Points) {
5753                                    $Corners = [];
5754                                    $Corners[] = $YZero;
5755                                    $Corners[] = $Area[$key][0]["Y"];
5756                                    foreach ($Points as $subKey => $Point) {
5757                                        if ($subKey == count($Points) - 1) {
5758                                            $Corners[] = $Point["X"] - 1;
5759                                        } else {
5760                                            $Corners[] = $Point["X"];
5761                                        }
5762                                        $Corners[] = $Point["Y"];
5763                                    }
5764                                    $Corners[] = $YZero;
5765                                    $Corners[] = $Points[$subKey]["Y"] - 1;
5766
5767                                    $this->drawPolygonChart(
5768                                        $Corners,
5769                                        [
5770                                            "R" => $R,
5771                                            "G" => $G,
5772                                            "B" => $B,
5773                                            "Alpha" => $Alpha / 2,
5774                                            "NoBorder" => true,
5775                                            "Threshold" => $Threshold
5776                                        ]
5777                                    );
5778                                }
5779                                $this->drawSpline(
5780                                    $WayPoints,
5781                                    [
5782                                        "Force" => $Force,
5783                                        "R" => $R,
5784                                        "G" => $G,
5785                                        "B" => $B,
5786                                        "Alpha" => $Alpha,
5787                                        "Ticks" => $Ticks
5788                                    ]
5789                                );
5790                            }
5791
5792                            $WayPoints = [];
5793                        } else {
5794                            $WayPoints[] = [$X, $Y];
5795                        }
5796                        $Y = $Y + $YStep;
5797                    }
5798                    $Area = $this->drawSpline(
5799                        $WayPoints,
5800                        ["Force" => $Force, "PathOnly" => true]
5801                    );
5802
5803                    if (count($Area)) {
5804                        foreach ($Area as $key => $Points) {
5805                            $Corners = [];
5806                            $Corners[] = $YZero;
5807                            $Corners[] = $Area[$key][0]["Y"];
5808                            foreach ($Points as $subKey => $Point) {
5809                                if ($subKey == count($Points) - 1) {
5810                                    $Corners[] = $Point["X"] - 1;
5811                                } else {
5812                                    $Corners[] = $Point["X"];
5813                                }
5814                                $Corners[] = $Point["Y"];
5815                            }
5816                            $Corners[] = $YZero;
5817                            $Corners[] = $Points[$subKey]["Y"] - 1;
5818
5819                            $this->drawPolygonChart(
5820                                $Corners,
5821                                [
5822                                    "R" => $R,
5823                                    "G" => $G,
5824                                    "B" => $B,
5825                                    "Alpha" => $Alpha / 2,
5826                                    "NoBorder" => true,
5827                                    "Threshold" => $Threshold
5828                                ]
5829                            );
5830                        }
5831                        $this->drawSpline(
5832                            $WayPoints,
5833                            [
5834                                "Force" => $Force,
5835                                "R" => $R,
5836                                "G" => $G,
5837                                "B" => $B,
5838                                "Alpha" => $Alpha,
5839                                "Ticks" => $Ticks
5840                            ]
5841                        );
5842                    }
5843                }
5844            }
5845        }
5846    }
5847
5848    /**
5849     * Draw a line chart
5850     * @param array $Format
5851     */
5852    public function drawLineChart(array $Format = [])
5853    {
5854        $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : true;
5855        $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4;
5856        $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : null;
5857        $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : null;
5858        $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : null;
5859        $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false;
5860        $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
5861        $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
5862        $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
5863        $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
5864        $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
5865        $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
5866        $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5;
5867        $ForceColor = isset($Format["ForceColor"]) ? $Format["ForceColor"] : false;
5868        $ForceR = isset($Format["ForceR"]) ? $Format["ForceR"] : 0;
5869        $ForceG = isset($Format["ForceG"]) ? $Format["ForceG"] : 0;
5870        $ForceB = isset($Format["ForceB"]) ? $Format["ForceB"] : 0;
5871        $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : 100;
5872
5873        $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
5874
5875        $Data = $this->DataSet->getData();
5876        list($XMargin, $XDivs) = $this->scaleGetXSettings();
5877        foreach ($Data["Series"] as $SerieName => $Serie) {
5878            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
5879                $R = $Serie["Color"]["R"];
5880                $G = $Serie["Color"]["G"];
5881                $B = $Serie["Color"]["B"];
5882                $Alpha = $Serie["Color"]["Alpha"];
5883                $Ticks = $Serie["Ticks"];
5884                $Weight = $Serie["Weight"];
5885
5886                if ($ForceColor) {
5887                    $R = $ForceR;
5888                    $G = $ForceG;
5889                    $B = $ForceB;
5890                    $Alpha = $ForceAlpha;
5891                }
5892
5893                if ($BreakR == null) {
5894                    $BreakSettings = [
5895                        "R" => $R,
5896                        "G" => $G,
5897                        "B" => $B,
5898                        "Alpha" => $Alpha,
5899                        "Ticks" => $VoidTicks,
5900                        "Weight" => $Weight
5901                    ];
5902                } else {
5903                    $BreakSettings = [
5904                        "R" => $BreakR,
5905                        "G" => $BreakG,
5906                        "B" => $BreakB,
5907                        "Alpha" => $Alpha,
5908                        "Ticks" => $VoidTicks,
5909                        "Weight" => $Weight
5910                    ];
5911                }
5912                if ($DisplayColor == DISPLAY_AUTO) {
5913                    $DisplayR = $R;
5914                    $DisplayG = $G;
5915                    $DisplayB = $B;
5916                }
5917
5918                $AxisID = $Serie["Axis"];
5919                $Mode = $Data["Axis"][$AxisID]["Display"];
5920                $Format = $Data["Axis"][$AxisID]["Format"];
5921                $Unit = $Data["Axis"][$AxisID]["Unit"];
5922
5923                if (isset($Serie["Description"])) {
5924                    $SerieDescription = $Serie["Description"];
5925                } else {
5926                    $SerieDescription = $SerieName;
5927                }
5928
5929                $PosArray = $this->scaleComputeY(
5930                    $Serie["Data"],
5931                    ["AxisID" => $Serie["Axis"]]
5932                );
5933
5934                $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
5935
5936                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
5937                    if ($XDivs == 0) {
5938                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
5939                    } else {
5940                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
5941                    }
5942                    $X = $this->GraphAreaX1 + $XMargin;
5943                    $LastX = null;
5944                    $LastY = null;
5945
5946                    if (!is_array($PosArray)) {
5947                        $Value = $PosArray;
5948                        $PosArray = [];
5949                        $PosArray[0] = $Value;
5950                    }
5951                    $LastGoodY = null;
5952                    $LastGoodX = null;
5953                    foreach ($PosArray as $Key => $Y) {
5954                        if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
5955                            if ($Serie["Data"][$Key] > 0) {
5956                                $Align = TEXT_ALIGN_BOTTOMMIDDLE;
5957                                $Offset = $DisplayOffset;
5958                            } else {
5959                                $Align = TEXT_ALIGN_TOPMIDDLE;
5960                                $Offset = -$DisplayOffset;
5961                            }
5962                            $this->drawText(
5963                                $X,
5964                                $Y - $Offset - $Weight,
5965                                $this->scaleFormat(
5966                                    $Serie["Data"][$Key],
5967                                    $Mode,
5968                                    $Format,
5969                                    $Unit
5970                                ),
5971                                [
5972                                    "R" => $DisplayR,
5973                                    "G" => $DisplayG,
5974                                    "B" => $DisplayB,
5975                                    "Align" => $Align
5976                                ]
5977                            );
5978                        }
5979
5980                        if ($RecordImageMap && $Y != VOID) {
5981                            $this->addToImageMap(
5982                                "CIRCLE",
5983                                floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize,
5984                                $this->toHTMLColor($R, $G, $B),
5985                                $SerieDescription,
5986                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
5987                            );
5988                        }
5989
5990                        if ($Y != VOID && $LastX != null && $LastY != null) {
5991                            $this->drawLine(
5992                                $LastX,
5993                                $LastY,
5994                                $X,
5995                                $Y,
5996                                [
5997                                    "R" => $R,
5998                                    "G" => $G,
5999                                    "B" => $B,
6000                                    "Alpha" => $Alpha,
6001                                    "Ticks" => $Ticks,
6002                                    "Weight" => $Weight
6003                                ]
6004                            );
6005                        }
6006                        if ($Y != VOID && $LastY == null && $LastGoodY != null && !$BreakVoid) {
6007                            $this->drawLine(
6008                                $LastGoodX,
6009                                $LastGoodY,
6010                                $X,
6011                                $Y,
6012                                $BreakSettings
6013                            );
6014                            $LastGoodY = null;
6015                        }
6016
6017                        if ($Y != VOID) {
6018                            $LastGoodY = $Y;
6019                            $LastGoodX = $X;
6020                        }
6021                        if ($Y == VOID) {
6022                            $Y = null;
6023                        }
6024
6025                        $LastX = $X;
6026                        $LastY = $Y;
6027                        $X = $X + $XStep;
6028                    }
6029                } else {
6030                    if ($XDivs == 0) {
6031                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
6032                    } else {
6033                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
6034                    }
6035                    $Y = $this->GraphAreaY1 + $XMargin;
6036                    $LastX = null;
6037                    $LastY = null;
6038
6039                    if (!is_array($PosArray)) {
6040                        $Value = $PosArray;
6041                        $PosArray = [];
6042                        $PosArray[0] = $Value;
6043                    }
6044                    $LastGoodY = null;
6045                    $LastGoodX = null;
6046                    foreach ($PosArray as $Key => $X) {
6047                        if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
6048                            $this->drawText(
6049                                $X + $DisplayOffset + $Weight,
6050                                $Y,
6051                                $this->scaleFormat(
6052                                    $Serie["Data"][$Key],
6053                                    $Mode,
6054                                    $Format,
6055                                    $Unit
6056                                ),
6057                                [
6058                                    "Angle" => 270,
6059                                    "R" => $DisplayR,
6060                                    "G" => $DisplayG,
6061                                    "B" => $DisplayB,
6062                                    "Align" => TEXT_ALIGN_BOTTOMMIDDLE
6063                                ]
6064                            );
6065                        }
6066
6067                        if ($RecordImageMap && $X != VOID) {
6068                            $this->addToImageMap(
6069                                "CIRCLE",
6070                                floor($X) . "," . floor($Y) . "," . $ImageMapPlotSize,
6071                                $this->toHTMLColor($R, $G, $B),
6072                                $SerieDescription,
6073                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6074                            );
6075                        }
6076
6077                        if ($X != VOID && $LastX != null && $LastY != null) {
6078                            $this->drawLine(
6079                                $LastX,
6080                                $LastY,
6081                                $X,
6082                                $Y,
6083                                [
6084                                    "R" => $R,
6085                                    "G" => $G,
6086                                    "B" => $B,
6087                                    "Alpha" => $Alpha,
6088                                    "Ticks" => $Ticks,
6089                                    "Weight" => $Weight
6090                                ]
6091                            );
6092                        }
6093                        if ($X != VOID && $LastX == null && $LastGoodY != null && !$BreakVoid) {
6094                            $this->drawLine(
6095                                $LastGoodX,
6096                                $LastGoodY,
6097                                $X,
6098                                $Y,
6099                                $BreakSettings
6100                            );
6101                            $LastGoodY = null;
6102                        }
6103
6104                        if ($X != VOID) {
6105                            $LastGoodY = $Y;
6106                            $LastGoodX = $X;
6107                        }
6108                        if ($X == VOID) {
6109                            $X = null;
6110                        }
6111
6112                        $LastX = $X;
6113                        $LastY = $Y;
6114                        $Y = $Y + $YStep;
6115                    }
6116                }
6117            }
6118        }
6119    }
6120
6121    /**
6122     * Draw a zone chart
6123     *
6124     * @param string $SerieA
6125     * @param string $SerieB
6126     * @param array $Format
6127     * @return null|integer
6128     */
6129    public function drawZoneChart($SerieA, $SerieB, array $Format = [])
6130    {
6131        $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0;
6132        $LineR = isset($Format["LineR"]) ? $Format["LineR"] : 150;
6133        $LineG = isset($Format["LineG"]) ? $Format["LineG"] : 150;
6134        $LineB = isset($Format["LineB"]) ? $Format["LineB"] : 150;
6135        $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 50;
6136        $LineTicks = isset($Format["LineTicks"]) ? $Format["LineTicks"] : 1;
6137        $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 150;
6138        $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 150;
6139        $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 150;
6140        $AreaAlpha = isset($Format["AreaAlpha"]) ? $Format["AreaAlpha"] : 5;
6141
6142        $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
6143
6144        $Data = $this->DataSet->getData();
6145        if (!isset($Data["Series"][$SerieA]["Data"])
6146            || !isset($Data["Series"][$SerieB]["Data"])
6147        ) {
6148            return 0;
6149        }
6150        $SerieAData = $Data["Series"][$SerieA]["Data"];
6151        $SerieBData = $Data["Series"][$SerieB]["Data"];
6152
6153        list($XMargin, $XDivs) = $this->scaleGetXSettings();
6154
6155        $Mode = $Data["Axis"][$AxisID]["Display"];
6156        $Format = $Data["Axis"][$AxisID]["Format"];
6157
6158        $PosArrayA = $this->scaleComputeY($SerieAData, ["AxisID" => $AxisID]);
6159        $PosArrayB = $this->scaleComputeY($SerieBData, ["AxisID" => $AxisID]);
6160        if (count($PosArrayA) != count($PosArrayB)) {
6161            return 0;
6162        }
6163
6164        if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
6165            if ($XDivs == 0) {
6166                $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
6167            } else {
6168                $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
6169            }
6170            $X = $this->GraphAreaX1 + $XMargin;
6171            $LastX = null;
6172            $LastY = null;
6173
6174            $LastY1 = null;
6175            $LastY2 = null;
6176            $BoundsA = [];
6177            $BoundsB = [];
6178            foreach ($PosArrayA as $Key => $Y1) {
6179                $Y2 = $PosArrayB[$Key];
6180
6181                $BoundsA[] = $X;
6182                $BoundsA[] = $Y1;
6183                $BoundsB[] = $X;
6184                $BoundsB[] = $Y2;
6185
6186                $LastX = $X;
6187                $LastY1 = $Y1;
6188                $LastY2 = $Y2;
6189
6190                $X = $X + $XStep;
6191            }
6192            $Bounds = array_merge($BoundsA, $this->reversePlots($BoundsB));
6193            $this->drawPolygonChart(
6194                $Bounds,
6195                [
6196                    "R" => $AreaR,
6197                    "G" => $AreaG,
6198                    "B" => $AreaB,
6199                    "Alpha" => $AreaAlpha
6200                ]
6201            );
6202
6203            for ($i = 0; $i <= count($BoundsA) - 4; $i = $i + 2) {
6204                $this->drawLine(
6205                    $BoundsA[$i],
6206                    $BoundsA[$i + 1],
6207                    $BoundsA[$i + 2],
6208                    $BoundsA[$i + 3],
6209                    [
6210                        "R" => $LineR,
6211                        "G" => $LineG,
6212                        "B" => $LineB,
6213                        "Alpha" => $LineAlpha,
6214                        "Ticks" => $LineTicks
6215                    ]
6216                );
6217                $this->drawLine(
6218                    $BoundsB[$i],
6219                    $BoundsB[$i + 1],
6220                    $BoundsB[$i + 2],
6221                    $BoundsB[$i + 3],
6222                    [
6223                        "R" => $LineR,
6224                        "G" => $LineG,
6225                        "B" => $LineB,
6226                        "Alpha" => $LineAlpha,
6227                        "Ticks" => $LineTicks
6228                    ]
6229                );
6230            }
6231        } else {
6232            if ($XDivs == 0) {
6233                $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
6234            } else {
6235                $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
6236            }
6237            $Y = $this->GraphAreaY1 + $XMargin;
6238            $LastX = null;
6239            $LastY = null;
6240
6241            $LastX1 = null;
6242            $LastX2 = null;
6243            $BoundsA = [];
6244            $BoundsB = [];
6245            foreach ($PosArrayA as $Key => $X1) {
6246                $X2 = $PosArrayB[$Key];
6247
6248                $BoundsA[] = $X1;
6249                $BoundsA[] = $Y;
6250                $BoundsB[] = $X2;
6251                $BoundsB[] = $Y;
6252
6253                $LastY = $Y;
6254                $LastX1 = $X1;
6255                $LastX2 = $X2;
6256
6257                $Y = $Y + $YStep;
6258            }
6259            $Bounds = array_merge($BoundsA, $this->reversePlots($BoundsB));
6260            $this->drawPolygonChart(
6261                $Bounds,
6262                ["R" => $AreaR, "G" => $AreaG, "B" => $AreaB, "Alpha" => $AreaAlpha]
6263            );
6264
6265            for ($i = 0; $i <= count($BoundsA) - 4; $i = $i + 2) {
6266                $this->drawLine(
6267                    $BoundsA[$i],
6268                    $BoundsA[$i + 1],
6269                    $BoundsA[$i + 2],
6270                    $BoundsA[$i + 3],
6271                    [
6272                        "R" => $LineR,
6273                        "G" => $LineG,
6274                        "B" => $LineB,
6275                        "Alpha" => $LineAlpha,
6276                        "Ticks" => $LineTicks
6277                    ]
6278                );
6279                $this->drawLine(
6280                    $BoundsB[$i],
6281                    $BoundsB[$i + 1],
6282                    $BoundsB[$i + 2],
6283                    $BoundsB[$i + 3],
6284                    [
6285                        "R" => $LineR,
6286                        "G" => $LineG,
6287                        "B" => $LineB,
6288                        "Alpha" => $LineAlpha,
6289                        "Ticks" => $LineTicks
6290                    ]
6291                );
6292            }
6293        }
6294    }
6295
6296    /**
6297     * Draw a step chart
6298     * @param array $Format
6299     */
6300    public function drawStepChart(array $Format = [])
6301    {
6302        $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : false;
6303        $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : true;
6304        $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4;
6305        $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : null;
6306        $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : null;
6307        $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : null;
6308        $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false;
6309        $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
6310        $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
6311        $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
6312        $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
6313        $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
6314        $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
6315        $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5;
6316
6317        $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
6318
6319        $Data = $this->DataSet->getData();
6320        list($XMargin, $XDivs) = $this->scaleGetXSettings();
6321        foreach ($Data["Series"] as $SerieName => $Serie) {
6322            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
6323                $R = $Serie["Color"]["R"];
6324                $G = $Serie["Color"]["G"];
6325                $B = $Serie["Color"]["B"];
6326                $Alpha = $Serie["Color"]["Alpha"];
6327                $Ticks = $Serie["Ticks"];
6328                $Weight = $Serie["Weight"];
6329
6330                if (isset($Serie["Description"])) {
6331                    $SerieDescription = $Serie["Description"];
6332                } else {
6333                    $SerieDescription = $SerieName;
6334                }
6335
6336                if ($BreakR == null) {
6337                    $BreakSettings = [
6338                        "R" => $R,
6339                        "G" => $G,
6340                        "B" => $B,
6341                        "Alpha" => $Alpha,
6342                        "Ticks" => $VoidTicks,
6343                        "Weight" => $Weight
6344                    ];
6345                } else {
6346                    $BreakSettings = [
6347                        "R" => $BreakR,
6348                        "G" => $BreakG,
6349                        "B" => $BreakB,
6350                        "Alpha" => $Alpha,
6351                        "Ticks" => $VoidTicks,
6352                        "Weight" => $Weight
6353                    ];
6354                }
6355                if ($DisplayColor == DISPLAY_AUTO) {
6356                    $DisplayR = $R;
6357                    $DisplayG = $G;
6358                    $DisplayB = $B;
6359                }
6360
6361                $AxisID = $Serie["Axis"];
6362                $Mode = $Data["Axis"][$AxisID]["Display"];
6363                $Format = $Data["Axis"][$AxisID]["Format"];
6364                $Unit = $Data["Axis"][$AxisID]["Unit"];
6365                $Color = [
6366                    "R" => $R,
6367                    "G" => $G,
6368                    "B" => $B,
6369                    "Alpha" => $Alpha,
6370                    "Ticks" => $Ticks,
6371                    "Weight" => $Weight
6372                ];
6373
6374                $PosArray = $this->scaleComputeY(
6375                    $Serie["Data"],
6376                    ["AxisID" => $Serie["Axis"]]
6377                );
6378
6379                $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
6380
6381                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
6382                    if ($XDivs == 0) {
6383                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
6384                    } else {
6385                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
6386                    }
6387                    $X = $this->GraphAreaX1 + $XMargin;
6388                    $LastX = null;
6389                    $LastY = null;
6390
6391                    if (!is_array($PosArray)) {
6392                        $Value = $PosArray;
6393                        $PosArray = [];
6394                        $PosArray[0] = $Value;
6395                    }
6396                    $LastGoodY = null;
6397                    $LastGoodX = null;
6398                    $Init = false;
6399                    foreach ($PosArray as $Key => $Y) {
6400                        if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
6401                            if ($Y <= $LastY) {
6402                                $Align = TEXT_ALIGN_BOTTOMMIDDLE;
6403                                $Offset = $DisplayOffset;
6404                            } else {
6405                                $Align = TEXT_ALIGN_TOPMIDDLE;
6406                                $Offset = -$DisplayOffset;
6407                            }
6408                            $this->drawText(
6409                                $X,
6410                                $Y - $Offset - $Weight,
6411                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit),
6412                                ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align]
6413                            );
6414                        }
6415
6416                        if ($Y != VOID && $LastX != null && $LastY != null) {
6417                            $this->drawLine($LastX, $LastY, $X, $LastY, $Color);
6418                            $this->drawLine($X, $LastY, $X, $Y, $Color);
6419                            if ($ReCenter && $X + $XStep < $this->GraphAreaX2 - $XMargin) {
6420                                $this->drawLine($X, $Y, $X + $XStep, $Y, $Color);
6421                                if ($RecordImageMap) {
6422                                    $this->addToImageMap(
6423                                        "RECT",
6424                                        sprintf(
6425                                            '%s,%s,%s,%s',
6426                                            floor($X - $ImageMapPlotSize),
6427                                            floor($Y - $ImageMapPlotSize),
6428                                            floor($X + $XStep + $ImageMapPlotSize),
6429                                            floor($Y + $ImageMapPlotSize)
6430                                        ),
6431                                        $this->toHTMLColor($R, $G, $B),
6432                                        $SerieDescription,
6433                                        $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6434                                    );
6435                                }
6436                            } else {
6437                                if ($RecordImageMap) {
6438                                    $this->addToImageMap(
6439                                        "RECT",
6440                                        sprintf(
6441                                            '%s,%s,%s,%s',
6442                                            floor($LastX - $ImageMapPlotSize),
6443                                            floor($LastY - $ImageMapPlotSize),
6444                                            floor($X + $ImageMapPlotSize),
6445                                            floor($LastY + $ImageMapPlotSize)
6446                                        ),
6447                                        $this->toHTMLColor($R, $G, $B),
6448                                        $SerieDescription,
6449                                        $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6450                                    );
6451                                }
6452                            }
6453                        }
6454
6455                        if ($Y != VOID && $LastY == null && $LastGoodY != null && !$BreakVoid) {
6456                            if ($ReCenter) {
6457                                $this->drawLine($LastGoodX + $XStep, $LastGoodY, $X, $LastGoodY, $BreakSettings);
6458                                if ($RecordImageMap) {
6459                                    $this->addToImageMap(
6460                                        "RECT",
6461                                        sprintf(
6462                                            '%s,%s,%s,%s',
6463                                            floor($LastGoodX + $XStep - $ImageMapPlotSize),
6464                                            floor($LastGoodY - $ImageMapPlotSize),
6465                                            floor($X + $ImageMapPlotSize),
6466                                            floor($LastGoodY + $ImageMapPlotSize)
6467                                        ),
6468                                        $this->toHTMLColor($R, $G, $B),
6469                                        $SerieDescription,
6470                                        $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6471                                    );
6472                                }
6473                            } else {
6474                                $this->drawLine($LastGoodX, $LastGoodY, $X, $LastGoodY, $BreakSettings);
6475                                if ($RecordImageMap) {
6476                                    $this->addToImageMap(
6477                                        "RECT",
6478                                        sprintf(
6479                                            '%s,%s,%s,%s',
6480                                            floor($LastGoodX - $ImageMapPlotSize),
6481                                            floor($LastGoodY - $ImageMapPlotSize),
6482                                            floor($X + $ImageMapPlotSize),
6483                                            floor($LastGoodY + $ImageMapPlotSize)
6484                                        ),
6485                                        $SerieDescription,
6486                                        $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6487                                    );
6488                                }
6489                            }
6490
6491                            $this->drawLine($X, $LastGoodY, $X, $Y, $BreakSettings);
6492                            $LastGoodY = null;
6493                        } elseif (!$BreakVoid && $LastGoodY == null && $Y != VOID) {
6494                            $this->drawLine($this->GraphAreaX1 + $XMargin, $Y, $X, $Y, $BreakSettings);
6495                            if ($RecordImageMap) {
6496                                $this->addToImageMap(
6497                                    "RECT",
6498                                    sprintf(
6499                                        '%s,%s,%s,%s',
6500                                        floor($this->GraphAreaX1 + $XMargin - $ImageMapPlotSize),
6501                                        floor($Y - $ImageMapPlotSize),
6502                                        floor($X + $ImageMapPlotSize),
6503                                        floor($Y + $ImageMapPlotSize)
6504                                    ),
6505                                    $this->toHTMLColor($R, $G, $B),
6506                                    $SerieDescription,
6507                                    $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6508                                );
6509                            }
6510                        }
6511
6512                        if ($Y != VOID) {
6513                            $LastGoodY = $Y;
6514                            $LastGoodX = $X;
6515                        }
6516                        if ($Y == VOID) {
6517                            $Y = null;
6518                        }
6519
6520                        if (!$Init && $ReCenter) {
6521                            $X = $X - $XStep / 2;
6522                            $Init = true;
6523                        }
6524                        $LastX = $X;
6525                        $LastY = $Y;
6526                        if ($LastX < $this->GraphAreaX1 + $XMargin) {
6527                            $LastX = $this->GraphAreaX1 + $XMargin;
6528                        }
6529                        $X = $X + $XStep;
6530                    }
6531                    if ($ReCenter) {
6532                        $this->drawLine($LastX, $LastY, $this->GraphAreaX2 - $XMargin, $LastY, $Color);
6533                        if ($RecordImageMap) {
6534                            $this->addToImageMap(
6535                                "RECT",
6536                                sprintf(
6537                                    '%s,%s,%s,%s',
6538                                    floor($LastX - $ImageMapPlotSize),
6539                                    floor($LastY - $ImageMapPlotSize),
6540                                    floor($this->GraphAreaX2 - $XMargin + $ImageMapPlotSize),
6541                                    floor($LastY + $ImageMapPlotSize)
6542                                ),
6543                                $this->toHTMLColor($R, $G, $B),
6544                                $SerieDescription,
6545                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6546                            );
6547                        }
6548                    }
6549                } else {
6550                    if ($XDivs == 0) {
6551                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
6552                    } else {
6553                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
6554                    }
6555                    $Y = $this->GraphAreaY1 + $XMargin;
6556                    $LastX = null;
6557                    $LastY = null;
6558
6559                    if (!is_array($PosArray)) {
6560                        $Value = $PosArray;
6561                        $PosArray = [];
6562                        $PosArray[0] = $Value;
6563                    }
6564                    $LastGoodY = null;
6565                    $LastGoodX = null;
6566                    $Init = false;
6567                    foreach ($PosArray as $Key => $X) {
6568                        if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
6569                            if ($X >= $LastX) {
6570                                $Align = TEXT_ALIGN_MIDDLELEFT;
6571                                $Offset = $DisplayOffset;
6572                            } else {
6573                                $Align = TEXT_ALIGN_MIDDLERIGHT;
6574                                $Offset = -$DisplayOffset;
6575                            }
6576                            $this->drawText(
6577                                $X + $Offset + $Weight,
6578                                $Y,
6579                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit),
6580                                [
6581                                    "R" => $DisplayR,
6582                                    "G" => $DisplayG,
6583                                    "B" => $DisplayB,
6584                                    "Align" => $Align
6585                                ]
6586                            );
6587                        }
6588
6589                        if ($X != VOID && $LastX != null && $LastY != null) {
6590                            $this->drawLine($LastX, $LastY, $LastX, $Y, $Color);
6591                            $this->drawLine($LastX, $Y, $X, $Y, $Color);
6592
6593                            if ($RecordImageMap) {
6594                                $this->addToImageMap(
6595                                    "RECT",
6596                                    sprintf(
6597                                        '%s,%s,%s,%s',
6598                                        floor($LastX - $ImageMapPlotSize),
6599                                        floor($LastY - $ImageMapPlotSize),
6600                                        floor($LastX + $XStep + $ImageMapPlotSize),
6601                                        floor($Y + $ImageMapPlotSize)
6602                                    ),
6603                                    $this->toHTMLColor($R, $G, $B),
6604                                    $SerieDescription,
6605                                    $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6606                                );
6607                            }
6608                        }
6609
6610                        if ($X != VOID && $LastX == null && $LastGoodY != null && !$BreakVoid) {
6611                            $this->drawLine(
6612                                $LastGoodX,
6613                                $LastGoodY,
6614                                $LastGoodX,
6615                                $LastGoodY + $YStep,
6616                                $Color
6617                            );
6618                            if ($RecordImageMap) {
6619                                $this->addToImageMap(
6620                                    "RECT",
6621                                    sprintf(
6622                                        '%s,%s,%s,%s',
6623                                        floor($LastGoodX - $ImageMapPlotSize),
6624                                        floor($LastGoodY - $ImageMapPlotSize),
6625                                        floor($LastGoodX + $ImageMapPlotSize),
6626                                        floor($LastGoodY + $YStep + $ImageMapPlotSize)
6627                                    ),
6628                                    $this->toHTMLColor($R, $G, $B),
6629                                    $SerieDescription,
6630                                    $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6631                                );
6632                            }
6633
6634                            $this->drawLine(
6635                                $LastGoodX,
6636                                $LastGoodY + $YStep,
6637                                $LastGoodX,
6638                                $Y,
6639                                $BreakSettings
6640                            );
6641                            if ($RecordImageMap) {
6642                                $this->addToImageMap(
6643                                    "RECT",
6644                                    sprintf(
6645                                        '%s,%s,%s,%s',
6646                                        floor($LastGoodX - $ImageMapPlotSize),
6647                                        floor($LastGoodY + $YStep - $ImageMapPlotSize),
6648                                        floor($LastGoodX + $ImageMapPlotSize),
6649                                        floor($YStep + $ImageMapPlotSize)
6650                                    ),
6651                                    $this->toHTMLColor($R, $G, $B),
6652                                    $SerieDescription,
6653                                    $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6654                                );
6655                            }
6656
6657                            $this->drawLine($LastGoodX, $Y, $X, $Y, $BreakSettings);
6658                            $LastGoodY = null;
6659                        } elseif ($X != VOID && $LastGoodY == null && !$BreakVoid) {
6660                            $this->drawLine($X, $this->GraphAreaY1 + $XMargin, $X, $Y, $BreakSettings);
6661                            if ($RecordImageMap) {
6662                                $this->addToImageMap(
6663                                    "RECT",
6664                                    sprintf(
6665                                        '%s,%s,%s,%s',
6666                                        floor($X - $ImageMapPlotSize),
6667                                        floor($this->GraphAreaY1 + $XMargin - $ImageMapPlotSize),
6668                                        floor($X + $ImageMapPlotSize),
6669                                        floor($Y + $ImageMapPlotSize)
6670                                    ),
6671                                    $this->toHTMLColor($R, $G, $B),
6672                                    $SerieDescription,
6673                                    $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6674                                );
6675                            }
6676                        }
6677
6678                        if ($X != VOID) {
6679                            $LastGoodY = $Y;
6680                            $LastGoodX = $X;
6681                        }
6682                        if ($X == VOID) {
6683                            $X = null;
6684                        }
6685
6686                        if (!$Init && $ReCenter) {
6687                            $Y = $Y - $YStep / 2;
6688                            $Init = true;
6689                        }
6690                        $LastX = $X;
6691                        $LastY = $Y;
6692                        if ($LastY < $this->GraphAreaY1 + $XMargin) {
6693                            $LastY = $this->GraphAreaY1 + $XMargin;
6694                        }
6695                        $Y = $Y + $YStep;
6696                    }
6697                    if ($ReCenter) {
6698                        $this->drawLine($LastX, $LastY, $LastX, $this->GraphAreaY2 - $XMargin, $Color);
6699                        if ($RecordImageMap) {
6700                            $this->addToImageMap(
6701                                "RECT",
6702                                sprintf(
6703                                    '%s,%s,%s,%s',
6704                                    floor($LastX - $ImageMapPlotSize),
6705                                    floor($LastY - $ImageMapPlotSize),
6706                                    floor($LastX + $ImageMapPlotSize),
6707                                    floor($this->GraphAreaY2 - $XMargin + $ImageMapPlotSize)
6708                                ),
6709                                $this->toHTMLColor($R, $G, $B),
6710                                $SerieDescription,
6711                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
6712                            );
6713                        }
6714                    }
6715                }
6716            }
6717        }
6718    }
6719
6720    /**
6721     * Draw a step chart
6722     * @param array $Format
6723     */
6724    public function drawFilledStepChart(array $Format = [])
6725    {
6726        $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : true;
6727        $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false;
6728        $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
6729        $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
6730        $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : null;
6731        $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
6732        $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
6733        $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
6734        $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : true;
6735
6736        $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
6737
6738        $Data = $this->DataSet->getData();
6739        list($XMargin, $XDivs) = $this->scaleGetXSettings();
6740        foreach ($Data["Series"] as $SerieName => $Serie) {
6741            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
6742                $R = $Serie["Color"]["R"];
6743                $G = $Serie["Color"]["G"];
6744                $B = $Serie["Color"]["B"];
6745                $Alpha = $Serie["Color"]["Alpha"];
6746
6747                if ($DisplayColor == DISPLAY_AUTO) {
6748                    $DisplayR = $R;
6749                    $DisplayG = $G;
6750                    $DisplayB = $B;
6751                }
6752
6753                $AxisID = $Serie["Axis"];
6754                $Format = $Data["Axis"][$AxisID]["Format"];
6755
6756                $Color = ["R" => $R, "G" => $G, "B" => $B];
6757                if ($ForceTransparency != null) {
6758                    $Color["Alpha"] = $ForceTransparency;
6759                } else {
6760                    $Color["Alpha"] = $Alpha;
6761                }
6762
6763                $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
6764                $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
6765
6766                $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
6767
6768                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
6769                    if ($YZero > $this->GraphAreaY2 - 1) {
6770                        $YZero = $this->GraphAreaY2 - 1;
6771                    }
6772                    if ($YZero < $this->GraphAreaY1 + 1) {
6773                        $YZero = $this->GraphAreaY1 + 1;
6774                    }
6775
6776                    if ($XDivs == 0) {
6777                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
6778                    } else {
6779                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
6780                    }
6781                    $X = $this->GraphAreaX1 + $XMargin;
6782                    $LastX = null;
6783                    $LastY = null;
6784
6785                    if (!$AroundZero) {
6786                        $YZero = $this->GraphAreaY2 - 1;
6787                    }
6788
6789                    if (!is_array($PosArray)) {
6790                        $Value = $PosArray;
6791                        $PosArray = [];
6792                        $PosArray[0] = $Value;
6793                    }
6794                    $LastGoodY = null;
6795                    $LastGoodX = null;
6796                    $Points = [];
6797                    $Init = false;
6798                    foreach ($PosArray as $Key => $Y) {
6799                        if ($Y == VOID && $LastX != null && $LastY != null && count($Points)) {
6800                            $Points[] = $LastX;
6801                            $Points[] = $LastY;
6802                            $Points[] = $X;
6803                            $Points[] = $LastY;
6804                            $Points[] = $X;
6805                            $Points[] = $YZero;
6806                            $this->drawPolygon($Points, $Color);
6807                            $Points = [];
6808                        }
6809
6810                        if ($Y != VOID && $LastX != null && $LastY != null) {
6811                            if (count($Points)) {
6812                                $Points[] = $LastX;
6813                                $Points[] = $YZero;
6814                            }
6815                            $Points[] = $LastX;
6816                            $Points[] = $LastY;
6817                            $Points[] = $X;
6818                            $Points[] = $LastY;
6819                            $Points[] = $X;
6820                            $Points[] = $Y;
6821                        }
6822
6823                        if ($Y != VOID) {
6824                            $LastGoodY = $Y;
6825                            $LastGoodX = $X;
6826                        }
6827                        if ($Y == VOID) {
6828                            $Y = null;
6829                        }
6830
6831                        if (!$Init && $ReCenter) {
6832                            $X = $X - $XStep / 2;
6833                            $Init = true;
6834                        }
6835                        $LastX = $X;
6836                        $LastY = $Y;
6837                        if ($LastX < $this->GraphAreaX1 + $XMargin) {
6838                            $LastX = $this->GraphAreaX1 + $XMargin;
6839                        }
6840                        $X = $X + $XStep;
6841                    }
6842
6843                    if ($ReCenter) {
6844                        $Points[] = $LastX + $XStep / 2;
6845                        $Points[] = $LastY;
6846                        $Points[] = $LastX + $XStep / 2;
6847                        $Points[] = $YZero;
6848                    } else {
6849                        $Points[] = $LastX;
6850                        $Points[] = $YZero;
6851                    }
6852
6853                    $this->drawPolygon($Points, $Color);
6854                } else {
6855                    if ($YZero < $this->GraphAreaX1 + 1) {
6856                        $YZero = $this->GraphAreaX1 + 1;
6857                    }
6858                    if ($YZero > $this->GraphAreaX2 - 1) {
6859                        $YZero = $this->GraphAreaX2 - 1;
6860                    }
6861
6862                    if ($XDivs == 0) {
6863                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
6864                    } else {
6865                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
6866                    }
6867                    $Y = $this->GraphAreaY1 + $XMargin;
6868                    $LastX = null;
6869                    $LastY = null;
6870
6871                    if (!is_array($PosArray)) {
6872                        $Value = $PosArray;
6873                        $PosArray = [];
6874                        $PosArray[0] = $Value;
6875                    }
6876                    $LastGoodY = null;
6877                    $LastGoodX = null;
6878                    $Points = [];
6879                    foreach ($PosArray as $Key => $X) {
6880                        if ($X == VOID && $LastX != null && $LastY != null && count($Points)) {
6881                            $Points[] = $LastX;
6882                            $Points[] = $LastY;
6883                            $Points[] = $LastX;
6884                            $Points[] = $Y;
6885                            $Points[] = $YZero;
6886                            $Points[] = $Y;
6887                            $this->drawPolygon($Points, $Color);
6888                            $Points = [];
6889                        }
6890
6891                        if ($X != VOID && $LastX != null && $LastY != null) {
6892                            if (count($Points)) {
6893                                $Points[] = $YZero;
6894                                $Points[] = $LastY;
6895                            }
6896                            $Points[] = $LastX;
6897                            $Points[] = $LastY;
6898                            $Points[] = $LastX;
6899                            $Points[] = $Y;
6900                            $Points[] = $X;
6901                            $Points[] = $Y;
6902                        }
6903
6904                        if ($X != VOID) {
6905                            $LastGoodY = $Y;
6906                            $LastGoodX = $X;
6907                        }
6908                        if ($X == VOID) {
6909                            $X = null;
6910                        }
6911
6912                        if ($LastX == null && $ReCenter) {
6913                            $Y = $Y - $YStep / 2;
6914                        }
6915                        $LastX = $X;
6916                        $LastY = $Y;
6917                        if ($LastY < $this->GraphAreaY1 + $XMargin) {
6918                            $LastY = $this->GraphAreaY1 + $XMargin;
6919                        }
6920                        $Y = $Y + $YStep;
6921                    }
6922
6923                    if ($ReCenter) {
6924                        $Points[] = $LastX;
6925                        $Points[] = $LastY + $YStep / 2;
6926                        $Points[] = $YZero;
6927                        $Points[] = $LastY + $YStep / 2;
6928                    } else {
6929                        $Points[] = $YZero;
6930                        $Points[] = $LastY;
6931                    }
6932
6933                    $this->drawPolygon($Points, $Color);
6934                }
6935            }
6936        }
6937    }
6938
6939    /**
6940     * Draw an area chart
6941     * @param array $Format
6942     */
6943    public function drawAreaChart(array $Format = [])
6944    {
6945        $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false;
6946        $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
6947        $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
6948        $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
6949        $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
6950        $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
6951        $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : 25;
6952        $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : true;
6953        $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null;
6954
6955        $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
6956
6957        $Data = $this->DataSet->getData();
6958        list($XMargin, $XDivs) = $this->scaleGetXSettings();
6959
6960        foreach ($Data["Series"] as $SerieName => $Serie) {
6961            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
6962                $R = $Serie["Color"]["R"];
6963                $G = $Serie["Color"]["G"];
6964                $B = $Serie["Color"]["B"];
6965                $Alpha = $Serie["Color"]["Alpha"];
6966                $Ticks = $Serie["Ticks"];
6967                if ($DisplayColor == DISPLAY_AUTO) {
6968                    $DisplayR = $R;
6969                    $DisplayG = $G;
6970                    $DisplayB = $B;
6971                }
6972
6973                $AxisID = $Serie["Axis"];
6974                $Mode = $Data["Axis"][$AxisID]["Display"];
6975                $Format = $Data["Axis"][$AxisID]["Format"];
6976                $Unit = $Data["Axis"][$AxisID]["Unit"];
6977
6978                $PosArray = $this->scaleComputeY($Serie["Data"], ["AxisID" => $Serie["Axis"]]);
6979                $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
6980
6981                if ($Threshold != null) {
6982                    foreach ($Threshold as $Key => $Params) {
6983                        $Threshold[$Key]["MinX"] = $this->scaleComputeY(
6984                            $Params["Min"],
6985                            ["AxisID" => $Serie["Axis"]]
6986                        );
6987                        $Threshold[$Key]["MaxX"] = $this->scaleComputeY(
6988                            $Params["Max"],
6989                            ["AxisID" => $Serie["Axis"]]
6990                        );
6991                    }
6992                }
6993
6994                $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
6995
6996                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
6997                    if ($YZero > $this->GraphAreaY2 - 1) {
6998                        $YZero = $this->GraphAreaY2 - 1;
6999                    }
7000
7001                    $Areas = [];
7002                    $AreaID = 0;
7003                    $Areas[$AreaID][] = $this->GraphAreaX1 + $XMargin;
7004                    if ($AroundZero) {
7005                        $Areas[$AreaID][] = $YZero;
7006                    } else {
7007                        $Areas[$AreaID][] = $this->GraphAreaY2 - 1;
7008                    }
7009
7010                    if ($XDivs == 0) {
7011                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
7012                    } else {
7013                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
7014                    }
7015                    $X = $this->GraphAreaX1 + $XMargin;
7016                    $LastX = null;
7017                    $LastY = null;
7018
7019                    if (!is_array($PosArray)) {
7020                        $Value = $PosArray;
7021                        $PosArray = [];
7022                        $PosArray[0] = $Value;
7023                    }
7024                    foreach ($PosArray as $Key => $Y) {
7025                        if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
7026                            if ($Serie["Data"][$Key] > 0) {
7027                                $Align = TEXT_ALIGN_BOTTOMMIDDLE;
7028                                $Offset = $DisplayOffset;
7029                            } else {
7030                                $Align = TEXT_ALIGN_TOPMIDDLE;
7031                                $Offset = -$DisplayOffset;
7032                            }
7033                            $this->drawText(
7034                                $X,
7035                                $Y - $Offset,
7036                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit),
7037                                ["R" => $DisplayR, "G" => $DisplayG, "B" => $DisplayB, "Align" => $Align]
7038                            );
7039                        }
7040
7041                        if ($Y == VOID && isset($Areas[$AreaID])) {
7042                            if ($LastX == null) {
7043                                $Areas[$AreaID][] = $X;
7044                            } else {
7045                                $Areas[$AreaID][] = $LastX;
7046                            }
7047
7048                            if ($AroundZero) {
7049                                $Areas[$AreaID][] = $YZero;
7050                            } else {
7051                                $Areas[$AreaID][] = $this->GraphAreaY2 - 1;
7052                            }
7053                            $AreaID++;
7054                        } elseif ($Y != VOID) {
7055                            if (!isset($Areas[$AreaID])) {
7056                                $Areas[$AreaID][] = $X;
7057                                if ($AroundZero) {
7058                                    $Areas[$AreaID][] = $YZero;
7059                                } else {
7060                                    $Areas[$AreaID][] = $this->GraphAreaY2 - 1;
7061                                }
7062                            }
7063
7064                            $Areas[$AreaID][] = $X;
7065                            $Areas[$AreaID][] = $Y;
7066                        }
7067
7068                        $LastX = $X;
7069                        $X = $X + $XStep;
7070                    }
7071                    $Areas[$AreaID][] = $LastX;
7072                    if ($AroundZero) {
7073                        $Areas[$AreaID][] = $YZero;
7074                    } else {
7075                        $Areas[$AreaID][] = $this->GraphAreaY2 - 1;
7076                    }
7077
7078                    /* Handle shadows in the areas */
7079                    if ($this->Shadow) {
7080                        $ShadowArea = [];
7081                        foreach ($Areas as $Key => $Points) {
7082                            $ShadowArea[$Key] = [];
7083                            foreach ($Points as $Key2 => $Value) {
7084                                if ($Key2 % 2 == 0) {
7085                                    $ShadowArea[$Key][] = $Value + $this->ShadowX;
7086                                } else {
7087                                    $ShadowArea[$Key][] = $Value + $this->ShadowY;
7088                                }
7089                            }
7090                        }
7091
7092                        foreach ($ShadowArea as $Key => $Points) {
7093                            $this->drawPolygonChart(
7094                                $Points,
7095                                [
7096                                    "R" => $this->ShadowR,
7097                                    "G" => $this->ShadowG,
7098                                    "B" => $this->ShadowB,
7099                                    "Alpha" => $this->Shadowa
7100                                ]
7101                            );
7102                        }
7103                    }
7104
7105                    $Alpha = $ForceTransparency != null ? $ForceTransparency : $Alpha;
7106                    $Color = [
7107                        "R" => $R,
7108                        "G" => $G,
7109                        "B" => $B,
7110                        "Alpha" => $Alpha,
7111                        "Threshold" => $Threshold
7112                    ];
7113
7114                    foreach ($Areas as $Key => $Points) {
7115                        $this->drawPolygonChart($Points, $Color);
7116                    }
7117                } else {
7118                    if ($YZero < $this->GraphAreaX1 + 1) {
7119                        $YZero = $this->GraphAreaX1 + 1;
7120                    }
7121                    if ($YZero > $this->GraphAreaX2 - 1) {
7122                        $YZero = $this->GraphAreaX2 - 1;
7123                    }
7124
7125                    $Areas = [];
7126                    $AreaID = 0;
7127                    if ($AroundZero) {
7128                        $Areas[$AreaID][] = $YZero;
7129                    } else {
7130                        $Areas[$AreaID][] = $this->GraphAreaX1 + 1;
7131                    }
7132                    $Areas[$AreaID][] = $this->GraphAreaY1 + $XMargin;
7133
7134                    if ($XDivs == 0) {
7135                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
7136                    } else {
7137                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
7138                    }
7139                    $Y = $this->GraphAreaY1 + $XMargin;
7140                    $LastX = null;
7141                    $LastY = null;
7142
7143                    if (!is_array($PosArray)) {
7144                        $Value = $PosArray;
7145                        $PosArray = [];
7146                        $PosArray[0] = $Value;
7147                    }
7148                    foreach ($PosArray as $Key => $X) {
7149                        if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
7150                            if ($Serie["Data"][$Key] > 0) {
7151                                $Align = TEXT_ALIGN_BOTTOMMIDDLE;
7152                                $Offset = $DisplayOffset;
7153                            } else {
7154                                $Align = TEXT_ALIGN_TOPMIDDLE;
7155                                $Offset = -$DisplayOffset;
7156                            }
7157                            $this->drawText(
7158                                $X + $Offset,
7159                                $Y,
7160                                $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit),
7161                                [
7162                                    "Angle" => 270,
7163                                    "R" => $DisplayR,
7164                                    "G" => $DisplayG,
7165                                    "B" => $DisplayB,
7166                                    "Align" => $Align
7167                                ]
7168                            );
7169                        }
7170
7171                        if ($X == VOID && isset($Areas[$AreaID])) {
7172                            if ($AroundZero) {
7173                                $Areas[$AreaID][] = $YZero;
7174                            } else {
7175                                $Areas[$AreaID][] = $this->GraphAreaX1 + 1;
7176                            }
7177
7178                            if ($LastY == null) {
7179                                $Areas[$AreaID][] = $Y;
7180                            } else {
7181                                $Areas[$AreaID][] = $LastY;
7182                            }
7183
7184                            $AreaID++;
7185                        } elseif ($X != VOID) {
7186                            if (!isset($Areas[$AreaID])) {
7187                                if ($AroundZero) {
7188                                    $Areas[$AreaID][] = $YZero;
7189                                } else {
7190                                    $Areas[$AreaID][] = $this->GraphAreaX1 + 1;
7191                                }
7192                                $Areas[$AreaID][] = $Y;
7193                            }
7194
7195                            $Areas[$AreaID][] = $X;
7196                            $Areas[$AreaID][] = $Y;
7197                        }
7198
7199                        $LastX = $X;
7200                        $LastY = $Y;
7201                        $Y = $Y + $YStep;
7202                    }
7203                    if ($AroundZero) {
7204                        $Areas[$AreaID][] = $YZero;
7205                    } else {
7206                        $Areas[$AreaID][] = $this->GraphAreaX1 + 1;
7207                    }
7208                    $Areas[$AreaID][] = $LastY;
7209
7210                    /* Handle shadows in the areas */
7211                    if ($this->Shadow) {
7212                        $ShadowArea = [];
7213                        foreach ($Areas as $Key => $Points) {
7214                            $ShadowArea[$Key] = [];
7215                            foreach ($Points as $Key2 => $Value) {
7216                                if ($Key2 % 2 == 0) {
7217                                    $ShadowArea[$Key][] = $Value + $this->ShadowX;
7218                                } else {
7219                                    $ShadowArea[$Key][] = $Value + $this->ShadowY;
7220                                }
7221                            }
7222                        }
7223
7224                        foreach ($ShadowArea as $Key => $Points) {
7225                            $this->drawPolygonChart(
7226                                $Points,
7227                                [
7228                                    "R" => $this->ShadowR,
7229                                    "G" => $this->ShadowG,
7230                                    "B" => $this->ShadowB,
7231                                    "Alpha" => $this->Shadowa
7232                                ]
7233                            );
7234                        }
7235                    }
7236
7237                    $Alpha = $ForceTransparency != null ? $ForceTransparency : $Alpha;
7238                    $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Threshold" => $Threshold];
7239
7240                    foreach ($Areas as $Key => $Points) {
7241                        $this->drawPolygonChart($Points, $Color);
7242                    }
7243                }
7244            }
7245        }
7246    }
7247
7248    /**
7249     * Draw a bar chart
7250     * @param array $Format
7251     */
7252    public function drawBarChart(array $Format = [])
7253    {
7254        $Floating0Serie = isset($Format["Floating0Serie"]) ? $Format["Floating0Serie"] : null;
7255        $Floating0Value = isset($Format["Floating0Value"]) ? $Format["Floating0Value"] : null;
7256        $Draw0Line = isset($Format["Draw0Line"]) ? $Format["Draw0Line"] : false;
7257        $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false;
7258        $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
7259        $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
7260        $DisplayFont = isset($Format["DisplayFont"]) ? $Format["DisplayFont"] : $this->FontName;
7261        $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize;
7262        $DisplayPos = isset($Format["DisplayPos"]) ? $Format["DisplayPos"] : LABEL_POS_OUTSIDE;
7263        $DisplayShadow = isset($Format["DisplayShadow"]) ? $Format["DisplayShadow"] : true;
7264        $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
7265        $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
7266        $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
7267        $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : true;
7268        $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5;
7269        $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : false;
7270        $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4;
7271        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
7272        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
7273        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
7274        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
7275        $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : false;
7276        $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE;
7277        $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20;
7278        $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255;
7279        $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255;
7280        $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255;
7281        $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0;
7282        $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0;
7283        $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0;
7284        $TxtMargin = isset($Format["TxtMargin"]) ? $Format["TxtMargin"] : 6;
7285        $OverrideColors = isset($Format["OverrideColors"]) ? $Format["OverrideColors"] : null;
7286        $OverrideSurrounding = isset($Format["OverrideSurrounding"]) ? $Format["OverrideSurrounding"] : 30;
7287        $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : null;
7288        $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1;
7289        $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1;
7290        $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1;
7291        $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
7292
7293        $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
7294
7295        $Data = $this->DataSet->getData();
7296        list($XMargin, $XDivs) = $this->scaleGetXSettings();
7297
7298        if ($OverrideColors != null) {
7299            $OverrideColors = $this->validatePalette($OverrideColors, $OverrideSurrounding);
7300            $this->DataSet->saveExtendedData("Palette", $OverrideColors);
7301        }
7302
7303        $RestoreShadow = $this->Shadow;
7304
7305        $SeriesCount = $this->countDrawableSeries();
7306        $CurrentSerie = 0;
7307        foreach ($Data["Series"] as $SerieName => $Serie) {
7308            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
7309                $R = $Serie["Color"]["R"];
7310                $G = $Serie["Color"]["G"];
7311                $B = $Serie["Color"]["B"];
7312                $Alpha = $Serie["Color"]["Alpha"];
7313                $Ticks = $Serie["Ticks"];
7314                if ($DisplayColor == DISPLAY_AUTO) {
7315                    $DisplayR = $R;
7316                    $DisplayG = $G;
7317                    $DisplayB = $B;
7318                }
7319                if ($Surrounding != null) {
7320                    $BorderR = $R + $Surrounding;
7321                    $BorderG = $G + $Surrounding;
7322                    $BorderB = $B + $Surrounding;
7323                }
7324                if ($InnerSurrounding != null) {
7325                    $InnerBorderR = $R + $InnerSurrounding;
7326                    $InnerBorderG = $G + $InnerSurrounding;
7327                    $InnerBorderB = $B + $InnerSurrounding;
7328                }
7329                if ($InnerBorderR == -1) {
7330                    $InnerColor = null;
7331                } else {
7332                    $InnerColor = [
7333                        "R" => $InnerBorderR,
7334                        "G" => $InnerBorderG,
7335                        "B" => $InnerBorderB
7336                    ];
7337                }
7338                $Color = [
7339                    "R" => $R,
7340                    "G" => $G,
7341                    "B" => $B,
7342                    "Alpha" => $Alpha,
7343                    "BorderR" => $BorderR,
7344                    "BorderG" => $BorderG,
7345                    "BorderB" => $BorderB
7346                ];
7347
7348                $AxisID = $Serie["Axis"];
7349                $Mode = $Data["Axis"][$AxisID]["Display"];
7350                $Format = $Data["Axis"][$AxisID]["Format"];
7351                $Unit = $Data["Axis"][$AxisID]["Unit"];
7352
7353                if (isset($Serie["Description"])) {
7354                    $SerieDescription = $Serie["Description"];
7355                } else {
7356                    $SerieDescription = $SerieName;
7357                }
7358
7359                $PosArray = $this->scaleComputeY(
7360                    $Serie["Data"],
7361                    ["AxisID" => $Serie["Axis"]]
7362                );
7363
7364                if ($Floating0Value != null) {
7365                    $YZero = $this->scaleComputeY(
7366                        $Floating0Value,
7367                        ["AxisID" => $Serie["Axis"]]
7368                    );
7369                } else {
7370                    $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
7371                }
7372
7373                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
7374                    if ($YZero > $this->GraphAreaY2 - 1) {
7375                        $YZero = $this->GraphAreaY2 - 1;
7376                    }
7377                    if ($YZero < $this->GraphAreaY1 + 1) {
7378                        $YZero = $this->GraphAreaY1 + 1;
7379                    }
7380
7381                    if ($XDivs == 0) {
7382                        $XStep = 0;
7383                    } else {
7384                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
7385                    }
7386                    $X = $this->GraphAreaX1 + $XMargin;
7387
7388                    if ($AroundZero) {
7389                        $Y1 = $YZero;
7390                    } else {
7391                        $Y1 = $this->GraphAreaY2 - 1;
7392                    }
7393                    if ($XDivs == 0) {
7394                        $XSize = ($this->GraphAreaX2 - $this->GraphAreaX1) / ($SeriesCount + $Interleave);
7395                    } else {
7396                        $XSize = ($XStep / ($SeriesCount + $Interleave));
7397                    }
7398
7399                    $XOffset = -($XSize * $SeriesCount) / 2 + $CurrentSerie * $XSize;
7400                    if ($X + $XOffset <= $this->GraphAreaX1) {
7401                        $XOffset = $this->GraphAreaX1 - $X + 1;
7402                    }
7403
7404                    $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $XOffset + $XSize / 2;
7405
7406                    if ($Rounded || $BorderR != -1) {
7407                        $XSpace = 1;
7408                    } else {
7409                        $XSpace = 0;
7410                    }
7411
7412                    if (!is_array($PosArray)) {
7413                        $Value = $PosArray;
7414                        $PosArray = [];
7415                        $PosArray[0] = $Value;
7416                    }
7417
7418                    $ID = 0;
7419                    foreach ($PosArray as $Key => $Y2) {
7420                        if ($Floating0Serie != null) {
7421                            if (isset($Data["Series"][$Floating0Serie]["Data"][$Key])) {
7422                                $Value = $Data["Series"][$Floating0Serie]["Data"][$Key];
7423                            } else {
7424                                $Value = 0;
7425                            }
7426
7427                            $YZero = $this->scaleComputeY($Value, ["AxisID" => $Serie["Axis"]]);
7428                            if ($YZero > $this->GraphAreaY2 - 1) {
7429                                $YZero = $this->GraphAreaY2 - 1;
7430                            }
7431                            if ($YZero < $this->GraphAreaY1 + 1) {
7432                                $YZero = $this->GraphAreaY1 + 1;
7433                            }
7434
7435                            if ($AroundZero) {
7436                                $Y1 = $YZero;
7437                            } else {
7438                                $Y1 = $this->GraphAreaY2 - 1;
7439                            }
7440                        }
7441
7442                        if ($OverrideColors != null) {
7443                            if (isset($OverrideColors[$ID])) {
7444                                $Color = [
7445                                    "R" => $OverrideColors[$ID]["R"],
7446                                    "G" => $OverrideColors[$ID]["G"],
7447                                    "B" => $OverrideColors[$ID]["B"],
7448                                    "Alpha" => $OverrideColors[$ID]["Alpha"],
7449                                    "BorderR" => $OverrideColors[$ID]["BorderR"],
7450                                    "BorderG" => $OverrideColors[$ID]["BorderG"],
7451                                    "BorderB" => $OverrideColors[$ID]["BorderB"]
7452                                ];
7453                            } else {
7454                                $Color = $this->getRandomColor();
7455                            }
7456                        }
7457
7458                        if ($Y2 != VOID) {
7459                            $BarHeight = $Y1 - $Y2;
7460
7461                            if ($Serie["Data"][$Key] == 0) {
7462                                $this->drawLine(
7463                                    $X + $XOffset + $XSpace,
7464                                    $Y1,
7465                                    $X + $XOffset + $XSize - $XSpace,
7466                                    $Y1,
7467                                    $Color
7468                                );
7469                                if ($RecordImageMap) {
7470                                    $this->addToImageMap(
7471                                        "RECT",
7472                                        sprintf(
7473                                            "%s,%s,%s,%s",
7474                                            floor($X + $XOffset + $XSpace),
7475                                            floor($Y1 - 1),
7476                                            floor($X + $XOffset + $XSize - $XSpace),
7477                                            floor($Y1 + 1)
7478                                        ),
7479                                        $this->toHTMLColor($R, $G, $B),
7480                                        $SerieDescription,
7481                                        $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
7482                                    );
7483                                }
7484                            } else {
7485                                if ($RecordImageMap) {
7486                                    $this->addToImageMap(
7487                                        "RECT",
7488                                        sprintf(
7489                                            "%s,%s,%s,%s",
7490                                            floor($X + $XOffset + $XSpace),
7491                                            floor($Y1),
7492                                            floor($X + $XOffset + $XSize - $XSpace),
7493                                            floor($Y2)
7494                                        ),
7495                                        $this->toHTMLColor($R, $G, $B),
7496                                        $SerieDescription,
7497                                        $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
7498                                    );
7499                                }
7500
7501                                if ($Rounded) {
7502                                    $this->drawRoundedFilledRectangle(
7503                                        $X + $XOffset + $XSpace,
7504                                        $Y1,
7505                                        $X + $XOffset + $XSize - $XSpace,
7506                                        $Y2,
7507                                        $RoundRadius,
7508                                        $Color
7509                                    );
7510                                } else {
7511                                    $this->drawFilledRectangle(
7512                                        $X + $XOffset + $XSpace,
7513                                        $Y1,
7514                                        $X + $XOffset + $XSize - $XSpace,
7515                                        $Y2,
7516                                        $Color
7517                                    );
7518
7519                                    if ($InnerColor != null) {
7520                                        $this->drawRectangle(
7521                                            $X + $XOffset + $XSpace + 1,
7522                                            min($Y1, $Y2) + 1,
7523                                            $X + $XOffset + $XSize - $XSpace - 1,
7524                                            max($Y1, $Y2) - 1,
7525                                            $InnerColor
7526                                        );
7527                                    }
7528
7529                                    if ($Gradient) {
7530                                        $this->Shadow = false;
7531
7532                                        if ($GradientMode == GRADIENT_SIMPLE) {
7533                                            if ($Serie["Data"][$Key] >= 0) {
7534                                                $GradienColor = [
7535                                                    "StartR" => $GradientStartR,
7536                                                    "StartG" => $GradientStartG,
7537                                                    "StartB" => $GradientStartB,
7538                                                    "EndR" => $GradientEndR,
7539                                                    "EndG" => $GradientEndG,
7540                                                    "EndB" => $GradientEndB,
7541                                                    "Alpha" => $GradientAlpha
7542                                                ];
7543                                            } else {
7544                                                $GradienColor = [
7545                                                    "StartR" => $GradientEndR,
7546                                                    "StartG" => $GradientEndG,
7547                                                    "StartB" => $GradientEndB,
7548                                                    "EndR" => $GradientStartR,
7549                                                    "EndG" => $GradientStartG,
7550                                                    "EndB" => $GradientStartB,
7551                                                    "Alpha" => $GradientAlpha
7552                                                ];
7553                                            }
7554                                            $this->drawGradientArea(
7555                                                $X + $XOffset + $XSpace,
7556                                                $Y1,
7557                                                $X + $XOffset + $XSize - $XSpace,
7558                                                $Y2,
7559                                                DIRECTION_VERTICAL,
7560                                                $GradienColor
7561                                            );
7562                                        } elseif ($GradientMode == GRADIENT_EFFECT_CAN) {
7563                                            $GradienColor1 = [
7564                                                "StartR" => $GradientEndR,
7565                                                "StartG" => $GradientEndG,
7566                                                "StartB" => $GradientEndB,
7567                                                "EndR" => $GradientStartR,
7568                                                "EndG" => $GradientStartG,
7569                                                "EndB" => $GradientStartB,
7570                                                "Alpha" => $GradientAlpha
7571                                            ];
7572                                            $GradienColor2 = [
7573                                                "StartR" => $GradientStartR,
7574                                                "StartG" => $GradientStartG,
7575                                                "StartB" => $GradientStartB,
7576                                                "EndR" => $GradientEndR,
7577                                                "EndG" => $GradientEndG,
7578                                                "EndB" => $GradientEndB,
7579                                                "Alpha" => $GradientAlpha
7580                                            ];
7581                                            $XSpan = floor($XSize / 3);
7582
7583                                            $this->drawGradientArea(
7584                                                $X + $XOffset + $XSpace,
7585                                                $Y1,
7586                                                $X + $XOffset + $XSpan - $XSpace,
7587                                                $Y2,
7588                                                DIRECTION_HORIZONTAL,
7589                                                $GradienColor1
7590                                            );
7591                                            $this->drawGradientArea(
7592                                                $X + $XOffset + $XSpan + $XSpace,
7593                                                $Y1,
7594                                                $X + $XOffset + $XSize - $XSpace,
7595                                                $Y2,
7596                                                DIRECTION_HORIZONTAL,
7597                                                $GradienColor2
7598                                            );
7599                                        }
7600                                        $this->Shadow = $RestoreShadow;
7601                                    }
7602                                }
7603
7604                                if ($Draw0Line) {
7605                                    $Line0Color = ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20];
7606
7607                                    if (abs($Y1 - $Y2) > 3) {
7608                                        $Line0Width = 3;
7609                                    } else {
7610                                        $Line0Width = 1;
7611                                    }
7612                                    if ($Y1 - $Y2 < 0) {
7613                                        $Line0Width = -$Line0Width;
7614                                    }
7615
7616                                    $this->drawFilledRectangle(
7617                                        $X + $XOffset + $XSpace,
7618                                        floor($Y1),
7619                                        $X + $XOffset + $XSize - $XSpace,
7620                                        floor($Y1) - $Line0Width,
7621                                        $Line0Color
7622                                    );
7623                                    $this->drawLine(
7624                                        $X + $XOffset + $XSpace,
7625                                        floor($Y1),
7626                                        $X + $XOffset + $XSize - $XSpace,
7627                                        floor($Y1),
7628                                        $Line0Color
7629                                    );
7630                                }
7631                            }
7632
7633                            if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
7634                                if ($DisplayShadow) {
7635                                    $this->Shadow = true;
7636                                }
7637
7638                                $Caption = $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit);
7639                                $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 90, $Caption);
7640                                $TxtHeight = $TxtPos[0]["Y"] - $TxtPos[1]["Y"] + $TxtMargin;
7641
7642                                if ($DisplayPos == LABEL_POS_INSIDE && abs($TxtHeight) < abs($BarHeight)) {
7643                                    $CenterX = (($X + $XOffset + $XSize - $XSpace) - ($X + $XOffset + $XSpace))
7644                                        / 2 + $X + $XOffset + $XSpace
7645                                    ;
7646                                    $CenterY = ($Y2 - $Y1) / 2 + $Y1;
7647
7648                                    $this->drawText(
7649                                        $CenterX,
7650                                        $CenterY,
7651                                        $Caption,
7652                                        [
7653                                            "R" => $DisplayR,
7654                                            "G" => $DisplayG,
7655                                            "B" => $DisplayB,
7656                                            "Align" => TEXT_ALIGN_MIDDLEMIDDLE,
7657                                            "FontSize" => $DisplaySize,
7658                                            "Angle" => 90
7659                                        ]
7660                                    );
7661                                } else {
7662                                    if ($Serie["Data"][$Key] >= 0) {
7663                                        $Align = TEXT_ALIGN_BOTTOMMIDDLE;
7664                                        $Offset = $DisplayOffset;
7665                                    } else {
7666                                        $Align = TEXT_ALIGN_TOPMIDDLE;
7667                                        $Offset = -$DisplayOffset;
7668                                    }
7669                                    $this->drawText(
7670                                        $X + $XOffset + $XSize / 2,
7671                                        $Y2 - $Offset,
7672                                        $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit),
7673                                        [
7674                                            "R" => $DisplayR,
7675                                            "G" => $DisplayG,
7676                                            "B" => $DisplayB,
7677                                            "Align" => $Align,
7678                                            "FontSize" => $DisplaySize
7679                                        ]
7680                                    );
7681                                }
7682
7683                                $this->Shadow = $RestoreShadow;
7684                            }
7685                        }
7686
7687                        $X = $X + $XStep;
7688                        $ID++;
7689                    }
7690                } else {
7691                    if ($YZero < $this->GraphAreaX1 + 1) {
7692                        $YZero = $this->GraphAreaX1 + 1;
7693                    }
7694                    if ($YZero > $this->GraphAreaX2 - 1) {
7695                        $YZero = $this->GraphAreaX2 - 1;
7696                    }
7697
7698                    if ($XDivs == 0) {
7699                        $YStep = 0;
7700                    } else {
7701                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
7702                    }
7703
7704                    $Y = $this->GraphAreaY1 + $XMargin;
7705
7706                    if ($AroundZero) {
7707                        $X1 = $YZero;
7708                    } else {
7709                        $X1 = $this->GraphAreaX1 + 1;
7710                    }
7711                    if ($XDivs == 0) {
7712                        $YSize = ($this->GraphAreaY2 - $this->GraphAreaY1) / ($SeriesCount + $Interleave);
7713                    } else {
7714                        $YSize = ($YStep / ($SeriesCount + $Interleave));
7715                    }
7716
7717                    $YOffset = -($YSize * $SeriesCount) / 2 + $CurrentSerie * $YSize;
7718                    if ($Y + $YOffset <= $this->GraphAreaY1) {
7719                        $YOffset = $this->GraphAreaY1 - $Y + 1;
7720                    }
7721
7722                    $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $YOffset + $YSize / 2;
7723
7724                    if ($Rounded || $BorderR != -1) {
7725                        $YSpace = 1;
7726                    } else {
7727                        $YSpace = 0;
7728                    }
7729
7730                    if (!is_array($PosArray)) {
7731                        $Value = $PosArray;
7732                        $PosArray = [];
7733                        $PosArray[0] = $Value;
7734                    }
7735
7736                    $ID = 0;
7737                    foreach ($PosArray as $Key => $X2) {
7738                        if ($Floating0Serie != null) {
7739                            if (isset($Data["Series"][$Floating0Serie]["Data"][$Key])) {
7740                                $Value = $Data["Series"][$Floating0Serie]["Data"][$Key];
7741                            } else {
7742                                $Value = 0;
7743                            }
7744
7745                            $YZero = $this->scaleComputeY($Value, ["AxisID" => $Serie["Axis"]]);
7746                            if ($YZero < $this->GraphAreaX1 + 1) {
7747                                $YZero = $this->GraphAreaX1 + 1;
7748                            }
7749                            if ($YZero > $this->GraphAreaX2 - 1) {
7750                                $YZero = $this->GraphAreaX2 - 1;
7751                            }
7752                            if ($AroundZero) {
7753                                $X1 = $YZero;
7754                            } else {
7755                                $X1 = $this->GraphAreaX1 + 1;
7756                            }
7757                        }
7758
7759                        if ($OverrideColors != null) {
7760                            if (isset($OverrideColors[$ID])) {
7761                                $Color = [
7762                                    "R" => $OverrideColors[$ID]["R"],
7763                                    "G" => $OverrideColors[$ID]["G"],
7764                                    "B" => $OverrideColors[$ID]["B"],
7765                                    "Alpha" => $OverrideColors[$ID]["Alpha"],
7766                                    "BorderR" => $OverrideColors[$ID]["BorderR"],
7767                                    "BorderG" => $OverrideColors[$ID]["BorderG"],
7768                                    "BorderB" => $OverrideColors[$ID]["BorderB"]
7769                                ];
7770                            } else {
7771                                $Color = $this->getRandomColor();
7772                            }
7773                        }
7774
7775                        if ($X2 != VOID) {
7776                            $BarWidth = $X2 - $X1;
7777                            if ($Serie["Data"][$Key] == 0) {
7778                                $this->drawLine(
7779                                    $X1,
7780                                    $Y + $YOffset + $YSpace,
7781                                    $X1,
7782                                    $Y + $YOffset + $YSize - $YSpace,
7783                                    $Color
7784                                );
7785                                if ($RecordImageMap) {
7786                                    $this->addToImageMap(
7787                                        "RECT",
7788                                        sprintf(
7789                                            "%s,%s,%s,%s",
7790                                            floor($X1 - 1),
7791                                            floor($Y + $YOffset + $YSpace),
7792                                            floor($X1 + 1),
7793                                            floor($Y + $YOffset + $YSize - $YSpace)
7794                                        ),
7795                                        $this->toHTMLColor($R, $G, $B),
7796                                        $SerieDescription,
7797                                        $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
7798                                    );
7799                                }
7800                            } else {
7801                                if ($RecordImageMap) {
7802                                    $this->addToImageMap(
7803                                        "RECT",
7804                                        sprintf(
7805                                            "%s,%s,%s,%s",
7806                                            floor($X1),
7807                                            floor($Y + $YOffset + $YSpace),
7808                                            floor($X2),
7809                                            floor($Y + $YOffset + $YSize - $YSpace)
7810                                        ),
7811                                        $this->toHTMLColor($R, $G, $B),
7812                                        $SerieDescription,
7813                                        $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
7814                                    );
7815                                }
7816
7817                                if ($Rounded) {
7818                                    $this->drawRoundedFilledRectangle(
7819                                        $X1 + 1,
7820                                        $Y + $YOffset + $YSpace,
7821                                        $X2,
7822                                        $Y + $YOffset + $YSize - $YSpace,
7823                                        $RoundRadius,
7824                                        $Color
7825                                    );
7826                                } else {
7827                                    $this->drawFilledRectangle(
7828                                        $X1,
7829                                        $Y + $YOffset + $YSpace,
7830                                        $X2,
7831                                        $Y + $YOffset + $YSize - $YSpace,
7832                                        $Color
7833                                    );
7834
7835                                    if ($InnerColor != null) {
7836                                        $this->drawRectangle(
7837                                            min($X1, $X2) + 1,
7838                                            $Y + $YOffset + $YSpace + 1,
7839                                            max($X1, $X2) - 1,
7840                                            $Y + $YOffset + $YSize - $YSpace - 1,
7841                                            $InnerColor
7842                                        );
7843                                    }
7844
7845                                    if ($Gradient) {
7846                                        $this->Shadow = false;
7847
7848                                        if ($GradientMode == GRADIENT_SIMPLE) {
7849                                            if ($Serie["Data"][$Key] >= 0) {
7850                                                $GradienColor = [
7851                                                    "StartR" => $GradientStartR,
7852                                                    "StartG" => $GradientStartG,
7853                                                    "StartB" => $GradientStartB,
7854                                                    "EndR" => $GradientEndR,
7855                                                    "EndG" => $GradientEndG,
7856                                                    "EndB" => $GradientEndB,
7857                                                    "Alpha" => $GradientAlpha
7858                                                ];
7859                                            } else {
7860                                                $GradienColor = [
7861                                                    "StartR" => $GradientEndR,
7862                                                    "StartG" => $GradientEndG,
7863                                                    "StartB" => $GradientEndB,
7864                                                    "EndR" => $GradientStartR,
7865                                                    "EndG" => $GradientStartG,
7866                                                    "EndB" => $GradientStartB,
7867                                                    "Alpha" => $GradientAlpha
7868                                                ];
7869                                            }
7870                                            $this->drawGradientArea(
7871                                                $X1,
7872                                                $Y + $YOffset + $YSpace,
7873                                                $X2,
7874                                                $Y + $YOffset + $YSize - $YSpace,
7875                                                DIRECTION_HORIZONTAL,
7876                                                $GradienColor
7877                                            );
7878                                        } elseif ($GradientMode == GRADIENT_EFFECT_CAN) {
7879                                            $GradienColor1 = [
7880                                                "StartR" => $GradientEndR,
7881                                                "StartG" => $GradientEndG,
7882                                                "StartB" => $GradientEndB,
7883                                                "EndR" => $GradientStartR,
7884                                                "EndG" => $GradientStartG,
7885                                                "EndB" => $GradientStartB,
7886                                                "Alpha" => $GradientAlpha
7887                                            ];
7888                                            $GradienColor2 = [
7889                                                "StartR" => $GradientStartR,
7890                                                "StartG" => $GradientStartG,
7891                                                "StartB" => $GradientStartB,
7892                                                "EndR" => $GradientEndR,
7893                                                "EndG" => $GradientEndG,
7894                                                "EndB" => $GradientEndB,
7895                                                "Alpha" => $GradientAlpha
7896                                            ];
7897                                            $YSpan = floor($YSize / 3);
7898
7899                                            $this->drawGradientArea(
7900                                                $X1,
7901                                                $Y + $YOffset + $YSpace,
7902                                                $X2,
7903                                                $Y + $YOffset + $YSpan - $YSpace,
7904                                                DIRECTION_VERTICAL,
7905                                                $GradienColor1
7906                                            );
7907                                            $this->drawGradientArea(
7908                                                $X1,
7909                                                $Y + $YOffset + $YSpan,
7910                                                $X2,
7911                                                $Y + $YOffset + $YSize - $YSpace,
7912                                                DIRECTION_VERTICAL,
7913                                                $GradienColor2
7914                                            );
7915                                        }
7916                                        $this->Shadow = $RestoreShadow;
7917                                    }
7918                                }
7919
7920                                if ($Draw0Line) {
7921                                    $Line0Color = ["R" => 0, "G" => 0, "B" => 0, "Alpha" => 20];
7922
7923                                    if (abs($X1 - $X2) > 3) {
7924                                        $Line0Width = 3;
7925                                    } else {
7926                                        $Line0Width = 1;
7927                                    }
7928                                    if ($X2 - $X1 < 0) {
7929                                        $Line0Width = -$Line0Width;
7930                                    }
7931
7932                                    $this->drawFilledRectangle(
7933                                        floor($X1),
7934                                        $Y + $YOffset + $YSpace,
7935                                        floor($X1) + $Line0Width,
7936                                        $Y + $YOffset + $YSize - $YSpace,
7937                                        $Line0Color
7938                                    );
7939                                    $this->drawLine(
7940                                        floor($X1),
7941                                        $Y + $YOffset + $YSpace,
7942                                        floor($X1),
7943                                        $Y + $YOffset + $YSize - $YSpace,
7944                                        $Line0Color
7945                                    );
7946                                }
7947                            }
7948
7949                            if ($DisplayValues && $Serie["Data"][$Key] != VOID) {
7950                                if ($DisplayShadow) {
7951                                    $this->Shadow = true;
7952                                }
7953
7954                                $Caption = $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit);
7955                                $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 0, $Caption);
7956                                $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"] + $TxtMargin;
7957
7958                                if ($DisplayPos == LABEL_POS_INSIDE && abs($TxtWidth) < abs($BarWidth)) {
7959                                    $CenterX = ($X2 - $X1) / 2 + $X1;
7960                                    $CenterY = (($Y + $YOffset + $YSize - $YSpace)
7961                                        - ($Y + $YOffset + $YSpace)) / 2
7962                                        + ($Y + $YOffset + $YSpace)
7963                                    ;
7964
7965                                    $this->drawText(
7966                                        $CenterX,
7967                                        $CenterY,
7968                                        $Caption,
7969                                        [
7970                                            "R" => $DisplayR,
7971                                            "G" => $DisplayG,
7972                                            "B" => $DisplayB,
7973                                            "Align" => TEXT_ALIGN_MIDDLEMIDDLE,
7974                                            "FontSize" => $DisplaySize
7975                                        ]
7976                                    );
7977                                } else {
7978                                    if ($Serie["Data"][$Key] >= 0) {
7979                                        $Align = TEXT_ALIGN_MIDDLELEFT;
7980                                        $Offset = $DisplayOffset;
7981                                    } else {
7982                                        $Align = TEXT_ALIGN_MIDDLERIGHT;
7983                                        $Offset = -$DisplayOffset;
7984                                    }
7985                                    $this->drawText(
7986                                        $X2 + $Offset,
7987                                        $Y + $YOffset + $YSize / 2,
7988                                        $Caption,
7989                                        [
7990                                            "R" => $DisplayR,
7991                                            "G" => $DisplayG,
7992                                            "B" => $DisplayB,
7993                                            "Align" => $Align,
7994                                            "FontSize" => $DisplaySize
7995                                        ]
7996                                    );
7997                                }
7998
7999                                $this->Shadow = $RestoreShadow;
8000                            }
8001                        }
8002                        $Y = $Y + $YStep;
8003                        $ID++;
8004                    }
8005                }
8006                $CurrentSerie++;
8007            }
8008        }
8009    }
8010
8011    /**
8012     * Draw a bar chart
8013     * @param array $Format
8014     */
8015    public function drawStackedBarChart(array $Format = [])
8016    {
8017        $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : false;
8018        $DisplayOrientation = isset($Format["DisplayOrientation"]) ? $Format["DisplayOrientation"] : ORIENTATION_AUTO;
8019        $DisplayRound = isset($Format["DisplayRound"]) ? $Format["DisplayRound"] : 0;
8020        $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
8021        $DisplayFont = isset($Format["DisplayFont"]) ? $Format["DisplayFont"] : $this->FontName;
8022        $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize;
8023        $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
8024        $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
8025        $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
8026        $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5;
8027        $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : false;
8028        $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4;
8029        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
8030        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
8031        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
8032        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
8033        $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : false;
8034        $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE;
8035        $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20;
8036        $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255;
8037        $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255;
8038        $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255;
8039        $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0;
8040        $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0;
8041        $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0;
8042        $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : null;
8043        $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1;
8044        $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1;
8045        $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1;
8046        $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
8047        $FontFactor = isset($Format["FontFactor"]) ? $Format["FontFactor"] : 8;
8048
8049        $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED;
8050
8051        $Data = $this->DataSet->getData();
8052        list($XMargin, $XDivs) = $this->scaleGetXSettings();
8053
8054        $RestoreShadow = $this->Shadow;
8055
8056        $LastX = [];
8057        $LastY = [];
8058        foreach ($Data["Series"] as $SerieName => $Serie) {
8059            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
8060                $R = $Serie["Color"]["R"];
8061                $G = $Serie["Color"]["G"];
8062                $B = $Serie["Color"]["B"];
8063                $Alpha = $Serie["Color"]["Alpha"];
8064                $Ticks = $Serie["Ticks"];
8065                if ($DisplayColor == DISPLAY_AUTO) {
8066                    $DisplayR = 255;
8067                    $DisplayG = 255;
8068                    $DisplayB = 255;
8069                }
8070                if ($Surrounding != null) {
8071                    $BorderR = $R + $Surrounding;
8072                    $BorderG = $G + $Surrounding;
8073                    $BorderB = $B + $Surrounding;
8074                }
8075                if ($InnerSurrounding != null) {
8076                    $InnerBorderR = $R + $InnerSurrounding;
8077                    $InnerBorderG = $G + $InnerSurrounding;
8078                    $InnerBorderB = $B + $InnerSurrounding;
8079                }
8080                if ($InnerBorderR == -1) {
8081                    $InnerColor = null;
8082                } else {
8083                    $InnerColor = [
8084                        "R" => $InnerBorderR,
8085                        "G" => $InnerBorderG,
8086                        "B" => $InnerBorderB
8087                    ];
8088                }
8089
8090                $AxisID = $Serie["Axis"];
8091                $Mode = $Data["Axis"][$AxisID]["Display"];
8092                $Format = $Data["Axis"][$AxisID]["Format"];
8093                $Unit = $Data["Axis"][$AxisID]["Unit"];
8094
8095                if (isset($Serie["Description"])) {
8096                    $SerieDescription = $Serie["Description"];
8097                } else {
8098                    $SerieDescription = $SerieName;
8099                }
8100
8101                $PosArray = $this->scaleComputeY(
8102                    $Serie["Data"],
8103                    ["AxisID" => $Serie["Axis"]],
8104                    true
8105                );
8106                $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
8107
8108                $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
8109
8110                $Color = [
8111                    "TransCorner" => true,
8112                    "R" => $R,
8113                    "G" => $G,
8114                    "B" => $B,
8115                    "Alpha" => $Alpha,
8116                    "BorderR" => $BorderR,
8117                    "BorderG" => $BorderG,
8118                    "BorderB" => $BorderB
8119                ];
8120
8121                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
8122                    if ($YZero > $this->GraphAreaY2 - 1) {
8123                        $YZero = $this->GraphAreaY2 - 1;
8124                    }
8125                    if ($YZero > $this->GraphAreaY2 - 1) {
8126                        $YZero = $this->GraphAreaY2 - 1;
8127                    }
8128
8129                    if ($XDivs == 0) {
8130                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
8131                    } else {
8132                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
8133                    }
8134                    $X = $this->GraphAreaX1 + $XMargin;
8135
8136                    $XSize = ($XStep / (1 + $Interleave));
8137                    $XOffset = -($XSize / 2);
8138
8139                    if (!is_array($PosArray)) {
8140                        $Value = $PosArray;
8141                        $PosArray = [];
8142                        $PosArray[0] = $Value;
8143                    }
8144                    foreach ($PosArray as $Key => $Height) {
8145                        if ($Height != VOID && $Serie["Data"][$Key] != 0) {
8146                            if ($Serie["Data"][$Key] > 0) {
8147                                $Pos = "+";
8148                            } else {
8149                                $Pos = "-";
8150                            }
8151
8152                            if (!isset($LastY[$Key])) {
8153                                $LastY[$Key] = [];
8154                            }
8155                            if (!isset($LastY[$Key][$Pos])) {
8156                                $LastY[$Key][$Pos] = $YZero;
8157                            }
8158
8159                            $Y1 = $LastY[$Key][$Pos];
8160                            $Y2 = $Y1 - $Height;
8161
8162                            if (($Rounded || $BorderR != -1) && ($Pos == "+" && $Y1 != $YZero)) {
8163                                $YSpaceUp = 1;
8164                            } else {
8165                                $YSpaceUp = 0;
8166                            }
8167                            if (($Rounded || $BorderR != -1) && ($Pos == "-" && $Y1 != $YZero)) {
8168                                $YSpaceDown = 1;
8169                            } else {
8170                                $YSpaceDown = 0;
8171                            }
8172
8173                            if ($RecordImageMap) {
8174                                $this->addToImageMap(
8175                                    "RECT",
8176                                    sprintf(
8177                                        "%s,%s,%s,%s",
8178                                        floor($X + $XOffset),
8179                                        floor($Y1 - $YSpaceUp + $YSpaceDown),
8180                                        floor($X + $XOffset + $XSize),
8181                                        floor($Y2)
8182                                    ),
8183                                    $this->toHTMLColor($R, $G, $B),
8184                                    $SerieDescription,
8185                                    $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
8186                                );
8187                            }
8188
8189                            if ($Rounded) {
8190                                $this->drawRoundedFilledRectangle(
8191                                    $X + $XOffset,
8192                                    $Y1 - $YSpaceUp + $YSpaceDown,
8193                                    $X + $XOffset + $XSize,
8194                                    $Y2,
8195                                    $RoundRadius,
8196                                    $Color
8197                                );
8198                            } else {
8199                                $this->drawFilledRectangle(
8200                                    $X + $XOffset,
8201                                    $Y1 - $YSpaceUp + $YSpaceDown,
8202                                    $X + $XOffset + $XSize,
8203                                    $Y2,
8204                                    $Color
8205                                );
8206
8207                                if ($InnerColor != null) {
8208                                    $RestoreShadow = $this->Shadow;
8209                                    $this->Shadow = false;
8210                                    $this->drawRectangle(
8211                                        min($X + $XOffset + 1, $X + $XOffset + $XSize),
8212                                        min($Y1 - $YSpaceUp + $YSpaceDown, $Y2) + 1,
8213                                        max($X + $XOffset + 1, $X + $XOffset + $XSize) - 1,
8214                                        max($Y1 - $YSpaceUp + $YSpaceDown, $Y2) - 1,
8215                                        $InnerColor
8216                                    );
8217                                    $this->Shadow = $RestoreShadow;
8218                                }
8219
8220                                if ($Gradient) {
8221                                    $this->Shadow = false;
8222
8223                                    if ($GradientMode == GRADIENT_SIMPLE) {
8224                                        $GradientColor = [
8225                                            "StartR" => $GradientStartR,
8226                                            "StartG" => $GradientStartG,
8227                                            "StartB" => $GradientStartB,
8228                                            "EndR" => $GradientEndR,
8229                                            "EndG" => $GradientEndG,
8230                                            "EndB" => $GradientEndB,
8231                                            "Alpha" => $GradientAlpha
8232                                        ];
8233                                        $this->drawGradientArea(
8234                                            $X + $XOffset,
8235                                            $Y1 - 1 - $YSpaceUp + $YSpaceDown,
8236                                            $X + $XOffset + $XSize,
8237                                            $Y2 + 1,
8238                                            DIRECTION_VERTICAL,
8239                                            $GradientColor
8240                                        );
8241                                    } elseif ($GradientMode == GRADIENT_EFFECT_CAN) {
8242                                        $GradientColor1 = [
8243                                            "StartR" => $GradientEndR,
8244                                            "StartG" => $GradientEndG,
8245                                            "StartB" => $GradientEndB,
8246                                            "EndR" => $GradientStartR,
8247                                            "EndG" => $GradientStartG,
8248                                            "EndB" => $GradientStartB,
8249                                            "Alpha" => $GradientAlpha
8250                                        ];
8251                                        $GradientColor2 = [
8252                                            "StartR" => $GradientStartR,
8253                                            "StartG" => $GradientStartG,
8254                                            "StartB" => $GradientStartB,
8255                                            "EndR" => $GradientEndR,
8256                                            "EndG" => $GradientEndG,
8257                                            "EndB" => $GradientEndB,
8258                                            "Alpha" => $GradientAlpha
8259                                        ];
8260                                        $XSpan = floor($XSize / 3);
8261
8262                                        $this->drawGradientArea(
8263                                            $X + $XOffset - .5,
8264                                            $Y1 - .5 - $YSpaceUp + $YSpaceDown,
8265                                            $X + $XOffset + $XSpan,
8266                                            $Y2 + .5,
8267                                            DIRECTION_HORIZONTAL,
8268                                            $GradientColor1
8269                                        );
8270                                        $this->drawGradientArea(
8271                                            $X + $XSpan + $XOffset - .5,
8272                                            $Y1 - .5 - $YSpaceUp + $YSpaceDown,
8273                                            $X + $XOffset + $XSize,
8274                                            $Y2 + .5,
8275                                            DIRECTION_HORIZONTAL,
8276                                            $GradientColor2
8277                                        );
8278                                    }
8279                                    $this->Shadow = $RestoreShadow;
8280                                }
8281                            }
8282
8283                            if ($DisplayValues) {
8284                                $BarHeight = abs($Y2 - $Y1) - 2;
8285                                $BarWidth = $XSize + ($XOffset / 2) - $FontFactor;
8286
8287                                $Caption = $this->scaleFormat(
8288                                    round($Serie["Data"][$Key], $DisplayRound),
8289                                    $Mode,
8290                                    $Format,
8291                                    $Unit
8292                                );
8293                                $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 0, $Caption);
8294                                $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]);
8295                                $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]);
8296
8297                                $XCenter = (($X + $XOffset + $XSize) - ($X + $XOffset)) / 2 + $X + $XOffset;
8298                                $YCenter = (($Y2) - ($Y1 - $YSpaceUp + $YSpaceDown)) / 2
8299                                    + $Y1 - $YSpaceUp + $YSpaceDown
8300                                ;
8301
8302                                $Done = false;
8303                                if ($DisplayOrientation == ORIENTATION_HORIZONTAL
8304                                    || $DisplayOrientation == ORIENTATION_AUTO
8305                                ) {
8306                                    if ($TxtHeight < $BarHeight && $TxtWidth < $BarWidth) {
8307                                        $this->drawText(
8308                                            $XCenter,
8309                                            $YCenter,
8310                                            $this->scaleFormat(
8311                                                $Serie["Data"][$Key],
8312                                                $Mode,
8313                                                $Format,
8314                                                $Unit
8315                                            ),
8316                                            [
8317                                                "R" => $DisplayR,
8318                                                "G" => $DisplayG,
8319                                                "B" => $DisplayB,
8320                                                "Align" => TEXT_ALIGN_MIDDLEMIDDLE,
8321                                                "FontSize" => $DisplaySize,
8322                                                "FontName" => $DisplayFont
8323                                            ]
8324                                        );
8325                                        $Done = true;
8326                                    }
8327                                }
8328
8329                                if ($DisplayOrientation == ORIENTATION_VERTICAL
8330                                    || ($DisplayOrientation == ORIENTATION_AUTO && !$Done)
8331                                ) {
8332                                    if ($TxtHeight < $BarWidth && $TxtWidth < $BarHeight) {
8333                                        $this->drawText(
8334                                            $XCenter,
8335                                            $YCenter,
8336                                            $this->scaleFormat(
8337                                                $Serie["Data"][$Key],
8338                                                $Mode,
8339                                                $Format,
8340                                                $Unit
8341                                            ),
8342                                            [
8343                                                "R" => $DisplayR,
8344                                                "G" => $DisplayG,
8345                                                "B" => $DisplayB,
8346                                                "Angle" => 90,
8347                                                "Align" => TEXT_ALIGN_MIDDLEMIDDLE,
8348                                                "FontSize" => $DisplaySize,
8349                                                "FontName" => $DisplayFont
8350                                            ]
8351                                        );
8352                                    }
8353                                }
8354                            }
8355                            $LastY[$Key][$Pos] = $Y2;
8356                        }
8357                        $X = $X + $XStep;
8358                    }
8359                } else {
8360                    if ($YZero < $this->GraphAreaX1 + 1) {
8361                        $YZero = $this->GraphAreaX1 + 1;
8362                    }
8363                    if ($YZero > $this->GraphAreaX2 - 1) {
8364                        $YZero = $this->GraphAreaX2 - 1;
8365                    }
8366
8367                    if ($XDivs == 0) {
8368                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
8369                    } else {
8370                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
8371                    }
8372                    $Y = $this->GraphAreaY1 + $XMargin;
8373
8374                    $YSize = $YStep / (1 + $Interleave);
8375                    $YOffset = -($YSize / 2);
8376
8377                    if (!is_array($PosArray)) {
8378                        $Value = $PosArray;
8379                        $PosArray = [];
8380                        $PosArray[0] = $Value;
8381                    }
8382                    foreach ($PosArray as $Key => $Width) {
8383                        if ($Width != VOID && $Serie["Data"][$Key] != 0) {
8384                            if ($Serie["Data"][$Key] > 0) {
8385                                $Pos = "+";
8386                            } else {
8387                                $Pos = "-";
8388                            }
8389
8390                            if (!isset($LastX[$Key])) {
8391                                $LastX[$Key] = [];
8392                            }
8393                            if (!isset($LastX[$Key][$Pos])) {
8394                                $LastX[$Key][$Pos] = $YZero;
8395                            }
8396
8397                            $X1 = $LastX[$Key][$Pos];
8398                            $X2 = $X1 + $Width;
8399
8400                            if (($Rounded || $BorderR != -1) && ($Pos == "+" && $X1 != $YZero)) {
8401                                $XSpaceLeft = 2;
8402                            } else {
8403                                $XSpaceLeft = 0;
8404                            }
8405                            if (($Rounded || $BorderR != -1) && ($Pos == "-" && $X1 != $YZero)) {
8406                                $XSpaceRight = 2;
8407                            } else {
8408                                $XSpaceRight = 0;
8409                            }
8410
8411                            if ($RecordImageMap) {
8412                                $this->addToImageMap(
8413                                    "RECT",
8414                                    sprintf(
8415                                        "%s,%s,%s,%s",
8416                                        floor($X1 + $XSpaceLeft),
8417                                        floor($Y + $YOffset),
8418                                        floor($X2 - $XSpaceRight),
8419                                        floor($Y + $YOffset + $YSize)
8420                                    ),
8421                                    $this->toHTMLColor($R, $G, $B),
8422                                    $SerieDescription,
8423                                    $this->scaleFormat($Serie["Data"][$Key], $Mode, $Format, $Unit)
8424                                );
8425                            }
8426
8427                            if ($Rounded) {
8428                                $this->drawRoundedFilledRectangle(
8429                                    $X1 + $XSpaceLeft,
8430                                    $Y + $YOffset,
8431                                    $X2 - $XSpaceRight,
8432                                    $Y + $YOffset + $YSize,
8433                                    $RoundRadius,
8434                                    $Color
8435                                );
8436                            } else {
8437                                $this->drawFilledRectangle(
8438                                    $X1 + $XSpaceLeft,
8439                                    $Y + $YOffset,
8440                                    $X2 - $XSpaceRight,
8441                                    $Y + $YOffset + $YSize,
8442                                    $Color
8443                                );
8444
8445                                if ($InnerColor != null) {
8446                                    $RestoreShadow = $this->Shadow;
8447                                    $this->Shadow = false;
8448                                    $this->drawRectangle(
8449                                        min($X1 + $XSpaceLeft, $X2 - $XSpaceRight) + 1,
8450                                        min($Y + $YOffset, $Y + $YOffset + $YSize) + 1,
8451                                        max($X1 + $XSpaceLeft, $X2 - $XSpaceRight) - 1,
8452                                        max($Y + $YOffset, $Y + $YOffset + $YSize) - 1,
8453                                        $InnerColor
8454                                    );
8455                                    $this->Shadow = $RestoreShadow;
8456                                }
8457
8458                                if ($Gradient) {
8459                                    $this->Shadow = false;
8460
8461                                    if ($GradientMode == GRADIENT_SIMPLE) {
8462                                        $GradientColor = [
8463                                            "StartR" => $GradientStartR,
8464                                            "StartG" => $GradientStartG,
8465                                            "StartB" => $GradientStartB,
8466                                            "EndR" => $GradientEndR,
8467                                            "EndG" => $GradientEndG,
8468                                            "EndB" => $GradientEndB,
8469                                            "Alpha" => $GradientAlpha
8470                                        ];
8471                                        $this->drawGradientArea(
8472                                            $X1 + $XSpaceLeft,
8473                                            $Y + $YOffset,
8474                                            $X2 - $XSpaceRight,
8475                                            $Y + $YOffset + $YSize,
8476                                            DIRECTION_HORIZONTAL,
8477                                            $GradientColor
8478                                        );
8479                                    } elseif ($GradientMode == GRADIENT_EFFECT_CAN) {
8480                                        $GradientColor1 = [
8481                                            "StartR" => $GradientEndR,
8482                                            "StartG" => $GradientEndG,
8483                                            "StartB" => $GradientEndB,
8484                                            "EndR" => $GradientStartR,
8485                                            "EndG" => $GradientStartG,
8486                                            "EndB" => $GradientStartB,
8487                                            "Alpha" => $GradientAlpha
8488                                        ];
8489                                        $GradientColor2 = [
8490                                            "StartR" => $GradientStartR,
8491                                            "StartG" => $GradientStartG,
8492                                            "StartB" => $GradientStartB,
8493                                            "EndR" => $GradientEndR,
8494                                            "EndG" => $GradientEndG,
8495                                            "EndB" => $GradientEndB,
8496                                            "Alpha" => $GradientAlpha
8497                                        ];
8498                                        $YSpan = floor($YSize / 3);
8499
8500                                        $this->drawGradientArea(
8501                                            $X1 + $XSpaceLeft,
8502                                            $Y + $YOffset,
8503                                            $X2 - $XSpaceRight,
8504                                            $Y + $YOffset + $YSpan,
8505                                            DIRECTION_VERTICAL,
8506                                            $GradientColor1
8507                                        );
8508                                        $this->drawGradientArea(
8509                                            $X1 + $XSpaceLeft,
8510                                            $Y + $YOffset + $YSpan,
8511                                            $X2 - $XSpaceRight,
8512                                            $Y + $YOffset + $YSize,
8513                                            DIRECTION_VERTICAL,
8514                                            $GradientColor2
8515                                        );
8516                                    }
8517                                    $this->Shadow = $RestoreShadow;
8518                                }
8519                            }
8520
8521                            if ($DisplayValues) {
8522                                $BarWidth = abs($X2 - $X1) - $FontFactor;
8523                                $BarHeight = $YSize + ($YOffset / 2) - $FontFactor / 2;
8524                                $Caption = $this->scaleFormat(
8525                                    round($Serie["Data"][$Key], $DisplayRound),
8526                                    $Mode,
8527                                    $Format,
8528                                    $Unit
8529                                );
8530                                $TxtPos = $this->getTextBox(0, 0, $DisplayFont, $DisplaySize, 0, $Caption);
8531                                $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]);
8532                                $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]);
8533
8534                                $XCenter = ($X2 - $X1) / 2 + $X1;
8535                                $YCenter = (($Y + $YOffset + $YSize) - ($Y + $YOffset)) / 2 + $Y + $YOffset;
8536
8537                                $Done = false;
8538                                if ($DisplayOrientation == ORIENTATION_HORIZONTAL
8539                                    || $DisplayOrientation == ORIENTATION_AUTO
8540                                ) {
8541                                    if ($TxtHeight < $BarHeight && $TxtWidth < $BarWidth) {
8542                                        $this->drawText(
8543                                            $XCenter,
8544                                            $YCenter,
8545                                            $this->scaleFormat(
8546                                                $Serie["Data"][$Key],
8547                                                $Mode,
8548                                                $Format,
8549                                                $Unit
8550                                            ),
8551                                            [
8552                                                "R" => $DisplayR,
8553                                                "G" => $DisplayG,
8554                                                "B" => $DisplayB,
8555                                                "Align" => TEXT_ALIGN_MIDDLEMIDDLE,
8556                                                "FontSize" => $DisplaySize,
8557                                                "FontName" => $DisplayFont
8558                                            ]
8559                                        );
8560                                        $Done = true;
8561                                    }
8562                                }
8563
8564                                if ($DisplayOrientation == ORIENTATION_VERTICAL
8565                                    || ($DisplayOrientation == ORIENTATION_AUTO && !$Done)
8566                                ) {
8567                                    if ($TxtHeight < $BarWidth && $TxtWidth < $BarHeight) {
8568                                        $this->drawText(
8569                                            $XCenter,
8570                                            $YCenter,
8571                                            $this->scaleFormat(
8572                                                $Serie["Data"][$Key],
8573                                                $Mode,
8574                                                $Format,
8575                                                $Unit
8576                                            ),
8577                                            [
8578                                                "R" => $DisplayR,
8579                                                "G" => $DisplayG,
8580                                                "B" => $DisplayB,
8581                                                "Angle" => 90,
8582                                                "Align" => TEXT_ALIGN_MIDDLEMIDDLE,
8583                                                "FontSize" => $DisplaySize,
8584                                                "FontName" => $DisplayFont
8585                                            ]
8586                                        );
8587                                    }
8588                                }
8589                            }
8590
8591                            $LastX[$Key][$Pos] = $X2;
8592                        }
8593
8594                        $Y = $Y + $YStep;
8595                    }
8596                }
8597            }
8598        }
8599    }
8600
8601    /**
8602     * Draw a stacked area chart
8603     * @param array $Format
8604     */
8605    public function drawStackedAreaChart(array $Format = [])
8606    {
8607        $DrawLine = isset($Format["DrawLine"]) ? $Format["DrawLine"] : false;
8608        $LineSurrounding = isset($Format["LineSurrounding"]) ? $Format["LineSurrounding"] : null;
8609        $LineR = isset($Format["LineR"]) ? $Format["LineR"] : VOID;
8610        $LineG = isset($Format["LineG"]) ? $Format["LineG"] : VOID;
8611        $LineB = isset($Format["LineB"]) ? $Format["LineB"] : VOID;
8612        $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 100;
8613        $DrawPlot = isset($Format["DrawPlot"]) ? $Format["DrawPlot"] : false;
8614        $PlotRadius = isset($Format["PlotRadius"]) ? $Format["PlotRadius"] : 2;
8615        $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : 1;
8616        $PlotBorderSurrounding = isset($Format["PlotBorderSurrounding"]) ? $Format["PlotBorderSurrounding"] : null;
8617        $PlotBorderR = isset($Format["PlotBorderR"]) ? $Format["PlotBorderR"] : 0;
8618        $PlotBorderG = isset($Format["PlotBorderG"]) ? $Format["PlotBorderG"] : 0;
8619        $PlotBorderB = isset($Format["PlotBorderB"]) ? $Format["PlotBorderB"] : 0;
8620        $PlotBorderAlpha = isset($Format["PlotBorderAlpha"]) ? $Format["PlotBorderAlpha"] : 50;
8621        $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : null;
8622
8623        $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED;
8624
8625        $Data = $this->DataSet->getData();
8626        list($XMargin, $XDivs) = $this->scaleGetXSettings();
8627
8628        $RestoreShadow = $this->Shadow;
8629        $this->Shadow = false;
8630
8631        /* Build the offset data series */
8632        $OverallOffset = [];
8633        $SerieOrder = [];
8634        foreach ($Data["Series"] as $SerieName => $Serie) {
8635            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
8636                $SerieOrder[] = $SerieName;
8637
8638                foreach ($Serie["Data"] as $Key => $Value) {
8639                    if ($Value == VOID) {
8640                        $Value = 0;
8641                    }
8642                    if ($Value >= 0) {
8643                        $Sign = "+";
8644                    } else {
8645                        $Sign = "-";
8646                    }
8647                    if (!isset($OverallOffset[$Key]) || !isset($OverallOffset[$Key][$Sign])) {
8648                        $OverallOffset[$Key][$Sign] = 0;
8649                    }
8650
8651                    if ($Sign == "+") {
8652                        $Data["Series"][$SerieName]["Data"][$Key] = $Value + $OverallOffset[$Key][$Sign];
8653                    } else {
8654                        $Data["Series"][$SerieName]["Data"][$Key] = $Value - $OverallOffset[$Key][$Sign];
8655                    }
8656
8657                    $OverallOffset[$Key][$Sign] = $OverallOffset[$Key][$Sign] + abs($Value);
8658                }
8659            }
8660        }
8661        $SerieOrder = array_reverse($SerieOrder);
8662
8663        foreach ($SerieOrder as $Key => $SerieName) {
8664            $Serie = $Data["Series"][$SerieName];
8665            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
8666                $R = $Serie["Color"]["R"];
8667                $G = $Serie["Color"]["G"];
8668                $B = $Serie["Color"]["B"];
8669                $Alpha = $Serie["Color"]["Alpha"];
8670                $Ticks = $Serie["Ticks"];
8671                if ($ForceTransparency != null) {
8672                    $Alpha = $ForceTransparency;
8673                }
8674
8675                $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha];
8676                if ($LineSurrounding != null) {
8677                    $LineColor = [
8678                        "R" => $R + $LineSurrounding,
8679                        "G" => $G + $LineSurrounding,
8680                        "B" => $B + $LineSurrounding,
8681                        "Alpha" => $Alpha
8682                    ];
8683                } elseif ($LineR != VOID) {
8684                    $LineColor = [
8685                        "R" => $LineR,
8686                        "G" => $LineG,
8687                        "B" => $LineB,
8688                        "Alpha" => $LineAlpha
8689                    ];
8690                } else {
8691                    $LineColor = $Color;
8692                }
8693                if ($PlotBorderSurrounding != null) {
8694                    $PlotBorderColor = [
8695                        "R" => $R + $PlotBorderSurrounding,
8696                        "G" => $G + $PlotBorderSurrounding,
8697                        "B" => $B + $PlotBorderSurrounding,
8698                        "Alpha" => $PlotBorderAlpha
8699                    ];
8700                } else {
8701                    $PlotBorderColor = [
8702                        "R" => $PlotBorderR,
8703                        "G" => $PlotBorderG,
8704                        "B" => $PlotBorderB,
8705                        "Alpha" => $PlotBorderAlpha
8706                    ];
8707                }
8708                $AxisID = $Serie["Axis"];
8709                $Format = $Data["Axis"][$AxisID]["Format"];
8710
8711                $PosArray = $this->scaleComputeY(
8712                    $Serie["Data"],
8713                    ["AxisID" => $Serie["Axis"]],
8714                    true
8715                );
8716                $YZero = $this->scaleComputeY(0, ["AxisID" => $Serie["Axis"]]);
8717
8718                $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
8719
8720                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
8721                    if ($YZero < $this->GraphAreaY1 + 1) {
8722                        $YZero = $this->GraphAreaY1 + 1;
8723                    }
8724                    if ($YZero > $this->GraphAreaY2 - 1) {
8725                        $YZero = $this->GraphAreaY2 - 1;
8726                    }
8727
8728                    if ($XDivs == 0) {
8729                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
8730                    } else {
8731                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
8732                    }
8733                    $X = $this->GraphAreaX1 + $XMargin;
8734
8735                    if (!is_array($PosArray)) {
8736                        $Value = $PosArray;
8737                        $PosArray = [];
8738                        $PosArray[0] = $Value;
8739                    }
8740
8741                    $Plots = [];
8742                    $Plots[] = $X;
8743                    $Plots[] = $YZero;
8744                    foreach ($PosArray as $Key => $Height) {
8745                        if ($Height != VOID) {
8746                            $Plots[] = $X;
8747                            $Plots[] = $YZero - $Height;
8748                        }
8749                        $X = $X + $XStep;
8750                    }
8751                    $Plots[] = $X - $XStep;
8752                    $Plots[] = $YZero;
8753
8754                    $this->drawPolygon($Plots, $Color);
8755
8756                    $this->Shadow = $RestoreShadow;
8757                    if ($DrawLine) {
8758                        for ($i = 2; $i <= count($Plots) - 6; $i = $i + 2) {
8759                            $this->drawLine(
8760                                $Plots[$i],
8761                                $Plots[$i + 1],
8762                                $Plots[$i + 2],
8763                                $Plots[$i + 3],
8764                                $LineColor
8765                            );
8766                        }
8767                    }
8768                    if ($DrawPlot) {
8769                        for ($i = 2; $i <= count($Plots) - 4; $i = $i + 2) {
8770                            if ($PlotBorder != 0) {
8771                                $this->drawFilledCircle(
8772                                    $Plots[$i],
8773                                    $Plots[$i + 1],
8774                                    $PlotRadius + $PlotBorder,
8775                                    $PlotBorderColor
8776                                );
8777                            }
8778                            $this->drawFilledCircle($Plots[$i], $Plots[$i + 1], $PlotRadius, $Color);
8779                        }
8780                    }
8781                    $this->Shadow = false;
8782                } elseif ($Data["Orientation"] == SCALE_POS_TOPBOTTOM) {
8783                    if ($YZero < $this->GraphAreaX1 + 1) {
8784                        $YZero = $this->GraphAreaX1 + 1;
8785                    }
8786                    if ($YZero > $this->GraphAreaX2 - 1) {
8787                        $YZero = $this->GraphAreaX2 - 1;
8788                    }
8789
8790                    if ($XDivs == 0) {
8791                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
8792                    } else {
8793                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
8794                    }
8795                    $Y = $this->GraphAreaY1 + $XMargin;
8796
8797                    if (!is_array($PosArray)) {
8798                        $Value = $PosArray;
8799                        $PosArray = [];
8800                        $PosArray[0] = $Value;
8801                    }
8802
8803                    $Plots = [];
8804                    $Plots[] = $YZero;
8805                    $Plots[] = $Y;
8806                    foreach ($PosArray as $Key => $Height) {
8807                        if ($Height != VOID) {
8808                            $Plots[] = $YZero + $Height;
8809                            $Plots[] = $Y;
8810                        }
8811                        $Y = $Y + $YStep;
8812                    }
8813                    $Plots[] = $YZero;
8814                    $Plots[] = $Y - $YStep;
8815
8816                    $this->drawPolygon($Plots, $Color);
8817
8818                    $this->Shadow = $RestoreShadow;
8819                    if ($DrawLine) {
8820                        for ($i = 2; $i <= count($Plots) - 6; $i = $i + 2) {
8821                            $this->drawLine(
8822                                $Plots[$i],
8823                                $Plots[$i + 1],
8824                                $Plots[$i + 2],
8825                                $Plots[$i + 3],
8826                                $LineColor
8827                            );
8828                        }
8829                    }
8830                    if ($DrawPlot) {
8831                        for ($i = 2; $i <= count($Plots) - 4; $i = $i + 2) {
8832                            if ($PlotBorder != 0) {
8833                                $this->drawFilledCircle(
8834                                    $Plots[$i],
8835                                    $Plots[$i + 1],
8836                                    $PlotRadius + $PlotBorder,
8837                                    $PlotBorderColor
8838                                );
8839                            }
8840
8841                            $this->drawFilledCircle($Plots[$i], $Plots[$i + 1], $PlotRadius, $Color);
8842                        }
8843                    }
8844                    $this->Shadow = false;
8845                }
8846            }
8847        }
8848        $this->Shadow = $RestoreShadow;
8849    }
8850
8851    /**
8852     * Draw the derivative chart associated to the data series
8853     * @param array $Format
8854     */
8855    public function drawDerivative(array $Format = [])
8856    {
8857        $Offset = isset($Format["Offset"]) ? $Format["Offset"] : 10;
8858        $SerieSpacing = isset($Format["SerieSpacing"]) ? $Format["SerieSpacing"] : 3;
8859        $DerivativeHeight = isset($Format["DerivativeHeight"]) ? $Format["DerivativeHeight"] : 4;
8860        $ShadedSlopeBox = isset($Format["ShadedSlopeBox"]) ? $Format["ShadedSlopeBox"] : false;
8861        $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true;
8862        $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255;
8863        $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255;
8864        $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255;
8865        $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 20;
8866        $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : true;
8867        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0;
8868        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0;
8869        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0;
8870        $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100;
8871        $Caption = isset($Format["Caption"]) ? $Format["Caption"] : true;
8872        $CaptionHeight = isset($Format["CaptionHeight"]) ? $Format["CaptionHeight"] : 10;
8873        $CaptionWidth = isset($Format["CaptionWidth"]) ? $Format["CaptionWidth"] : 20;
8874        $CaptionMargin = isset($Format["CaptionMargin"]) ? $Format["CaptionMargin"] : 4;
8875        $CaptionLine = isset($Format["CaptionLine"]) ? $Format["CaptionLine"] : false;
8876        $CaptionBox = isset($Format["CaptionBox"]) ? $Format["CaptionBox"] : false;
8877        $CaptionBorderR = isset($Format["CaptionBorderR"]) ? $Format["CaptionBorderR"] : 0;
8878        $CaptionBorderG = isset($Format["CaptionBorderG"]) ? $Format["CaptionBorderG"] : 0;
8879        $CaptionBorderB = isset($Format["CaptionBorderB"]) ? $Format["CaptionBorderB"] : 0;
8880        $CaptionFillR = isset($Format["CaptionFillR"]) ? $Format["CaptionFillR"] : 255;
8881        $CaptionFillG = isset($Format["CaptionFillG"]) ? $Format["CaptionFillG"] : 255;
8882        $CaptionFillB = isset($Format["CaptionFillB"]) ? $Format["CaptionFillB"] : 255;
8883        $CaptionFillAlpha = isset($Format["CaptionFillAlpha"]) ? $Format["CaptionFillAlpha"] : 80;
8884        $PositiveSlopeStartR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 184;
8885        $PositiveSlopeStartG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 234;
8886        $PositiveSlopeStartB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 88;
8887        $PositiveSlopeEndR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 239;
8888        $PositiveSlopeEndG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 31;
8889        $PositiveSlopeEndB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 36;
8890        $NegativeSlopeStartR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 184;
8891        $NegativeSlopeStartG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 234;
8892        $NegativeSlopeStartB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 88;
8893        $NegativeSlopeEndR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 67;
8894        $NegativeSlopeEndG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 124;
8895        $NegativeSlopeEndB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 227;
8896
8897        $Data = $this->DataSet->getData();
8898
8899        list($XMargin, $XDivs) = $this->scaleGetXSettings();
8900
8901        if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
8902            $YPos = $this->DataSet->Data["GraphArea"]["Y2"] + $Offset;
8903        } else {
8904            $XPos = $this->DataSet->Data["GraphArea"]["X2"] + $Offset;
8905        }
8906        foreach ($Data["Series"] as $SerieName => $Serie) {
8907            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
8908                $R = $Serie["Color"]["R"];
8909                $G = $Serie["Color"]["G"];
8910                $B = $Serie["Color"]["B"];
8911                $Alpha = $Serie["Color"]["Alpha"];
8912                $Ticks = $Serie["Ticks"];
8913                $Weight = $Serie["Weight"];
8914
8915                $AxisID = $Serie["Axis"];
8916                $PosArray = $this->scaleComputeY(
8917                    $Serie["Data"],
8918                    ["AxisID" => $Serie["Axis"]]
8919                );
8920
8921                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
8922                    if ($Caption) {
8923                        if ($CaptionLine) {
8924                            $StartX = floor($this->GraphAreaX1 - $CaptionWidth + $XMargin - $CaptionMargin);
8925                            $EndX = floor($this->GraphAreaX1 - $CaptionMargin + $XMargin);
8926
8927                            $CaptionSettings = [
8928                                "R" => $R,
8929                                "G" => $G,
8930                                "B" => $B,
8931                                "Alpha" => $Alpha,
8932                                "Ticks" => $Ticks,
8933                                "Weight" => $Weight
8934                            ];
8935                            if ($CaptionBox) {
8936                                $this->drawFilledRectangle(
8937                                    $StartX,
8938                                    $YPos,
8939                                    $EndX,
8940                                    $YPos + $CaptionHeight,
8941                                    [
8942                                        "R" => $CaptionFillR,
8943                                        "G" => $CaptionFillG,
8944                                        "B" => $CaptionFillB,
8945                                        "BorderR" => $CaptionBorderR,
8946                                        "BorderG" => $CaptionBorderG,
8947                                        "BorderB" => $CaptionBorderB,
8948                                        "Alpha" => $CaptionFillAlpha
8949                                    ]
8950                                );
8951                            }
8952                            $this->drawLine(
8953                                $StartX + 2,
8954                                $YPos + ($CaptionHeight / 2),
8955                                $EndX - 2,
8956                                $YPos + ($CaptionHeight / 2),
8957                                $CaptionSettings
8958                            );
8959                        } else {
8960                            $this->drawFilledRectangle(
8961                                $this->GraphAreaX1 - $CaptionWidth + $XMargin - $CaptionMargin,
8962                                $YPos,
8963                                $this->GraphAreaX1 - $CaptionMargin + $XMargin,
8964                                $YPos + $CaptionHeight,
8965                                [
8966                                    "R" => $R,
8967                                    "G" => $G,
8968                                    "B" => $B,
8969                                    "BorderR" => $CaptionBorderR,
8970                                    "BorderG" => $CaptionBorderG,
8971                                    "BorderB" => $CaptionBorderB
8972                                ]
8973                            );
8974                        }
8975                    }
8976
8977                    if ($XDivs == 0) {
8978                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
8979                    } else {
8980                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
8981                    }
8982                    $X = $this->GraphAreaX1 + $XMargin;
8983
8984                    $TopY = $YPos + ($CaptionHeight / 2) - ($DerivativeHeight / 2);
8985                    $BottomY = $YPos + ($CaptionHeight / 2) + ($DerivativeHeight / 2);
8986
8987                    $StartX = floor($this->GraphAreaX1 + $XMargin);
8988                    $EndX = floor($this->GraphAreaX2 - $XMargin);
8989
8990                    if ($DrawBackground) {
8991                        $this->drawFilledRectangle(
8992                            $StartX - 1,
8993                            $TopY - 1,
8994                            $EndX + 1,
8995                            $BottomY + 1,
8996                            [
8997                                "R" => $BackgroundR,
8998                                "G" => $BackgroundG,
8999                                "B" => $BackgroundB,
9000                                "Alpha" => $BackgroundAlpha
9001                            ]
9002                        );
9003                    }
9004                    if ($DrawBorder) {
9005                        $this->drawRectangle(
9006                            $StartX - 1,
9007                            $TopY - 1,
9008                            $EndX + 1,
9009                            $BottomY + 1,
9010                            [
9011                                "R" => $BorderR,
9012                                "G" => $BorderG,
9013                                "B" => $BorderB,
9014                                "Alpha" => $BorderAlpha
9015                            ]
9016                        );
9017                    }
9018
9019                    if (!is_array($PosArray)) {
9020                        $Value = $PosArray;
9021                        $PosArray = [];
9022                        $PosArray[0] = $Value;
9023                    }
9024
9025                    $RestoreShadow = $this->Shadow;
9026                    $this->Shadow = false;
9027
9028                    /* Determine the Max slope index */
9029                    $LastX = null;
9030                    $LastY = null;
9031                    $MinSlope = 0;
9032                    $MaxSlope = 1;
9033                    foreach ($PosArray as $Key => $Y) {
9034                        if ($Y != VOID && $LastX != null) {
9035                            $Slope = ($LastY - $Y);
9036                            if ($Slope > $MaxSlope) {
9037                                $MaxSlope = $Slope;
9038                            } if ($Slope < $MinSlope) {
9039                                $MinSlope = $Slope;
9040                            }
9041                        }
9042
9043                        if ($Y == VOID) {
9044                            $LastX = null;
9045                            $LastY = null;
9046                        } else {
9047                            $LastX = $X;
9048                            $LastY = $Y;
9049                        }
9050                    }
9051
9052                    $LastX = null;
9053                    $LastY = null;
9054                    $LastColor = null;
9055                    foreach ($PosArray as $Key => $Y) {
9056                        if ($Y != VOID && $LastY != null) {
9057                            $Slope = ($LastY - $Y);
9058
9059                            if ($Slope >= 0) {
9060                                $SlopeIndex = (100 / $MaxSlope) * $Slope;
9061                                $R = (($PositiveSlopeEndR - $PositiveSlopeStartR) / 100)
9062                                    * $SlopeIndex + $PositiveSlopeStartR
9063                                ;
9064                                $G = (($PositiveSlopeEndG - $PositiveSlopeStartG) / 100)
9065                                    * $SlopeIndex + $PositiveSlopeStartG
9066                                ;
9067                                $B = (($PositiveSlopeEndB - $PositiveSlopeStartB) / 100)
9068                                    * $SlopeIndex + $PositiveSlopeStartB
9069                                ;
9070                            } elseif ($Slope < 0) {
9071                                $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope);
9072                                $R = (($NegativeSlopeEndR - $NegativeSlopeStartR) / 100)
9073                                    * $SlopeIndex + $NegativeSlopeStartR
9074                                ;
9075                                $G = (($NegativeSlopeEndG - $NegativeSlopeStartG) / 100)
9076                                    * $SlopeIndex + $NegativeSlopeStartG
9077                                ;
9078                                $B = (($NegativeSlopeEndB - $NegativeSlopeStartB) / 100)
9079                                    * $SlopeIndex + $NegativeSlopeStartB
9080                                ;
9081                            }
9082
9083                            $Color = ["R" => $R, "G" => $G, "B" => $B];
9084
9085                            if ($ShadedSlopeBox && $LastColor != null) {// && $Slope != 0
9086                                $GradientSettings = [
9087                                    "StartR" => $LastColor["R"],
9088                                    "StartG" => $LastColor["G"],
9089                                    "StartB" => $LastColor["B"],
9090                                    "EndR" => $R,
9091                                    "EndG" => $G,
9092                                    "EndB" => $B
9093                                ];
9094                                $this->drawGradientArea(
9095                                    $LastX,
9096                                    $TopY,
9097                                    $X,
9098                                    $BottomY,
9099                                    DIRECTION_HORIZONTAL,
9100                                    $GradientSettings
9101                                );
9102                            } elseif (!$ShadedSlopeBox || $LastColor == null) { // || $Slope == 0
9103                                $this->drawFilledRectangle(
9104                                    floor($LastX),
9105                                    $TopY,
9106                                    floor($X),
9107                                    $BottomY,
9108                                    $Color
9109                                );
9110                            }
9111                            $LastColor = $Color;
9112                        }
9113
9114                        if ($Y == VOID) {
9115                            $LastY = null;
9116                        } else {
9117                            $LastX = $X;
9118                            $LastY = $Y;
9119                        }
9120
9121                        $X = $X + $XStep;
9122                    }
9123
9124                    $YPos = $YPos + $CaptionHeight + $SerieSpacing;
9125                } else {
9126                    if ($Caption) {
9127                        $StartY = floor($this->GraphAreaY1 - $CaptionWidth + $XMargin - $CaptionMargin);
9128                        $EndY = floor($this->GraphAreaY1 - $CaptionMargin + $XMargin);
9129                        if ($CaptionLine) {
9130                            $CaptionSettings = [
9131                                "R" => $R,
9132                                "G" => $G,
9133                                "B" => $B,
9134                                "Alpha" => $Alpha,
9135                                "Ticks" => $Ticks,
9136                                "Weight" => $Weight
9137                            ];
9138                            if ($CaptionBox) {
9139                                $this->drawFilledRectangle(
9140                                    $XPos,
9141                                    $StartY,
9142                                    $XPos + $CaptionHeight,
9143                                    $EndY,
9144                                    [
9145                                        "R" => $CaptionFillR,
9146                                        "G" => $CaptionFillG,
9147                                        "B" => $CaptionFillB,
9148                                        "BorderR" => $CaptionBorderR,
9149                                        "BorderG" => $CaptionBorderG,
9150                                        "BorderB" => $CaptionBorderB,
9151                                        "Alpha" => $CaptionFillAlpha
9152                                    ]
9153                                );
9154                            }
9155                            $this->drawLine(
9156                                $XPos + ($CaptionHeight / 2),
9157                                $StartY + 2,
9158                                $XPos + ($CaptionHeight / 2),
9159                                $EndY - 2,
9160                                $CaptionSettings
9161                            );
9162                        } else {
9163                            $this->drawFilledRectangle(
9164                                $XPos,
9165                                $StartY,
9166                                $XPos + $CaptionHeight,
9167                                $EndY,
9168                                [
9169                                    "R" => $R,
9170                                    "G" => $G,
9171                                    "B" => $B,
9172                                    "BorderR" => $CaptionBorderR,
9173                                    "BorderG" => $CaptionBorderG,
9174                                    "BorderB" => $CaptionBorderB
9175                                ]
9176                            );
9177                        }
9178                    }
9179
9180
9181                    if ($XDivs == 0) {
9182                        $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
9183                    } else {
9184                        $XStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
9185                    }
9186                    $Y = $this->GraphAreaY1 + $XMargin;
9187
9188                    $TopX = $XPos + ($CaptionHeight / 2) - ($DerivativeHeight / 2);
9189                    $BottomX = $XPos + ($CaptionHeight / 2) + ($DerivativeHeight / 2);
9190
9191                    $StartY = floor($this->GraphAreaY1 + $XMargin);
9192                    $EndY = floor($this->GraphAreaY2 - $XMargin);
9193
9194                    if ($DrawBackground) {
9195                        $this->drawFilledRectangle(
9196                            $TopX - 1,
9197                            $StartY - 1,
9198                            $BottomX + 1,
9199                            $EndY + 1,
9200                            [
9201                                "R" => $BackgroundR,
9202                                "G" => $BackgroundG,
9203                                "B" => $BackgroundB,
9204                                "Alpha" => $BackgroundAlpha
9205                            ]
9206                        );
9207                    }
9208                    if ($DrawBorder) {
9209                        $this->drawRectangle(
9210                            $TopX - 1,
9211                            $StartY - 1,
9212                            $BottomX + 1,
9213                            $EndY + 1,
9214                            [
9215                                "R" => $BorderR,
9216                                "G" => $BorderG,
9217                                "B" => $BorderB,
9218                                "Alpha" => $BorderAlpha
9219                            ]
9220                        );
9221                    }
9222
9223                    if (!is_array($PosArray)) {
9224                        $Value = $PosArray;
9225                        $PosArray = [];
9226                        $PosArray[0] = $Value;
9227                    }
9228
9229                    $RestoreShadow = $this->Shadow;
9230                    $this->Shadow = false;
9231
9232                    /* Determine the Max slope index */
9233                    $LastX = null;
9234                    $LastY = null;
9235                    $MinSlope = 0;
9236                    $MaxSlope = 1;
9237                    foreach ($PosArray as $Key => $X) {
9238                        if ($X != VOID && $LastX != null) {
9239                            $Slope = ($X - $LastX);
9240                            if ($Slope > $MaxSlope) {
9241                                $MaxSlope = $Slope;
9242                            }
9243                            if ($Slope < $MinSlope) {
9244                                $MinSlope = $Slope;
9245                            }
9246                        }
9247
9248                        if ($X == VOID) {
9249                            $LastX = null;
9250                        } else {
9251                            $LastX = $X;
9252                        }
9253                    }
9254
9255                    $LastX = null;
9256                    $LastY = null;
9257                    $LastColor = null;
9258                    foreach ($PosArray as $Key => $X) {
9259                        if ($X != VOID && $LastX != null) {
9260                            $Slope = ($X - $LastX);
9261
9262                            if ($Slope >= 0) {
9263                                $SlopeIndex = (100 / $MaxSlope) * $Slope;
9264                                $R = (($PositiveSlopeEndR - $PositiveSlopeStartR) / 100)
9265                                    * $SlopeIndex + $PositiveSlopeStartR
9266                                ;
9267                                $G = (($PositiveSlopeEndG - $PositiveSlopeStartG) / 100)
9268                                    * $SlopeIndex + $PositiveSlopeStartG
9269                                ;
9270                                $B = (($PositiveSlopeEndB - $PositiveSlopeStartB) / 100)
9271                                    * $SlopeIndex + $PositiveSlopeStartB
9272                                ;
9273                            } elseif ($Slope < 0) {
9274                                $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope);
9275                                $R = (($NegativeSlopeEndR - $NegativeSlopeStartR) / 100)
9276                                    * $SlopeIndex + $NegativeSlopeStartR
9277                                ;
9278                                $G = (($NegativeSlopeEndG - $NegativeSlopeStartG) / 100)
9279                                    * $SlopeIndex + $NegativeSlopeStartG
9280                                ;
9281                                $B = (($NegativeSlopeEndB - $NegativeSlopeStartB) / 100)
9282                                    * $SlopeIndex + $NegativeSlopeStartB
9283                                ;
9284                            }
9285
9286                            $Color = ["R" => $R, "G" => $G, "B" => $B];
9287
9288                            if ($ShadedSlopeBox && $LastColor != null) {
9289                                $GradientSettings = [
9290                                    "StartR" => $LastColor["R"],
9291                                    "StartG" => $LastColor["G"],
9292                                    "StartB" => $LastColor["B"],
9293                                    "EndR" => $R,
9294                                    "EndG" => $G,
9295                                    "EndB" => $B
9296                                ];
9297
9298                                $this->drawGradientArea(
9299                                    $TopX,
9300                                    $LastY,
9301                                    $BottomX,
9302                                    $Y,
9303                                    DIRECTION_VERTICAL,
9304                                    $GradientSettings
9305                                );
9306                            } elseif (!$ShadedSlopeBox || $LastColor == null) {
9307                                $this->drawFilledRectangle(
9308                                    $TopX,
9309                                    floor($LastY),
9310                                    $BottomX,
9311                                    floor($Y),
9312                                    $Color
9313                                );
9314                            }
9315                            $LastColor = $Color;
9316                        }
9317
9318                        if ($X == VOID) {
9319                            $LastX = null;
9320                        } else {
9321                            $LastX = $X;
9322                            $LastY = $Y;
9323                        }
9324
9325                        $Y = $Y + $XStep;
9326                    }
9327
9328                    $XPos = $XPos + $CaptionHeight + $SerieSpacing;
9329                }
9330                $this->Shadow = $RestoreShadow;
9331            }
9332        }
9333    }
9334
9335    /**
9336     * Draw the line of best fit
9337     * @param array $Format
9338     */
9339    public function drawBestFit(array $Format = [])
9340    {
9341        $OverrideTicks = isset($Format["Ticks"]) ? $Format["Ticks"] : null;
9342        $OverrideR = isset($Format["R"]) ? $Format["R"] : VOID;
9343        $OverrideG = isset($Format["G"]) ? $Format["G"] : VOID;
9344        $OverrideB = isset($Format["B"]) ? $Format["B"] : VOID;
9345        $OverrideAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : VOID;
9346
9347        $Data = $this->DataSet->getData();
9348        list($XMargin, $XDivs) = $this->scaleGetXSettings();
9349
9350        foreach ($Data["Series"] as $SerieName => $Serie) {
9351            if ($Serie["isDrawable"] == true && $SerieName != $Data["Abscissa"]) {
9352                if ($OverrideR != VOID && $OverrideG != VOID && $OverrideB != VOID) {
9353                    $R = $OverrideR;
9354                    $G = $OverrideG;
9355                    $B = $OverrideB;
9356                } else {
9357                    $R = $Serie["Color"]["R"];
9358                    $G = $Serie["Color"]["G"];
9359                    $B = $Serie["Color"]["B"];
9360                }
9361                if ($OverrideTicks == null) {
9362                    $Ticks = $Serie["Ticks"];
9363                } else {
9364                    $Ticks = $OverrideTicks;
9365                }
9366                if ($OverrideAlpha == VOID) {
9367                    $Alpha = $Serie["Color"]["Alpha"];
9368                } else {
9369                    $Alpha = $OverrideAlpha;
9370                }
9371
9372                $Color = ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha, "Ticks" => $Ticks];
9373
9374                $PosArray = $this->scaleComputeY(
9375                    $Serie["Data"],
9376                    ["AxisID" => $Serie["Axis"]]
9377                );
9378
9379                if ($Data["Orientation"] == SCALE_POS_LEFTRIGHT) {
9380                    if ($XDivs == 0) {
9381                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1) / 4;
9382                    } else {
9383                        $XStep = ($this->GraphAreaX2 - $this->GraphAreaX1 - $XMargin * 2) / $XDivs;
9384                    }
9385                    $X = $this->GraphAreaX1 + $XMargin;
9386
9387                    if (!is_array($PosArray)) {
9388                        $Value = $PosArray;
9389                        $PosArray = [];
9390                        $PosArray[0] = $Value;
9391                    }
9392                    $Sxy = 0;
9393                    $Sx = 0;
9394                    $Sy = 0;
9395                    $Sxx = 0;
9396                    foreach ($PosArray as $Key => $Y) {
9397                        if ($Y != VOID) {
9398                            $Sxy = $Sxy + $X * $Y;
9399                            $Sx = $Sx + $X;
9400                            $Sy = $Sy + $Y;
9401                            $Sxx = $Sxx + $X * $X;
9402                        }
9403
9404                        $X = $X + $XStep;
9405                    }
9406                    $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray);
9407                    $M = (($n * $Sxy) - ($Sx * $Sy)) / (($n * $Sxx) - ($Sx * $Sx));
9408                    $B = (($Sy) - ($M * $Sx)) / ($n);
9409
9410                    $X1 = $this->GraphAreaX1 + $XMargin;
9411                    $Y1 = $M * $X1 + $B;
9412                    $X2 = $this->GraphAreaX2 - $XMargin;
9413                    $Y2 = $M * $X2 + $B;
9414
9415                    if ($Y1 < $this->GraphAreaY1) {
9416                        $X1 = $X1 + ($this->GraphAreaY1 - $Y1);
9417                        $Y1 = $this->GraphAreaY1;
9418                    }
9419                    if ($Y1 > $this->GraphAreaY2) {
9420                        $X1 = $X1 + ($Y1 - $this->GraphAreaY2);
9421                        $Y1 = $this->GraphAreaY2;
9422                    }
9423                    if ($Y2 < $this->GraphAreaY1) {
9424                        $X2 = $X2 - ($this->GraphAreaY1 - $Y2);
9425                        $Y2 = $this->GraphAreaY1;
9426                    }
9427                    if ($Y2 > $this->GraphAreaY2) {
9428                        $X2 = $X2 - ($Y2 - $this->GraphAreaY2);
9429                        $Y2 = $this->GraphAreaY2;
9430                    }
9431
9432                    $this->drawLine($X1, $Y1, $X2, $Y2, $Color);
9433                } else {
9434                    if ($XDivs == 0) {
9435                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1) / 4;
9436                    } else {
9437                        $YStep = ($this->GraphAreaY2 - $this->GraphAreaY1 - $XMargin * 2) / $XDivs;
9438                    }
9439                    $Y = $this->GraphAreaY1 + $XMargin;
9440
9441                    if (!is_array($PosArray)) {
9442                        $Value = $PosArray;
9443                        $PosArray = [];
9444                        $PosArray[0] = $Value;
9445                    }
9446                    $Sxy = 0;
9447                    $Sx = 0;
9448                    $Sy = 0;
9449                    $Sxx = 0;
9450                    foreach ($PosArray as $Key => $X) {
9451                        if ($X != VOID) {
9452                            $Sxy = $Sxy + $X * $Y;
9453                            $Sx = $Sx + $Y;
9454                            $Sy = $Sy + $X;
9455                            $Sxx = $Sxx + $Y * $Y;
9456                        }
9457
9458                        $Y = $Y + $YStep;
9459                    }
9460                    $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray);
9461                    $M = (($n * $Sxy) - ($Sx * $Sy)) / (($n * $Sxx) - ($Sx * $Sx));
9462                    $B = (($Sy) - ($M * $Sx)) / ($n);
9463
9464                    $Y1 = $this->GraphAreaY1 + $XMargin;
9465                    $X1 = $M * $Y1 + $B;
9466                    $Y2 = $this->GraphAreaY2 - $XMargin;
9467                    $X2 = $M * $Y2 + $B;
9468
9469                    if ($X1 < $this->GraphAreaX1) {
9470                        $Y1 = $Y1 + ($this->GraphAreaX1 - $X1);
9471                        $X1 = $this->GraphAreaX1;
9472                    }
9473                    if ($X1 > $this->GraphAreaX2) {
9474                        $Y1 = $Y1 + ($X1 - $this->GraphAreaX2);
9475                        $X1 = $this->GraphAreaX2;
9476                    }
9477                    if ($X2 < $this->GraphAreaX1) {
9478                        $Y2 = $Y2 - ($this->GraphAreaY1 - $X2);
9479                        $X2 = $this->GraphAreaX1;
9480                    }
9481                    if ($X2 > $this->GraphAreaX2) {
9482                        $Y2 = $Y2 - ($X2 - $this->GraphAreaX2);
9483                        $X2 = $this->GraphAreaX2;
9484                    }
9485
9486                    $this->drawLine($X1, $Y1, $X2, $Y2, $Color);
9487                }
9488            }
9489        }
9490    }
9491
9492    /**
9493     * Draw a label box
9494     * @param int $X
9495     * @param int $Y
9496     * @param string $Title
9497     * @param array $Captions
9498     * @param array $Format
9499     */
9500    public function drawLabelBox($X, $Y, $Title, array $Captions, array $Format = [])
9501    {
9502        $NoTitle = isset($Format["NoTitle"]) ? $Format["NoTitle"] : null;
9503        $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 50;
9504        $DrawSerieColor = isset($Format["DrawSerieColor"]) ? $Format["DrawSerieColor"] : true;
9505        $SerieBoxSize = isset($Format["SerieBoxSize"]) ? $Format["SerieBoxSize"] : 6;
9506        $SerieBoxSpacing = isset($Format["SerieBoxSpacing"]) ? $Format["SerieBoxSpacing"] : 4;
9507        $VerticalMargin = isset($Format["VerticalMargin"]) ? $Format["VerticalMargin"] : 10;
9508        $HorizontalMargin = isset($Format["HorizontalMargin"]) ? $Format["HorizontalMargin"] : 8;
9509        $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR;
9510        $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG;
9511        $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB;
9512        $FontName = isset($Format["FontName"]) ? $this->loadFont($Format["FontName"], 'fonts') : $this->FontName;
9513        $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
9514        $TitleMode = isset($Format["TitleMode"]) ? $Format["TitleMode"] : LABEL_TITLE_NOBACKGROUND;
9515        $TitleR = isset($Format["TitleR"]) ? $Format["TitleR"] : $R;
9516        $TitleG = isset($Format["TitleG"]) ? $Format["TitleG"] : $G;
9517        $TitleB = isset($Format["TitleB"]) ? $Format["TitleB"] : $B;
9518        $TitleBackgroundR = isset($Format["TitleBackgroundR"]) ? $Format["TitleBackgroundR"] : 0;
9519        $TitleBackgroundG = isset($Format["TitleBackgroundG"]) ? $Format["TitleBackgroundG"] : 0;
9520        $TitleBackgroundB = isset($Format["TitleBackgroundB"]) ? $Format["TitleBackgroundB"] : 0;
9521        $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255;
9522        $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255;
9523        $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255;
9524        $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 220;
9525        $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 220;
9526        $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 220;
9527        $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 100;
9528
9529        if (!$DrawSerieColor) {
9530            $SerieBoxSize = 0;
9531            $SerieBoxSpacing = 0;
9532        }
9533
9534        $TxtPos = $this->getTextBox($X, $Y, $FontName, $FontSize, 0, $Title);
9535        $TitleWidth = ($TxtPos[1]["X"] - $TxtPos[0]["X"]) + $VerticalMargin * 2;
9536        $TitleHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]);
9537
9538        if ($NoTitle) {
9539            $TitleWidth = 0;
9540            $TitleHeight = 0;
9541        }
9542
9543        $CaptionWidth = 0;
9544        $CaptionHeight = -$HorizontalMargin;
9545        foreach ($Captions as $Key => $Caption) {
9546            $TxtPos = $this->getTextBox(
9547                $X,
9548                $Y,
9549                $FontName,
9550                $FontSize,
9551                0,
9552                $Caption["Caption"]
9553            );
9554            $CaptionWidth = max(
9555                $CaptionWidth,
9556                ($TxtPos[1]["X"] - $TxtPos[0]["X"]) + $VerticalMargin * 2
9557            );
9558            $CaptionHeight = $CaptionHeight
9559                + max(($TxtPos[0]["Y"] - $TxtPos[2]["Y"]), ($SerieBoxSize + 2))
9560                + $HorizontalMargin
9561            ;
9562        }
9563
9564        if ($CaptionHeight <= 5) {
9565            $CaptionHeight = $CaptionHeight + $HorizontalMargin / 2;
9566        }
9567
9568        if ($DrawSerieColor) {
9569            $CaptionWidth = $CaptionWidth + $SerieBoxSize + $SerieBoxSpacing;
9570        }
9571
9572        $BoxWidth = max($BoxWidth, $TitleWidth, $CaptionWidth);
9573
9574        $XMin = $X - 5 - floor(($BoxWidth - 10) / 2);
9575        $XMax = $X + 5 + floor(($BoxWidth - 10) / 2);
9576
9577        $RestoreShadow = $this->Shadow;
9578        if ($this->Shadow == true) {
9579            $this->Shadow = false;
9580
9581            $Poly = [];
9582            $Poly[] = $X + $this->ShadowX;
9583            $Poly[] = $Y + $this->ShadowX;
9584            $Poly[] = $X + 5 + $this->ShadowX;
9585            $Poly[] = $Y - 5 + $this->ShadowX;
9586            $Poly[] = $XMax + $this->ShadowX;
9587            $Poly[] = $Y - 5 + $this->ShadowX;
9588
9589            if ($NoTitle) {
9590                $Poly[] = $XMax + $this->ShadowX;
9591                $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2 + $this->ShadowX;
9592                $Poly[] = $XMin + $this->ShadowX;
9593                $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2 + $this->ShadowX;
9594            } else {
9595                $Poly[] = $XMax + $this->ShadowX;
9596                $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3 + $this->ShadowX;
9597                $Poly[] = $XMin + $this->ShadowX;
9598                $Poly[] = $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3 + $this->ShadowX;
9599            }
9600
9601            $Poly[] = $XMin + $this->ShadowX;
9602            $Poly[] = $Y - 5 + $this->ShadowX;
9603            $Poly[] = $X - 5 + $this->ShadowX;
9604            $Poly[] = $Y - 5 + $this->ShadowX;
9605            $this->drawPolygon(
9606                $Poly,
9607                [
9608                    "R" => $this->ShadowR,
9609                    "G" => $this->ShadowG,
9610                    "B" => $this->ShadowB,
9611                    "Alpha" => $this->Shadowa
9612                ]
9613            );
9614        }
9615
9616        /* Draw the background */
9617        $GradientSettings = [
9618            "StartR" => $GradientStartR,
9619            "StartG" => $GradientStartG,
9620            "StartB" => $GradientStartB,
9621            "EndR" => $GradientEndR,
9622            "EndG" => $GradientEndG,
9623            "EndB" => $GradientEndB,
9624            "Alpha" => $BoxAlpha
9625        ];
9626        if ($NoTitle) {
9627            $this->drawGradientArea(
9628                $XMin,
9629                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2,
9630                $XMax,
9631                $Y - 6,
9632                DIRECTION_VERTICAL,
9633                $GradientSettings
9634            );
9635        } else {
9636            $this->drawGradientArea(
9637                $XMin,
9638                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9639                $XMax,
9640                $Y - 6,
9641                DIRECTION_VERTICAL,
9642                $GradientSettings
9643            );
9644        }
9645        $Poly = [];
9646        $Poly[] = $X;
9647        $Poly[] = $Y;
9648        $Poly[] = $X - 5;
9649        $Poly[] = $Y - 5;
9650        $Poly[] = $X + 5;
9651        $Poly[] = $Y - 5;
9652        $this->drawPolygon(
9653            $Poly,
9654            [
9655                "R" => $GradientEndR,
9656                "G" => $GradientEndG,
9657                "B" => $GradientEndB,
9658                "Alpha" => $BoxAlpha,
9659                "NoBorder" => true
9660            ]
9661        );
9662
9663        /* Outer border */
9664        $OuterBorderColor = $this->allocateColor($this->Picture, 100, 100, 100, $BoxAlpha);
9665        imageline($this->Picture, $XMin, $Y - 5, $X - 5, $Y - 5, $OuterBorderColor);
9666        imageline($this->Picture, $X, $Y, $X - 5, $Y - 5, $OuterBorderColor);
9667        imageline($this->Picture, $X, $Y, $X + 5, $Y - 5, $OuterBorderColor);
9668        imageline($this->Picture, $X + 5, $Y - 5, $XMax, $Y - 5, $OuterBorderColor);
9669        if ($NoTitle) {
9670            imageline(
9671                $this->Picture,
9672                $XMin,
9673                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2,
9674                $XMin,
9675                $Y - 5,
9676                $OuterBorderColor
9677            );
9678            imageline(
9679                $this->Picture,
9680                $XMax,
9681                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2,
9682                $XMax,
9683                $Y - 5,
9684                $OuterBorderColor
9685            );
9686            imageline(
9687                $this->Picture,
9688                $XMin,
9689                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2,
9690                $XMax,
9691                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2,
9692                $OuterBorderColor
9693            );
9694        } else {
9695            imageline(
9696                $this->Picture,
9697                $XMin,
9698                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9699                $XMin,
9700                $Y - 5,
9701                $OuterBorderColor
9702            );
9703            imageline(
9704                $this->Picture,
9705                $XMax,
9706                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9707                $XMax,
9708                $Y - 5,
9709                $OuterBorderColor
9710            );
9711            imageline(
9712                $this->Picture,
9713                $XMin,
9714                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9715                $XMax,
9716                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9717                $OuterBorderColor
9718            );
9719        }
9720
9721        /* Inner border */
9722        $InnerBorderColor = $this->allocateColor($this->Picture, 255, 255, 255, $BoxAlpha);
9723        imageline($this->Picture, $XMin + 1, $Y - 6, $X - 5, $Y - 6, $InnerBorderColor);
9724        imageline($this->Picture, $X, $Y - 1, $X - 5, $Y - 6, $InnerBorderColor);
9725        imageline($this->Picture, $X, $Y - 1, $X + 5, $Y - 6, $InnerBorderColor);
9726        imageline($this->Picture, $X + 5, $Y - 6, $XMax - 1, $Y - 6, $InnerBorderColor);
9727        if ($NoTitle) {
9728            imageline(
9729                $this->Picture,
9730                $XMin + 1,
9731                $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2,
9732                $XMin + 1,
9733                $Y - 6,
9734                $InnerBorderColor
9735            );
9736            imageline(
9737                $this->Picture,
9738                $XMax - 1,
9739                $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2,
9740                $XMax - 1,
9741                $Y - 6,
9742                $InnerBorderColor
9743            );
9744            imageline(
9745                $this->Picture,
9746                $XMin + 1,
9747                $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2,
9748                $XMax - 1,
9749                $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 2,
9750                $InnerBorderColor
9751            );
9752        } else {
9753            imageline(
9754                $this->Picture,
9755                $XMin + 1,
9756                $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9757                $XMin + 1,
9758                $Y - 6,
9759                $InnerBorderColor
9760            );
9761            imageline(
9762                $this->Picture,
9763                $XMax - 1,
9764                $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9765                $XMax - 1,
9766                $Y - 6,
9767                $InnerBorderColor
9768            );
9769            imageline(
9770                $this->Picture,
9771                $XMin + 1,
9772                $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9773                $XMax - 1,
9774                $Y - 4 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9775                $InnerBorderColor
9776            );
9777        }
9778
9779        /* Draw the separator line */
9780        if ($TitleMode == LABEL_TITLE_NOBACKGROUND && !$NoTitle) {
9781            $YPos = $Y - 7 - $CaptionHeight - $HorizontalMargin - $HorizontalMargin / 2;
9782            $XMargin = $VerticalMargin / 2;
9783            $this->drawLine(
9784                $XMin + $XMargin,
9785                $YPos + 1,
9786                $XMax - $XMargin,
9787                $YPos + 1,
9788                [
9789                    "R" => $GradientEndR,
9790                    "G" => $GradientEndG,
9791                    "B" => $GradientEndB,
9792                    "Alpha" => $BoxAlpha
9793                ]
9794            );
9795            $this->drawLine(
9796                $XMin + $XMargin,
9797                $YPos,
9798                $XMax - $XMargin,
9799                $YPos,
9800                [
9801                    "R" => $GradientStartR,
9802                    "G" => $GradientStartG,
9803                    "B" => $GradientStartB,
9804                    "Alpha" => $BoxAlpha
9805                ]
9806            );
9807        } elseif ($TitleMode == LABEL_TITLE_BACKGROUND) {
9808            $this->drawFilledRectangle(
9809                $XMin,
9810                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin * 3,
9811                $XMax,
9812                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin + $HorizontalMargin / 2,
9813                [
9814                    "R" => $TitleBackgroundR,
9815                    "G" => $TitleBackgroundG,
9816                    "B" => $TitleBackgroundB,
9817                    "Alpha" => $BoxAlpha
9818                ]
9819            );
9820            imageline(
9821                $this->Picture,
9822                $XMin + 1,
9823                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin + $HorizontalMargin / 2 + 1,
9824                $XMax - 1,
9825                $Y - 5 - $TitleHeight - $CaptionHeight - $HorizontalMargin + $HorizontalMargin / 2 + 1,
9826                $InnerBorderColor
9827            );
9828        }
9829
9830        /* Write the description */
9831        if (!$NoTitle) {
9832            $this->drawText(
9833                $XMin + $VerticalMargin,
9834                $Y - 7 - $CaptionHeight - $HorizontalMargin * 2,
9835                $Title,
9836                [
9837                    "Align" => TEXT_ALIGN_BOTTOMLEFT,
9838                    "R" => $TitleR,
9839                    "G" => $TitleG,
9840                    "B" => $TitleB
9841                ]
9842            );
9843        }
9844
9845        /* Write the value */
9846        $YPos = $Y - 5 - $HorizontalMargin;
9847        $XPos = $XMin + $VerticalMargin + $SerieBoxSize + $SerieBoxSpacing;
9848        foreach ($Captions as $Key => $Caption) {
9849            $CaptionTxt = $Caption["Caption"];
9850            $TxtPos = $this->getTextBox($XPos, $YPos, $FontName, $FontSize, 0, $CaptionTxt);
9851            $CaptionHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]);
9852
9853            /* Write the serie color if needed */
9854            if ($DrawSerieColor) {
9855                $BoxSettings = [
9856                    "R" => $Caption["Format"]["R"],
9857                    "G" => $Caption["Format"]["G"],
9858                    "B" => $Caption["Format"]["B"],
9859                    "Alpha" => $Caption["Format"]["Alpha"],
9860                    "BorderR" => 0,
9861                    "BorderG" => 0,
9862                    "BorderB" => 0
9863                ];
9864                $this->drawFilledRectangle(
9865                    $XMin + $VerticalMargin,
9866                    $YPos - $SerieBoxSize,
9867                    $XMin + $VerticalMargin + $SerieBoxSize,
9868                    $YPos,
9869                    $BoxSettings
9870                );
9871            }
9872
9873            $this->drawText($XPos, $YPos, $CaptionTxt, ["Align" => TEXT_ALIGN_BOTTOMLEFT]);
9874
9875            $YPos = $YPos - $CaptionHeight - $HorizontalMargin;
9876        }
9877
9878        $this->Shadow = $RestoreShadow;
9879    }
9880
9881    /**
9882     * Draw a basic shape
9883     * @param int $X
9884     * @param int $Y
9885     * @param int $Shape
9886     * @param int $PlotSize
9887     * @param int $PlotBorder
9888     * @param int $BorderSize
9889     * @param int $R
9890     * @param int $G
9891     * @param int $B
9892     * @param int|float $Alpha
9893     * @param int $BorderR
9894     * @param int $BorderG
9895     * @param int $BorderB
9896     * @param int|float $BorderAlpha
9897     */
9898    public function drawShape(
9899        $X,
9900        $Y,
9901        $Shape,
9902        $PlotSize,
9903        $PlotBorder,
9904        $BorderSize,
9905        $R,
9906        $G,
9907        $B,
9908        $Alpha,
9909        $BorderR,
9910        $BorderG,
9911        $BorderB,
9912        $BorderAlpha
9913    ) {
9914        if ($Shape == SERIE_SHAPE_FILLEDCIRCLE) {
9915            if ($PlotBorder) {
9916                $this->drawFilledCircle(
9917                    $X,
9918                    $Y,
9919                    $PlotSize + $BorderSize,
9920                    ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]
9921                );
9922            }
9923            $this->drawFilledCircle(
9924                $X,
9925                $Y,
9926                $PlotSize,
9927                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
9928            );
9929        } elseif ($Shape == SERIE_SHAPE_FILLEDSQUARE) {
9930            if ($PlotBorder) {
9931                $this->drawFilledRectangle(
9932                    $X - $PlotSize - $BorderSize,
9933                    $Y - $PlotSize - $BorderSize,
9934                    $X + $PlotSize + $BorderSize,
9935                    $Y + $PlotSize + $BorderSize,
9936                    ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]
9937                );
9938            }
9939            $this->drawFilledRectangle(
9940                $X - $PlotSize,
9941                $Y - $PlotSize,
9942                $X + $PlotSize,
9943                $Y + $PlotSize,
9944                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
9945            );
9946        } elseif ($Shape == SERIE_SHAPE_FILLEDTRIANGLE) {
9947            if ($PlotBorder) {
9948                $Pos = [];
9949                $Pos[] = $X;
9950                $Pos[] = $Y - $PlotSize - $BorderSize;
9951                $Pos[] = $X - $PlotSize - $BorderSize;
9952                $Pos[] = $Y + $PlotSize + $BorderSize;
9953                $Pos[] = $X + $PlotSize + $BorderSize;
9954                $Pos[] = $Y + $PlotSize + $BorderSize;
9955                $this->drawPolygon(
9956                    $Pos,
9957                    ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]
9958                );
9959            }
9960
9961            $Pos = [];
9962            $Pos[] = $X;
9963            $Pos[] = $Y - $PlotSize;
9964            $Pos[] = $X - $PlotSize;
9965            $Pos[] = $Y + $PlotSize;
9966            $Pos[] = $X + $PlotSize;
9967            $Pos[] = $Y + $PlotSize;
9968            $this->drawPolygon($Pos, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
9969        } elseif ($Shape == SERIE_SHAPE_TRIANGLE) {
9970            $this->drawLine(
9971                $X,
9972                $Y - $PlotSize,
9973                $X - $PlotSize,
9974                $Y + $PlotSize,
9975                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
9976            );
9977            $this->drawLine(
9978                $X - $PlotSize,
9979                $Y + $PlotSize,
9980                $X + $PlotSize,
9981                $Y + $PlotSize,
9982                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
9983            );
9984            $this->drawLine(
9985                $X + $PlotSize,
9986                $Y + $PlotSize,
9987                $X,
9988                $Y - $PlotSize,
9989                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
9990            );
9991        } elseif ($Shape == SERIE_SHAPE_SQUARE) {
9992            $this->drawRectangle(
9993                $X - $PlotSize,
9994                $Y - $PlotSize,
9995                $X + $PlotSize,
9996                $Y + $PlotSize,
9997                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
9998            );
9999        } elseif ($Shape == SERIE_SHAPE_CIRCLE) {
10000            $this->drawCircle(
10001                $X,
10002                $Y,
10003                $PlotSize,
10004                $PlotSize,
10005                ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]
10006            );
10007        } elseif ($Shape == SERIE_SHAPE_DIAMOND) {
10008            $Pos = [];
10009            $Pos[] = $X - $PlotSize;
10010            $Pos[] = $Y;
10011            $Pos[] = $X;
10012            $Pos[] = $Y - $PlotSize;
10013            $Pos[] = $X + $PlotSize;
10014            $Pos[] = $Y;
10015            $Pos[] = $X;
10016            $Pos[] = $Y + $PlotSize;
10017            $this->drawPolygon(
10018                $Pos,
10019                [
10020                    "NoFill" => true,
10021                    "BorderR" => $R,
10022                    "BorderG" => $G,
10023                    "BorderB" => $B,
10024                    "BorderAlpha" => $Alpha
10025                ]
10026            );
10027        } elseif ($Shape == SERIE_SHAPE_FILLEDDIAMOND) {
10028            if ($PlotBorder) {
10029                $Pos = [];
10030                $Pos[] = $X - $PlotSize - $BorderSize;
10031                $Pos[] = $Y;
10032                $Pos[] = $X;
10033                $Pos[] = $Y - $PlotSize - $BorderSize;
10034                $Pos[] = $X + $PlotSize + $BorderSize;
10035                $Pos[] = $Y;
10036                $Pos[] = $X;
10037                $Pos[] = $Y + $PlotSize + $BorderSize;
10038                $this->drawPolygon(
10039                    $Pos,
10040                    ["R" => $BorderR, "G" => $BorderG, "B" => $BorderB, "Alpha" => $BorderAlpha]
10041                );
10042            }
10043
10044            $Pos = [];
10045            $Pos[] = $X - $PlotSize;
10046            $Pos[] = $Y;
10047            $Pos[] = $X;
10048            $Pos[] = $Y - $PlotSize;
10049            $Pos[] = $X + $PlotSize;
10050            $Pos[] = $Y;
10051            $Pos[] = $X;
10052            $Pos[] = $Y + $PlotSize;
10053            $this->drawPolygon($Pos, ["R" => $R, "G" => $G, "B" => $B, "Alpha" => $Alpha]);
10054        }
10055    }
10056
10057    /**
10058     *
10059     * @param array $Points
10060     * @param array $Format
10061     * @return null|integer
10062     */
10063    public function drawPolygonChart(array $Points, array $Format = [])
10064    {
10065        $R = isset($Format["R"]) ? $Format["R"] : 0;
10066        $G = isset($Format["G"]) ? $Format["G"] : 0;
10067        $B = isset($Format["B"]) ? $Format["B"] : 0;
10068        $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
10069        $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : false;
10070        $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : false;
10071        $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
10072        $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
10073        $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
10074        $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha / 2;
10075        $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : null;
10076        $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : null;
10077
10078        if ($Surrounding != null) {
10079            $BorderR = $R + $Surrounding;
10080            $BorderG = $G + $Surrounding;
10081            $BorderB = $B + $Surrounding;
10082        }
10083
10084        $RestoreShadow = $this->Shadow;
10085        $this->Shadow = false;
10086
10087        $AllIntegers = true;
10088        for ($i = 0; $i <= count($Points) - 2; $i = $i + 2) {
10089            if ($this->getFirstDecimal($Points[$i + 1]) != 0) {
10090                $AllIntegers = false;
10091            }
10092        }
10093
10094        /* Convert polygon to segments */
10095        $Segments = [];
10096        for ($i = 2; $i <= count($Points) - 2; $i = $i + 2) {
10097            $Segments[] = [
10098                "X1" => $Points[$i - 2],
10099                "Y1" => $Points[$i - 1],
10100                "X2" => $Points[$i],
10101                "Y2" => $Points[$i + 1]
10102            ];
10103        }
10104        $Segments[] = [
10105            "X1" => $Points[$i - 2],
10106            "Y1" => $Points[$i - 1],
10107            "X2" => $Points[0],
10108            "Y2" => $Points[1]
10109        ];
10110
10111        /* Simplify straight lines */
10112        $Result = [];
10113        $inHorizon = false;
10114        $LastX = VOID;
10115        foreach ($Segments as $Key => $Pos) {
10116            if ($Pos["Y1"] != $Pos["Y2"]) {
10117                if ($inHorizon) {
10118                    $inHorizon = false;
10119                    $Result[] = [
10120                        "X1" => $LastX,
10121                        "Y1" => $Pos["Y1"],
10122                        "X2" => $Pos["X1"],
10123                        "Y2" => $Pos["Y1"]
10124                    ];
10125                }
10126
10127                $Result[] = [
10128                    "X1" => $Pos["X1"],
10129                    "Y1" => $Pos["Y1"],
10130                    "X2" => $Pos["X2"],
10131                    "Y2" => $Pos["Y2"]
10132                ];
10133            } else {
10134                if (!$inHorizon) {
10135                    $inHorizon = true;
10136                    $LastX = $Pos["X1"];
10137                }
10138            }
10139        }
10140        $Segments = $Result;
10141
10142        /* Do we have something to draw */
10143        if (!count($Segments)) {
10144            return 0;
10145        }
10146
10147        /* For segments debugging purpose */
10148        //foreach($Segments as $Key => $Pos)
10149        // echo $Pos["X1"].",".$Pos["Y1"].",".$Pos["X2"].",".$Pos["Y2"]."\r\n";
10150
10151        /* Find out the min & max Y boundaries */
10152        $MinY = OUT_OF_SIGHT;
10153        $MaxY = OUT_OF_SIGHT;
10154        foreach ($Segments as $Key => $Coords) {
10155            if ($MinY == OUT_OF_SIGHT || $MinY > min($Coords["Y1"], $Coords["Y2"])) {
10156                $MinY = min($Coords["Y1"], $Coords["Y2"]);
10157            }
10158            if ($MaxY == OUT_OF_SIGHT || $MaxY < max($Coords["Y1"], $Coords["Y2"])) {
10159                $MaxY = max($Coords["Y1"], $Coords["Y2"]);
10160            }
10161        }
10162
10163        if ($AllIntegers) {
10164            $YStep = 1;
10165        } else {
10166            $YStep = .5;
10167        }
10168
10169        $MinY = floor($MinY);
10170        $MaxY = floor($MaxY);
10171
10172        /* Scan each Y lines */
10173        $DefaultColor = $this->allocateColor($this->Picture, $R, $G, $B, $Alpha);
10174        $DebugLine = 0;
10175        $DebugColor = $this->allocateColor($this->Picture, 255, 0, 0, 100);
10176
10177        $MinY = floor($MinY);
10178        $MaxY = floor($MaxY);
10179        $YStep = 1;
10180
10181        if (!$NoFill) {
10182            //if ($DebugLine ) { $MinY = $DebugLine; $MaxY = $DebugLine; }
10183            for ($Y = $MinY; $Y <= $MaxY; $Y = $Y + $YStep) {
10184                $Intersections = [];
10185                $LastSlope = null;
10186                $RestoreLast = "-";
10187                foreach ($Segments as $Key => $Coords) {
10188                    $X1 = $Coords["X1"];
10189                    $X2 = $Coords["X2"];
10190                    $Y1 = $Coords["Y1"];
10191                    $Y2 = $Coords["Y2"];
10192
10193                    if (min($Y1, $Y2) <= $Y && max($Y1, $Y2) >= $Y) {
10194                        if ($Y1 == $Y2) {
10195                            $X = $X1;
10196                        } else {
10197                            $X = $X1 + (($Y - $Y1) * $X2 - ($Y - $Y1) * $X1) / ($Y2 - $Y1);
10198                        }
10199
10200                        $X = floor($X);
10201
10202                        if ($X2 == $X1) {
10203                            $Slope = "!";
10204                        } else {
10205                            $SlopeC = ($Y2 - $Y1) / ($X2 - $X1);
10206                            if ($SlopeC == 0) {
10207                                $Slope = "=";
10208                            } elseif ($SlopeC > 0) {
10209                                $Slope = "+";
10210                            } elseif ($SlopeC < 0) {
10211                                $Slope = "-";
10212                            }
10213                        }
10214
10215                        if (!is_array($Intersections)) {
10216                            $Intersections[] = $X;
10217                        } elseif (!in_array($X, $Intersections)) {
10218                            $Intersections[] = $X;
10219                        } elseif (in_array($X, $Intersections)) {
10220                            if ($Y == $DebugLine) {
10221                                echo $Slope . "/" . $LastSlope . "(" . $X . ") ";
10222                            }
10223
10224                            if ($Slope == "=" && $LastSlope == "-") {
10225                                $Intersections[] = $X;
10226                            }
10227                            if ($Slope != $LastSlope && $LastSlope != "!" && $LastSlope != "=") {
10228                                $Intersections[] = $X;
10229                            }
10230                            if ($Slope != $LastSlope && $LastSlope == "!" && $Slope == "+") {
10231                                $Intersections[] = $X;
10232                            }
10233                        }
10234
10235                        if (is_array($Intersections)
10236                            && in_array($X, $Intersections)
10237                            && $LastSlope == "="
10238                            && ($Slope == "-")
10239                        ) {
10240                            $Intersections[] = $X;
10241                        }
10242
10243                        $LastSlope = $Slope;
10244                    }
10245                }
10246                if ($RestoreLast != "-") {
10247                    $Intersections[] = $RestoreLast;
10248                    echo "@" . $Y . "\r\n";
10249                }
10250
10251                if (is_array($Intersections)) {
10252                    sort($Intersections);
10253
10254                    if ($Y == $DebugLine) {
10255                        print_r($Intersections);
10256                    }
10257
10258                    /* Remove null plots */
10259                    $Result = [];
10260                    for ($i = 0; $i <= count($Intersections) - 1; $i = $i + 2) {
10261                        if (isset($Intersections[$i + 1])) {
10262                            if ($Intersections[$i] != $Intersections[$i + 1]) {
10263                                $Result[] = $Intersections[$i];
10264                                $Result[] = $Intersections[$i + 1];
10265                            }
10266                        }
10267                    }
10268
10269                    if (is_array($Result)) {
10270                        $Intersections = $Result;
10271
10272                        $LastX = OUT_OF_SIGHT;
10273                        foreach ($Intersections as $Key => $X) {
10274                            if ($LastX == OUT_OF_SIGHT) {
10275                                $LastX = $X;
10276                            } elseif ($LastX != OUT_OF_SIGHT) {
10277                                if ($this->getFirstDecimal($LastX) > 1) {
10278                                    $LastX++;
10279                                }
10280
10281                                $Color = $DefaultColor;
10282                                if ($Threshold != null) {
10283                                    foreach ($Threshold as $Key => $Parameters) {
10284                                        if ($Y <= $Parameters["MinX"]
10285                                            && $Y >= $Parameters["MaxX"]
10286                                        ) {
10287                                            if (isset($Parameters["R"])) {
10288                                                $R = $Parameters["R"];
10289                                            } else {
10290                                                $R = 0;
10291                                            }
10292                                            if (isset($Parameters["G"])) {
10293                                                $G = $Parameters["G"];
10294                                            } else {
10295                                                $G = 0;
10296                                            }
10297                                            if (isset($Parameters["B"])) {
10298                                                $B = $Parameters["B"];
10299                                            } else {
10300                                                $B = 0;
10301                                            }
10302                                            if (isset($Parameters["Alpha"])) {
10303                                                $Alpha = $Parameters["Alpha"];
10304                                            } else {
10305                                                $Alpha = 100;
10306                                            }
10307                                            $Color = $this->allocateColor(
10308                                                $this->Picture,
10309                                                $R,
10310                                                $G,
10311                                                $B,
10312                                                $Alpha
10313                                            );
10314                                        }
10315                                    }
10316                                }
10317
10318                                imageline($this->Picture, $LastX, $Y, $X, $Y, $Color);
10319
10320                                if ($Y == $DebugLine) {
10321                                    imageline($this->Picture, $LastX, $Y, $X, $Y, $DebugColor);
10322                                }
10323
10324                                $LastX = OUT_OF_SIGHT;
10325                            }
10326                        }
10327                    }
10328                }
10329            }
10330        }
10331
10332        /* Draw the polygon border, if required */
10333        if (!$NoBorder) {
10334            foreach ($Segments as $Key => $Coords) {
10335                $this->drawLine(
10336                    $Coords["X1"],
10337                    $Coords["Y1"],
10338                    $Coords["X2"],
10339                    $Coords["Y2"],
10340                    [
10341                        "R" => $BorderR,
10342                        "G" => $BorderG,
10343                        "B" => $BorderB,
10344                        "Alpha" => $BorderAlpha,
10345                        "Threshold" => $Threshold
10346                    ]
10347                );
10348            }
10349        }
10350
10351        $this->Shadow = $RestoreShadow;
10352    }
10353}
10354