1<?php
2
3namespace CpChart\Chart;
4
5use CpChart\Data;
6use CpChart\Image;
7
8/**
9 * Radar - class to draw radar charts
10 *
11 *  Version     : 2.1.4
12 *  Made by     : Jean-Damien POGOLOTTI
13 *  Last Update : 19/01/2014
14 *
15 *  This file can be distributed under the license you can find at :
16 *
17 *  http://www.pchart.net/license
18 *
19 *  You can find the whole class documentation on the pChart web site.
20 */
21class Radar
22{
23    /**
24     * @var Image
25     */
26    public $pChartObject;
27
28    /**
29     * Draw a radar chart
30     * @param Image $Object
31     * @param Data $Values
32     * @param array $Format
33     */
34    public function drawRadar(Image $Object, Data $Values, array $Format = [])
35    {
36        $this->pChartObject = $Object;
37
38        $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID;
39        $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60;
40        $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60;
41        $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60;
42        $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50;
43        $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : 0;
44        $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : true;
45        $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2;
46        $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : true;
47        $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : true;
48        $AxisFontName = isset($Format["AxisFontName"]) ? $Format["AxisFontName"] : $this->pChartObject->FontName;
49        $AxisFontSize = isset($Format["AxisFontSize"]) ? $Format["AxisFontSize"] : $this->pChartObject->FontSize;
50        $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : false;
51        $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : true;
52        $ValueFontName = isset($Format["ValueFontName"]) ? $Format["ValueFontName"] : $this->pChartObject->FontName
53        ;
54        $ValueFontSize = isset($Format["ValueFontSize"]) ? $Format["ValueFontSize"] : $this->pChartObject->FontSize
55        ;
56        $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4;
57        $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2;
58        $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID;
59        $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID;
60        $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID;
61        $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100;
62        $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255;
63        $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255;
64        $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255;
65        $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100;
66        $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true;
67        $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255;
68        $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255;
69        $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255;
70        $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50;
71        $BackgroundGradient = isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : null;
72        $Layout = isset($Format["Layout"]) ? $Format["Layout"] : RADAR_LAYOUT_STAR;
73        $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO;
74        $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4;
75        $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : true;
76        $SkipLabels = isset($Format["SkipLabels"]) ? $Format["SkipLabels"] : 1;
77        $LabelMiddle = isset($Format["LabelMiddle"]) ? $Format["LabelMiddle"] : false;
78        $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : true;
79        $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255;
80        $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255;
81        $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255;
82        $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50;
83        $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED;
84        $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4;
85        $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : true;
86        $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4;
87        $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30;
88        $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : true;
89        $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : true;
90        $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : false;
91        $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : 40;
92        $FontSize = $Object->FontSize;
93        $X1 = $Object->GraphAreaX1;
94        $Y1 = $Object->GraphAreaY1;
95        $X2 = $Object->GraphAreaX2;
96        $Y2 = $Object->GraphAreaY2;
97        $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
98
99        /* Cancel default tick length if ticks not enabled */
100        if ($DrawTicks == false) {
101            $TicksLength = 0;
102        }
103
104        /* Data Processing */
105        $Data = $Values->getData();
106        $Palette = $Values->getPalette();
107
108        /* Catch the number of required axis */
109        $LabelSerie = $Data["Abscissa"];
110        if ($LabelSerie) {
111            $Points = count($Data["Series"][$LabelSerie]["Data"]);
112        } else {
113            $Points = 0;
114            foreach ($Data["Series"] as $SerieName => $DataArray) {
115                if (count($DataArray["Data"]) > $Points) {
116                    $Points = count($DataArray["Data"]);
117                }
118            }
119        }
120
121        /* Draw the axis */
122        $CenterX = ($X2 - $X1) / 2 + $X1;
123        $CenterY = ($Y2 - $Y1) / 2 + $Y1;
124
125        $EdgeHeight = min(($X2 - $X1) / 2, ($Y2 - $Y1) / 2);
126        if ($WriteLabels) {
127            $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength;
128        }
129        /* Determine the scale if set to automatic */
130        if ($SegmentHeight == SEGMENT_HEIGHT_AUTO) {
131            if ($FixedMax != VOID) {
132                $Max = $FixedMax;
133            } else {
134                $Max = 0;
135                foreach ($Data["Series"] as $SerieName => $DataArray) {
136                    if ($SerieName != $LabelSerie) {
137                        if (max($DataArray["Data"]) > $Max) {
138                            $Max = max($DataArray["Data"]);
139                        }
140                    }
141                }
142            }
143            $MaxSegments = $EdgeHeight / 20;
144            $Scale = $Object->computeScale(0, $Max, $MaxSegments, [1, 2, 5]);
145
146            $Segments = $Scale["Rows"];
147            $SegmentHeight = $Scale["RowHeight"];
148        }
149
150        if ($LabelMiddle && $SkipLabels == 1) {
151            $Axisoffset = (360 / $Points) / 2;
152        } elseif ($LabelMiddle && $SkipLabels != 1) {
153            $Axisoffset = (360 / ($Points / $SkipLabels)) / 2;
154        } elseif (!$LabelMiddle) {
155            $Axisoffset = 0;
156        }
157
158        /* Background processing */
159        if ($DrawBackground) {
160            $RestoreShadow = $Object->Shadow;
161            $Object->Shadow = false;
162
163            if ($BackgroundGradient == null) {
164                if ($Layout == RADAR_LAYOUT_STAR) {
165                    $Color = [
166                        "R" => $BackgroundR,
167                        "G" => $BackgroundG,
168                        "B" => $BackgroundB,
169                        "Alpha" => $BackgroundAlpha
170                    ];
171                    $PointArray = [];
172                    for ($i = 0; $i <= 360; $i = $i + (360 / $Points)) {
173                        $PointArray[] = cos(deg2rad($i + $AxisRotation)) * $EdgeHeight + $CenterX;
174                        $PointArray[] = sin(deg2rad($i + $AxisRotation)) * $EdgeHeight + $CenterY;
175                    }
176                    $Object->drawPolygon($PointArray, $Color);
177                } elseif ($Layout == RADAR_LAYOUT_CIRCLE) {
178                    $Color = [
179                        "R" => $BackgroundR,
180                        "G" => $BackgroundG,
181                        "B" => $BackgroundB,
182                        "Alpha" => $BackgroundAlpha
183                    ];
184                    $Object->drawFilledCircle(
185                        $CenterX,
186                        $CenterY,
187                        $EdgeHeight,
188                        $Color
189                    );
190                }
191            } else {
192                $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments;
193                $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments;
194                $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments;
195                $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"])
196                    / $Segments
197                ;
198
199                if ($Layout == RADAR_LAYOUT_STAR) {
200                    for ($j = $Segments; $j >= 1; $j--) {
201                        $Color = [
202                            "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j,
203                            "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j,
204                            "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j,
205                            "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j
206                        ];
207                        $PointArray = [];
208
209                        for ($i = 0; $i <= 360; $i = $i + (360 / $Points)) {
210                            $PointArray[] = cos(deg2rad($i + $AxisRotation))
211                                * ($EdgeHeight / $Segments) * $j + $CenterX
212                            ;
213                            $PointArray[] = sin(deg2rad($i + $AxisRotation))
214                                * ($EdgeHeight / $Segments) * $j + $CenterY
215                            ;
216                        }
217                        $Object->drawPolygon($PointArray, $Color);
218                    }
219                } elseif ($Layout == RADAR_LAYOUT_CIRCLE) {
220                    for ($j = $Segments; $j >= 1; $j--) {
221                        $Color = [
222                            "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j,
223                            "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j,
224                            "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j,
225                            "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j
226                        ];
227                        $Object->drawFilledCircle(
228                            $CenterX,
229                            $CenterY,
230                            ($EdgeHeight / $Segments) * $j,
231                            $Color
232                        );
233                    }
234                }
235            }
236            $Object->Shadow = $RestoreShadow;
237        }
238
239        /* Axis to axis lines */
240        $Color = ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha];
241        $ColorDotted = [
242            "R" => $AxisR,
243            "G" => $AxisG,
244            "B" => $AxisB,
245            "Alpha" => $AxisAlpha * .8,
246            "Ticks" => 2
247        ];
248        if ($Layout == RADAR_LAYOUT_STAR) {
249            for ($j = 1; $j <= $Segments; $j++) {
250                for ($i = 0; $i < 360; $i = $i + (360 / $Points)) {
251                    $EdgeX1 = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX;
252                    $EdgeY1 = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY;
253                    $EdgeX2 = cos(deg2rad($i + $AxisRotation + (360 / $Points)))
254                        * ($EdgeHeight / $Segments) * $j + $CenterX
255                    ;
256                    $EdgeY2 = sin(deg2rad($i + $AxisRotation + (360 / $Points)))
257                        * ($EdgeHeight / $Segments) * $j + $CenterY
258                    ;
259
260                    $Object->drawLine($EdgeX1, $EdgeY1, $EdgeX2, $EdgeY2, $Color);
261                }
262            }
263        } elseif ($Layout == RADAR_LAYOUT_CIRCLE) {
264            for ($j = 1; $j <= $Segments; $j++) {
265                $Radius = ($EdgeHeight / $Segments) * $j;
266                $Object->drawCircle($CenterX, $CenterY, $Radius, $Radius, $Color);
267            }
268        }
269
270        if ($DrawAxisValues) {
271            if ($LabelsBackground) {
272                $Options = [
273                    "DrawBox"  => true,
274                    "Align"  => TEXT_ALIGN_MIDDLEMIDDLE,
275                    "BoxR" => $LabelsBGR,
276                    "BoxG" => $LabelsBGG,
277                    "BoxB" => $LabelsBGB,
278                    "BoxAlpha" => $LabelsBGAlpha
279                ];
280            } else {
281                $Options = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE];
282            }
283            if ($AxisBoxRounded) {
284                $Options["BoxRounded"] = true;
285            }
286
287            $Options["FontName"] = $AxisFontName;
288            $Options["FontSize"] = $AxisFontSize;
289
290            $Angle = 360 / ($Points * 2);
291            for ($j = 1; $j <= $Segments; $j++) {
292                $Label = $j * $SegmentHeight;
293
294                if ($Layout == RADAR_LAYOUT_CIRCLE) {
295                    $EdgeX1 = cos(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX;
296                    $EdgeY1 = sin(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY;
297                } elseif ($Layout == RADAR_LAYOUT_STAR) {
298                    $EdgeX1 = cos(deg2rad($AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX;
299                    $EdgeY1 = sin(deg2rad($AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY;
300                    $EdgeX2 = cos(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight / $Segments)
301                        * $j + $CenterX
302                    ;
303                    $EdgeY2 = sin(deg2rad((360 / $Points) + $AxisRotation)) * ($EdgeHeight / $Segments)
304                        * $j + $CenterY
305                    ;
306
307                    $EdgeX1 = ($EdgeX2 - $EdgeX1) / 2 + $EdgeX1;
308                    $EdgeY1 = ($EdgeY2 - $EdgeY1) / 2 + $EdgeY1;
309                }
310
311                $Object->drawText($EdgeX1, $EdgeY1, $Label, $Options);
312            }
313        }
314
315        /* Axis lines */
316        $ID = 0;
317        for ($i = 0; $i < 360; $i = $i + (360 / $Points)) {
318            $EdgeX = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterX;
319            $EdgeY = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterY;
320
321            if ($ID % $SkipLabels == 0) {
322                $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $Color);
323            } else {
324                $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $ColorDotted);
325            }
326
327            if ($WriteLabels) {
328                $LabelX = cos(deg2rad($i + $AxisRotation + $Axisoffset))
329                    * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterX
330                ;
331                $LabelY = sin(deg2rad($i + $AxisRotation + $Axisoffset))
332                    * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterY
333                ;
334
335                if ($LabelSerie) {
336                    $Label = isset($Data["Series"][$LabelSerie]["Data"][$ID])
337                        ? $Data["Series"][$LabelSerie]["Data"][$ID] : ""
338                    ;
339                } else {
340                    $Label = $ID;
341                }
342
343                if ($ID % $SkipLabels == 0) {
344                    if ($LabelPos == RADAR_LABELS_ROTATED) {
345                        $Object->drawText(
346                            $LabelX,
347                            $LabelY,
348                            $Label,
349                            [
350                                "Angle" => (360 - ($i + $AxisRotation + $Axisoffset)) - 90,
351                                "Align" => TEXT_ALIGN_BOTTOMMIDDLE
352                            ]
353                        );
354                    } else {
355                        if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY))) {
356                            $Object->drawText(
357                                $LabelX,
358                                $LabelY,
359                                $Label,
360                                ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]
361                            );
362                        }
363                        if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY))) {
364                            $Object->drawText(
365                                $LabelX,
366                                $LabelY,
367                                $Label,
368                                ["Align" => TEXT_ALIGN_BOTTOMLEFT]
369                            );
370                        }
371                        if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY))) {
372                            $Object->drawText(
373                                $LabelX,
374                                $LabelY,
375                                $Label,
376                                ["Align" => TEXT_ALIGN_MIDDLELEFT]
377                            );
378                        }
379                        if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY))) {
380                            $Object->drawText(
381                                $LabelX,
382                                $LabelY,
383                                $Label,
384                                ["Align" => TEXT_ALIGN_TOPLEFT]
385                            );
386                        }
387                        if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY))) {
388                            $Object->drawText(
389                                $LabelX,
390                                $LabelY,
391                                $Label,
392                                ["Align" => TEXT_ALIGN_BOTTOMRIGHT]
393                            );
394                        }
395                        if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY))) {
396                            $Object->drawText(
397                                $LabelX,
398                                $LabelY,
399                                $Label,
400                                ["Align" => TEXT_ALIGN_MIDDLERIGHT]
401                            );
402                        }
403                        if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY))) {
404                            $Object->drawText(
405                                $LabelX,
406                                $LabelY,
407                                $Label,
408                                ["Align" => TEXT_ALIGN_TOPRIGHT]
409                            );
410                        }
411                        if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY))) {
412                            $Object->drawText(
413                                $LabelX,
414                                $LabelY,
415                                $Label,
416                                ["Align" => TEXT_ALIGN_TOPMIDDLE]
417                            );
418                        }
419                    }
420                }
421            }
422            $ID++;
423        }
424
425        /* Compute the plots position */
426        $ID = 0;
427        $Plot = [];
428        foreach ($Data["Series"] as $SerieName => $DataS) {
429            if ($SerieName != $LabelSerie) {
430                $Color = [
431                    "R" => $Palette[$ID]["R"],
432                    "G" => $Palette[$ID]["G"],
433                    "B" => $Palette[$ID]["B"],
434                    "Alpha" => $Palette[$ID]["Alpha"],
435                    "Surrounding" => $PointSurrounding
436                ];
437                foreach ($DataS["Data"] as $Key => $Value) {
438                    $Angle = (360 / $Points) * $Key;
439                    $Length = ($EdgeHeight / ($Segments * $SegmentHeight)) * $Value;
440
441                    $X = cos(deg2rad($Angle + $AxisRotation)) * $Length + $CenterX;
442                    $Y = sin(deg2rad($Angle + $AxisRotation)) * $Length + $CenterY;
443
444                    $Plot[$ID][] = [$X, $Y, $Value];
445
446                    if ($RecordImageMap) {
447                        $this->pChartObject->addToImageMap(
448                            "CIRCLE",
449                            sprintf("%s,%s,%s", floor($X), floor($Y), floor($PointRadius)),
450                            $this->pChartObject->toHTMLColor(
451                                $Palette[$ID]["R"],
452                                $Palette[$ID]["G"],
453                                $Palette[$ID]["B"]
454                            ),
455                            $DataS["Description"],
456                            $Data["Series"][$LabelSerie]["Data"][$Key] . " = " . $Value
457                        );
458                    }
459                }
460                $ID++;
461            }
462        }
463
464        /* Draw all that stuff! */
465        foreach ($Plot as $ID => $Points) {
466            $Color = [
467                "R" => $Palette[$ID]["R"],
468                "G" => $Palette[$ID]["G"],
469                "B" => $Palette[$ID]["B"],
470                "Alpha" => $Palette[$ID]["Alpha"],
471                "Surrounding" => $PointSurrounding
472            ];
473
474            /* Draw the polygons */
475            if ($DrawPoly) {
476                if ($PolyAlpha != null) {
477                    $Color = [
478                        "R" => $Palette[$ID]["R"],
479                        "G" => $Palette[$ID]["G"],
480                        "B" => $Palette[$ID]["B"],
481                        "Alpha" => $PolyAlpha,
482                        "Surrounding" => $PointSurrounding
483                    ];
484                }
485                $PointsArray = [];
486                for ($i = 0; $i < count($Points); $i++) {
487                    $PointsArray[] = $Points[$i][0];
488                    $PointsArray[] = $Points[$i][1];
489                }
490                $Object->drawPolygon($PointsArray, $Color);
491            }
492
493            $Color = [
494                "R" => $Palette[$ID]["R"],
495                "G" => $Palette[$ID]["G"],
496                "B" => $Palette[$ID]["B"],
497                "Alpha" => $Palette[$ID]["Alpha"],
498                "Surrounding" => $PointSurrounding
499            ];
500
501            /* Bubble and labels settings */
502            $TextSettings = [
503                "Align"  => TEXT_ALIGN_MIDDLEMIDDLE,
504                "FontName" => $ValueFontName,
505                "FontSize" => $ValueFontSize,
506                "R"  => $Palette[$ID]["R"],
507                "G"  => $Palette[$ID]["G"],
508                "B"  => $Palette[$ID]["B"]
509            ];
510            $InnerColor = [
511                "R" => $InnerBubbleR,
512                "G" => $InnerBubbleG,
513                "B" => $InnerBubbleB,
514                "Alpha" => $InnerBubbleAlpha
515            ];
516            if ($OuterBubbleR != VOID) {
517                $OuterColor = [
518                    "R" => $OuterBubbleR,
519                    "G" => $OuterBubbleG,
520                    "B" => $OuterBubbleB,
521                    "Alpha" => $OuterBubbleAlpha
522                ];
523            } else {
524                $OuterColor = [
525                    "R" => $Palette[$ID]["R"] + 20,
526                    "G" => $Palette[$ID]["G"] + 20,
527                    "B" => $Palette[$ID]["B"] + 20,
528                    "Alpha" => $Palette[$ID]["Alpha"]
529                ];
530            }
531            /* Loop to the starting points if asked */
532            if ($LineLoopStart && $DrawLines) {
533                $Object->drawLine(
534                    $Points[count($Points) - 1][0],
535                    $Points[count($Points) - 1][1],
536                    $Points[0][0],
537                    $Points[0][1],
538                    $Color
539                );
540            }
541
542            /* Draw the lines & points */
543            for ($i = 0; $i < count($Points); $i++) {
544                if ($DrawLines && $i < count($Points) - 1) {
545                    $Object->drawLine(
546                        $Points[$i][0],
547                        $Points[$i][1],
548                        $Points[$i + 1][0],
549                        $Points[$i + 1][1],
550                        $Color
551                    );
552                }
553                if ($DrawPoints) {
554                    $Object->drawFilledCircle(
555                        $Points[$i][0],
556                        $Points[$i][1],
557                        $PointRadius,
558                        $Color
559                    );
560                }
561                if ($WriteValuesInBubble && $WriteValues) {
562                    $TxtPos = $this->pChartObject->getTextBox(
563                        $Points[$i][0],
564                        $Points[$i][1],
565                        $ValueFontName,
566                        $ValueFontSize,
567                        0,
568                        $Points[$i][2]
569                    );
570                    $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding * 2) / 2);
571
572                    $this->pChartObject->drawFilledCircle(
573                        $Points[$i][0],
574                        $Points[$i][1],
575                        $Radius + $OuterBubbleRadius,
576                        $OuterColor
577                    );
578                    $this->pChartObject->drawFilledCircle(
579                        $Points[$i][0],
580                        $Points[$i][1],
581                        $Radius,
582                        $InnerColor
583                    );
584                }
585
586                if ($WriteValues) {
587                    $this->pChartObject->drawText(
588                        $Points[$i][0] - 1,
589                        $Points[$i][1] - 1,
590                        $Points[$i][2],
591                        $TextSettings
592                    );
593                }
594            }
595        }
596    }
597
598    /**
599     * Draw a radar chart
600     * @param Image $Object
601     * @param Data $Values
602     * @param array $Format
603     */
604    public function drawPolar(Image $Object, Data $Values, array $Format = [])
605    {
606        $this->pChartObject = $Object;
607
608        $FixedMax = isset($Format["FixedMax"]) ? $Format["FixedMax"] : VOID;
609        $AxisR = isset($Format["AxisR"]) ? $Format["AxisR"] : 60;
610        $AxisG = isset($Format["AxisG"]) ? $Format["AxisG"] : 60;
611        $AxisB = isset($Format["AxisB"]) ? $Format["AxisB"] : 60;
612        $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 50;
613        $AxisRotation = isset($Format["AxisRotation"]) ? $Format["AxisRotation"] : -90;
614        $DrawTicks = isset($Format["DrawTicks"]) ? $Format["DrawTicks"] : true;
615        $TicksLength = isset($Format["TicksLength"]) ? $Format["TicksLength"] : 2;
616        $DrawAxisValues = isset($Format["DrawAxisValues"]) ? $Format["DrawAxisValues"] : true;
617        $AxisBoxRounded = isset($Format["AxisBoxRounded"]) ? $Format["AxisBoxRounded"] : true;
618        $AxisFontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->pChartObject->FontName;
619        $AxisFontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->pChartObject->FontSize;
620        $WriteValues = isset($Format["WriteValues"]) ? $Format["WriteValues"] : false;
621        $WriteValuesInBubble = isset($Format["WriteValuesInBubble"]) ? $Format["WriteValuesInBubble"] : true;
622        $ValueFontName = isset($Format["ValueFontName"])
623            ? $Format["ValueFontName"] : $this->pChartObject->FontName
624        ;
625        $ValueFontSize = isset($Format["ValueFontSize"])
626            ? $Format["ValueFontSize"] : $this->pChartObject->FontSize
627        ;
628        $ValuePadding = isset($Format["ValuePadding"]) ? $Format["ValuePadding"] : 4;
629        $OuterBubbleRadius = isset($Format["OuterBubbleRadius"]) ? $Format["OuterBubbleRadius"] : 2;
630        $OuterBubbleR = isset($Format["OuterBubbleR"]) ? $Format["OuterBubbleR"] : VOID;
631        $OuterBubbleG = isset($Format["OuterBubbleG"]) ? $Format["OuterBubbleG"] : VOID;
632        $OuterBubbleB = isset($Format["OuterBubbleB"]) ? $Format["OuterBubbleB"] : VOID;
633        $OuterBubbleAlpha = isset($Format["OuterBubbleAlpha"]) ? $Format["OuterBubbleAlpha"] : 100;
634        $InnerBubbleR = isset($Format["InnerBubbleR"]) ? $Format["InnerBubbleR"] : 255;
635        $InnerBubbleG = isset($Format["InnerBubbleG"]) ? $Format["InnerBubbleG"] : 255;
636        $InnerBubbleB = isset($Format["InnerBubbleB"]) ? $Format["InnerBubbleB"] : 255;
637        $InnerBubbleAlpha = isset($Format["InnerBubbleAlpha"]) ? $Format["InnerBubbleAlpha"] : 100;
638        $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : true;
639        $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255;
640        $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255;
641        $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255;
642        $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 50;
643        $BackgroundGradient = isset($Format["BackgroundGradient"]) ? $Format["BackgroundGradient"] : null;
644        $AxisSteps = isset($Format["AxisSteps"]) ? $Format["AxisSteps"] : 20;
645        $SegmentHeight = isset($Format["SegmentHeight"]) ? $Format["SegmentHeight"] : SEGMENT_HEIGHT_AUTO;
646        $Segments = isset($Format["Segments"]) ? $Format["Segments"] : 4;
647        $WriteLabels = isset($Format["WriteLabels"]) ? $Format["WriteLabels"] : true;
648        $LabelsBackground = isset($Format["LabelsBackground"]) ? $Format["LabelsBackground"] : true;
649        $LabelsBGR = isset($Format["LabelsBGR"]) ? $Format["LabelsBGR"] : 255;
650        $LabelsBGG = isset($Format["LabelsBGR"]) ? $Format["LabelsBGG"] : 255;
651        $LabelsBGB = isset($Format["LabelsBGR"]) ? $Format["LabelsBGB"] : 255;
652        $LabelsBGAlpha = isset($Format["LabelsBGAlpha"]) ? $Format["LabelsBGAlpha"] : 50;
653        $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : RADAR_LABELS_ROTATED;
654        $LabelPadding = isset($Format["LabelPadding"]) ? $Format["LabelPadding"] : 4;
655        $DrawPoints = isset($Format["DrawPoints"]) ? $Format["DrawPoints"] : true;
656        $PointRadius = isset($Format["PointRadius"]) ? $Format["PointRadius"] : 4;
657        $PointSurrounding = isset($Format["PointRadius"]) ? $Format["PointRadius"] : -30;
658        $DrawLines = isset($Format["DrawLines"]) ? $Format["DrawLines"] : true;
659        $LineLoopStart = isset($Format["LineLoopStart"]) ? $Format["LineLoopStart"] : false;
660        $DrawPoly = isset($Format["DrawPoly"]) ? $Format["DrawPoly"] : false;
661        $PolyAlpha = isset($Format["PolyAlpha"]) ? $Format["PolyAlpha"] : null;
662        $FontSize = $Object->FontSize;
663        $X1 = $Object->GraphAreaX1;
664        $Y1 = $Object->GraphAreaY1;
665        $X2 = $Object->GraphAreaX2;
666        $Y2 = $Object->GraphAreaY2;
667        $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : false;
668
669        if ($AxisBoxRounded) {
670            $DrawAxisValues = true;
671        }
672
673        /* Cancel default tick length if ticks not enabled */
674        if ($DrawTicks == false) {
675            $TicksLength = 0;
676        }
677
678        /* Data Processing */
679        $Data = $Values->getData();
680        $Palette = $Values->getPalette();
681
682        /* Catch the number of required axis */
683        $LabelSerie = $Data["Abscissa"];
684        if ($LabelSerie) {
685            $Points = count($Data["Series"][$LabelSerie]["Data"]);
686        } else {
687            $Points = 0;
688            foreach ($Data["Series"] as $SerieName => $DataArray) {
689                if (count($DataArray["Data"]) > $Points) {
690                    $Points = count($DataArray["Data"]);
691                }
692            }
693        }
694
695        /* Draw the axis */
696        $CenterX = ($X2 - $X1) / 2 + $X1;
697        $CenterY = ($Y2 - $Y1) / 2 + $Y1;
698
699        $EdgeHeight = min(($X2 - $X1) / 2, ($Y2 - $Y1) / 2);
700        if ($WriteLabels) {
701            $EdgeHeight = $EdgeHeight - $FontSize - $LabelPadding - $TicksLength;
702        }
703
704        /* Determine the scale if set to automatic */
705        if ($SegmentHeight == SEGMENT_HEIGHT_AUTO) {
706            if ($FixedMax != VOID) {
707                $Max = $FixedMax;
708            } else {
709                $Max = 0;
710                foreach ($Data["Series"] as $SerieName => $DataArray) {
711                    if ($SerieName != $LabelSerie) {
712                        if (max($DataArray["Data"]) > $Max) {
713                            $Max = max($DataArray["Data"]);
714                        }
715                    }
716                }
717            }
718            $MaxSegments = $EdgeHeight / 20;
719            $Scale = $Object->computeScale(0, $Max, $MaxSegments, [1, 2, 5]);
720
721            $Segments = $Scale["Rows"];
722            $SegmentHeight = $Scale["RowHeight"];
723        }
724
725
726        /* Background processing */
727        if ($DrawBackground) {
728            $RestoreShadow = $Object->Shadow;
729            $Object->Shadow = false;
730
731            if ($BackgroundGradient == null) {
732                $Color = [
733                    "R" => $BackgroundR,
734                    "G" => $BackgroundG,
735                    "B" => $BackgroundB,
736                    "Alpha" => $BackgroundAlpha
737                ];
738                $Object->drawFilledCircle(
739                    $CenterX,
740                    $CenterY,
741                    $EdgeHeight,
742                    $Color
743                );
744            } else {
745                $GradientROffset = ($BackgroundGradient["EndR"] - $BackgroundGradient["StartR"]) / $Segments;
746                $GradientGOffset = ($BackgroundGradient["EndG"] - $BackgroundGradient["StartG"]) / $Segments;
747                $GradientBOffset = ($BackgroundGradient["EndB"] - $BackgroundGradient["StartB"]) / $Segments;
748                $GradientAlphaOffset = ($BackgroundGradient["EndAlpha"] - $BackgroundGradient["StartAlpha"])
749                    / $Segments
750                ;
751
752                for ($j = $Segments; $j >= 1; $j--) {
753                    $Color = [
754                        "R" => $BackgroundGradient["StartR"] + $GradientROffset * $j,
755                        "G" => $BackgroundGradient["StartG"] + $GradientGOffset * $j,
756                        "B" => $BackgroundGradient["StartB"] + $GradientBOffset * $j,
757                        "Alpha" => $BackgroundGradient["StartAlpha"] + $GradientAlphaOffset * $j
758                    ];
759                    $Object->drawFilledCircle(
760                        $CenterX,
761                        $CenterY,
762                        ($EdgeHeight / $Segments) * $j,
763                        $Color
764                    );
765                }
766            }
767            $Object->Shadow = $RestoreShadow;
768        }
769
770        /* Axis to axis lines */
771        $Color = ["R" => $AxisR, "G" => $AxisG, "B" => $AxisB, "Alpha" => $AxisAlpha];
772        for ($j = 1; $j <= $Segments; $j++) {
773            $Radius = ($EdgeHeight / $Segments) * $j;
774            $Object->drawCircle($CenterX, $CenterY, $Radius, $Radius, $Color);
775        }
776
777        if ($DrawAxisValues) {
778            if ($LabelsBackground) {
779                $Options = [
780                    "DrawBox"  => true,
781                    "Align"  => TEXT_ALIGN_MIDDLEMIDDLE,
782                    "BoxR" => $LabelsBGR,
783                    "BoxG" => $LabelsBGG,
784                    "BoxB" => $LabelsBGB,
785                    "BoxAlpha" => $LabelsBGAlpha
786                ];
787            } else {
788                $Options = ["Align" => TEXT_ALIGN_MIDDLEMIDDLE];
789            }
790
791            if ($AxisBoxRounded) {
792                $Options["BoxRounded"] = true;
793            }
794
795            $Options["FontName"] = $AxisFontName;
796            $Options["FontSize"] = $AxisFontSize;
797
798            $Angle = 360 / ($Points * 2);
799            for ($j = 1; $j <= $Segments; $j++) {
800                $EdgeX1 = cos(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterX;
801                $EdgeY1 = sin(deg2rad($Angle + $AxisRotation)) * ($EdgeHeight / $Segments) * $j + $CenterY;
802                $Label = $j * $SegmentHeight;
803
804                $Object->drawText($EdgeX1, $EdgeY1, $Label, $Options);
805            }
806        }
807
808        /* Axis lines */
809        $ID = 0;
810        for ($i = 0; $i <= 359; $i = $i + $AxisSteps) {
811            $EdgeX = cos(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterX;
812            $EdgeY = sin(deg2rad($i + $AxisRotation)) * ($EdgeHeight + $TicksLength) + $CenterY;
813
814            $Object->drawLine($CenterX, $CenterY, $EdgeX, $EdgeY, $Color);
815
816            if ($WriteLabels) {
817                $LabelX = cos(deg2rad($i + $AxisRotation))
818                    * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterX
819                ;
820                $LabelY = sin(deg2rad($i + $AxisRotation))
821                    * ($EdgeHeight + $LabelPadding + $TicksLength) + $CenterY
822                ;
823                $Label = $i . "°";
824
825                if ($LabelPos == RADAR_LABELS_ROTATED) {
826                    $Object->drawText(
827                        $LabelX,
828                        $LabelY,
829                        $Label,
830                        ["Angle" => (360 - $i), "Align" => TEXT_ALIGN_BOTTOMMIDDLE]
831                    );
832                } else {
833                    if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) < floor($CenterY))) {
834                        $Object->drawText(
835                            $LabelX,
836                            $LabelY,
837                            $Label,
838                            ["Align" => TEXT_ALIGN_BOTTOMMIDDLE]
839                        );
840                    }
841                    if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) < floor($CenterY))) {
842                        $Object->drawText(
843                            $LabelX,
844                            $LabelY,
845                            $Label,
846                            ["Align" => TEXT_ALIGN_BOTTOMLEFT]
847                        );
848                    }
849                    if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) == floor($CenterY))) {
850                        $Object->drawText(
851                            $LabelX,
852                            $LabelY,
853                            $Label,
854                            ["Align" => TEXT_ALIGN_MIDDLELEFT]
855                        );
856                    }
857                    if ((floor($LabelX) > floor($CenterX)) && (floor($LabelY) > floor($CenterY))) {
858                        $Object->drawText(
859                            $LabelX,
860                            $LabelY,
861                            $Label,
862                            ["Align" => TEXT_ALIGN_TOPLEFT]
863                        );
864                    }
865                    if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) < floor($CenterY))) {
866                        $Object->drawText(
867                            $LabelX,
868                            $LabelY,
869                            $Label,
870                            ["Align" => TEXT_ALIGN_BOTTOMRIGHT]
871                        );
872                    }
873                    if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) == floor($CenterY))) {
874                        $Object->drawText(
875                            $LabelX,
876                            $LabelY,
877                            $Label,
878                            ["Align" => TEXT_ALIGN_MIDDLERIGHT]
879                        );
880                    }
881                    if ((floor($LabelX) < floor($CenterX)) && (floor($LabelY) > floor($CenterY))) {
882                        $Object->drawText(
883                            $LabelX,
884                            $LabelY,
885                            $Label,
886                            ["Align" => TEXT_ALIGN_TOPRIGHT]
887                        );
888                    }
889                    if ((floor($LabelX) == floor($CenterX)) && (floor($LabelY) > floor($CenterY))) {
890                        $Object->drawText(
891                            $LabelX,
892                            $LabelY,
893                            $Label,
894                            ["Align" => TEXT_ALIGN_TOPMIDDLE]
895                        );
896                    }
897                }
898            }
899            $ID++;
900        }
901
902        /* Compute the plots position */
903        $ID = 0;
904        $Plot = [];
905        foreach ($Data["Series"] as $SerieName => $DataSet) {
906            if ($SerieName != $LabelSerie) {
907                $Color = [
908                    "R" => $Palette[$ID]["R"],
909                    "G" => $Palette[$ID]["G"],
910                    "B" => $Palette[$ID]["B"],
911                    "Alpha" => $Palette[$ID]["Alpha"],
912                    "Surrounding" => $PointSurrounding
913                ];
914                foreach ($DataSet["Data"] as $Key => $Value) {
915                    $Angle = $Data["Series"][$LabelSerie]["Data"][$Key];
916                    $Length = ($EdgeHeight / ($Segments * $SegmentHeight)) * $Value;
917
918                    $X = cos(deg2rad($Angle + $AxisRotation)) * $Length + $CenterX;
919                    $Y = sin(deg2rad($Angle + $AxisRotation)) * $Length + $CenterY;
920
921                    if ($RecordImageMap) {
922                        $this->pChartObject->addToImageMap(
923                            "CIRCLE",
924                            sprintf("%s,%s,%s", floor($X), floor($Y), floor($PointRadius)),
925                            $this->pChartObject->toHTMLColor(
926                                $Palette[$ID]["R"],
927                                $Palette[$ID]["G"],
928                                $Palette[$ID]["B"]
929                            ),
930                            $DataSet["Description"],
931                            $Data["Series"][$LabelSerie]["Data"][$Key] . "&deg = " . $Value
932                        );
933                    }
934
935                    $Plot[$ID][] = [$X, $Y, $Value];
936                }
937                $ID++;
938            }
939        }
940
941        /* Draw all that stuff! */
942        foreach ($Plot as $ID => $Points) {
943            $Color = [
944                "R" => $Palette[$ID]["R"],
945                "G" => $Palette[$ID]["G"],
946                "B" => $Palette[$ID]["B"],
947                "Alpha" => $Palette[$ID]["Alpha"],
948                "Surrounding" => $PointSurrounding
949            ];
950
951            /* Draw the polygons */
952            if ($DrawPoly) {
953                if ($PolyAlpha != null) {
954                    $Color = [
955                        "R" => $Palette[$ID]["R"],
956                        "G" => $Palette[$ID]["G"],
957                        "B" => $Palette[$ID]["B"],
958                        "Alpha" => $PolyAlpha,
959                        "Surrounding" => $PointSurrounding
960                    ];
961                }
962                $PointsArray = [];
963                for ($i = 0; $i < count($Points); $i++) {
964                    $PointsArray[] = $Points[$i][0];
965                    $PointsArray[] = $Points[$i][1];
966                }
967
968                $Object->drawPolygon($PointsArray, $Color);
969            }
970
971            $Color = [
972                "R" => $Palette[$ID]["R"],
973                "G" => $Palette[$ID]["G"],
974                "B" => $Palette[$ID]["B"],
975                "Alpha" => $Palette[$ID]["Alpha"],
976                "Surrounding" => $PointSurrounding
977            ];
978
979            /* Bubble and labels settings */
980            $TextSettings = [
981                "Align"  => TEXT_ALIGN_MIDDLEMIDDLE,
982                "FontName" => $ValueFontName,
983                "FontSize" => $ValueFontSize,
984                "R"  => $Palette[$ID]["R"],
985                "G"  => $Palette[$ID]["G"],
986                "B"  => $Palette[$ID]["B"]
987            ];
988            $InnerColor = [
989                "R" => $InnerBubbleR,
990                "G" => $InnerBubbleG,
991                "B" => $InnerBubbleB,
992                "Alpha" => $InnerBubbleAlpha
993            ];
994            if ($OuterBubbleR != VOID) {
995                $OuterColor = [
996                    "R" => $OuterBubbleR,
997                    "G" => $OuterBubbleG,
998                    "B" => $OuterBubbleB,
999                    "Alpha" => $OuterBubbleAlpha
1000                ];
1001            } else {
1002                $OuterColor = [
1003                    "R" => $Palette[$ID]["R"] + 20,
1004                    "G" => $Palette[$ID]["G"] + 20,
1005                    "B" => $Palette[$ID]["B"] + 20,
1006                    "Alpha" => $Palette[$ID]["Alpha"]
1007                ];
1008            }
1009            /* Loop to the starting points if asked */
1010            if ($LineLoopStart && $DrawLines) {
1011                $Object->drawLine(
1012                    $Points[count($Points) - 1][0],
1013                    $Points[count($Points) - 1][1],
1014                    $Points[0][0],
1015                    $Points[0][1],
1016                    $Color
1017                );
1018            }
1019            /* Draw the lines & points */
1020            for ($i = 0; $i < count($Points); $i++) {
1021                if ($DrawLines && $i < count($Points) - 1) {
1022                    $Object->drawLine(
1023                        $Points[$i][0],
1024                        $Points[$i][1],
1025                        $Points[$i + 1][0],
1026                        $Points[$i + 1][1],
1027                        $Color
1028                    );
1029                }
1030                if ($DrawPoints) {
1031                    $Object->drawFilledCircle(
1032                        $Points[$i][0],
1033                        $Points[$i][1],
1034                        $PointRadius,
1035                        $Color
1036                    );
1037                }
1038                if ($WriteValuesInBubble && $WriteValues) {
1039                    $TxtPos = $this->pChartObject->getTextBox(
1040                        $Points[$i][0],
1041                        $Points[$i][1],
1042                        $ValueFontName,
1043                        $ValueFontSize,
1044                        0,
1045                        $Points[$i][2]
1046                    );
1047                    $Radius = floor(($TxtPos[1]["X"] - $TxtPos[0]["X"] + $ValuePadding * 2) / 2);
1048
1049                    $this->pChartObject->drawFilledCircle(
1050                        $Points[$i][0],
1051                        $Points[$i][1],
1052                        $Radius + $OuterBubbleRadius,
1053                        $OuterColor
1054                    );
1055                    $this->pChartObject->drawFilledCircle(
1056                        $Points[$i][0],
1057                        $Points[$i][1],
1058                        $Radius,
1059                        $InnerColor
1060                    );
1061                }
1062
1063                if ($WriteValues) {
1064                    $this->pChartObject->drawText(
1065                        $Points[$i][0] - 1,
1066                        $Points[$i][1] - 1,
1067                        $Points[$i][2],
1068                        $TextSettings
1069                    );
1070                }
1071            }
1072        }
1073    }
1074}
1075