Issues (1844)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

vendor/amenadiel/jpgraph/src/plot/PiePlot.php (17 issues)

1
<?php
2
3
/**
4
 * JPGraph v4.0.3
5
 */
6
7
namespace Amenadiel\JpGraph\Plot;
8
9
use Amenadiel\JpGraph\Graph;
10
use Amenadiel\JpGraph\Text;
11
use Amenadiel\JpGraph\Util;
12
13
/*
14
 * File:        JPGRAPH_PIE.PHP
15
 * // Description: Pie plot extension for JpGraph
16
 * // Created:     2001-02-14
17
 * // Ver:         $Id: jpgraph_pie.php 1926 2010-01-11 16:33:07Z ljp $
18
 * //
19
 * // Copyright (c) Asial Corporation. All rights reserved.
20
 */
21
// Defines for PiePlot::SetLabelType()
22
define('PIE_VALUE_ABS', 1);
23
define('PIE_VALUE_PER', 0);
24
define('PIE_VALUE_PERCENTAGE', 0);
25
define('PIE_VALUE_ADJPERCENTAGE', 2);
26
define('PIE_VALUE_ADJPER', 2);
27
28
/**
29
 * @class PiePlot
30
 * // Description: Draws a pie plot
31
 */
32
class PiePlot
33
{
34
    public $posx                     = 0.5;
35
    public $posy                     = 0.5;
36
    public $is_using_plot_theme      = false;
37
    public $theme                    = 'earth';
38
    protected $use_plot_theme_colors = false;
39
    protected $radius                = 0.3;
40
    protected $explode_radius        = [];
41
    protected $explode_all           = false;
42
    protected $explode_r             = 20;
43
    protected $labels;
44
    protected $legends;
45
    protected $csimtargets;
46
    protected $csimwintargets; // Array of targets for CSIM
47
    protected $csimareas = ''; // Generated CSIM text
48
    protected $csimalts; // ALT tags for corresponding target
49
    protected $data;
50
    public $title;
51
    protected $startangle    = 0;
52
    protected $weight        = 1;
53
    protected $color         = 'black';
54
    protected $legend_margin = 6;
55
    protected $show_labels   = true;
56
    protected $themearr      = [
57
        'earth'  => [136, 34, 40, 45, 46, 62, 63, 134, 74, 10, 120, 136, 141, 168, 180, 77, 209, 218, 346, 395, 89, 430],
58
        'pastel' => [27, 415, 128, 59, 66, 79, 105, 110, 42, 147, 152, 230, 236, 240, 331, 337, 405, 38],
59
        'water'  => [8, 370, 24, 40, 335, 56, 213, 237, 268, 14, 326, 387, 10, 388],
60
        'sand'   => [27, 168, 34, 170, 19, 50, 65, 72, 131, 209, 46, 393], ];
61
    protected $setslicecolors      = [];
62
    protected $labeltype           = 0; // Default to percentage
63
    protected $pie_border          = true;
64
    protected $pie_interior_border = true;
65
    public $value;
66
    protected $ishadowcolor         = '';
67
    protected $ishadowdrop          = 4;
68
    protected $ilabelposadj         = 1;
69
    protected $legendcsimtargets    = [];
70
    protected $legendcsimwintargets = [];
71
    protected $legendcsimalts       = [];
72
    protected $adjusted_data        = [];
73
    public $guideline;
74
    protected $guidelinemargin         = 10;
75
    protected $iShowGuideLineForSingle = false;
76
    protected $iGuideLineCurve         = false;
77
    protected $iGuideVFactor           = 1.4;
78
    protected $iGuideLineRFactor       = 0.8;
79
    protected $la                      = []; // Holds the exact angle for each label
80
81
    /**
82
     * CONSTRUCTOR.
83
     *
84
     * @param mixed $data
85
     */
86
    public function __construct($data)
87
    {
88
        $this->data  = array_reverse($data);
89
        $this->title = new Text\Text('');
90
        $this->title->SetFont(FF_DEFAULT, FS_BOLD);
91
        $this->value = new DisplayValue();
92
        $this->value->Show();
93
        $this->value->SetFormat('%.1f%%');
94
        $this->guideline = new Graph\LineProperty();
95
    }
96
97
    /**
98
     * PUBLIC METHODS.
99
     *
100
     * @param mixed $x
101
     * @param mixed $y
102
     */
103
    public function SetCenter($x, $y = 0.5)
104
    {
105
        $this->posx = $x;
106
        $this->posy = $y;
107
    }
108
109
    // Enable guideline and set drwaing policy
110
    public function SetGuideLines($aFlg = true, $aCurved = true, $aAlways = false)
111
    {
112
        $this->guideline->Show($aFlg);
113
        $this->iShowGuideLineForSingle = $aAlways;
114
        $this->iGuideLineCurve         = $aCurved;
115
    }
116
117
    // Adjuste the distance between labels and labels and pie
118
    public function SetGuideLinesAdjust($aVFactor, $aRFactor = 0.8)
119
    {
120
        $this->iGuideVFactor     = $aVFactor;
121
        $this->iGuideLineRFactor = $aRFactor;
122
    }
123
124
    public function SetColor($aColor)
125
    {
126
        $this->color = $aColor;
127
    }
128
129
    public function SetSliceColors($aColors)
130
    {
131
        $this->setslicecolors = $aColors;
132
    }
133
134
    public function SetShadow($aColor = 'darkgray', $aDropWidth = 4)
135
    {
136
        $this->ishadowcolor = $aColor;
137
        $this->ishadowdrop  = $aDropWidth;
138
    }
139
140
    public function SetCSIMTargets($aTargets, $aAlts = '', $aWinTargets = '')
141
    {
142
        $this->csimtargets = array_reverse($aTargets);
143
        if (is_array($aWinTargets)) {
144
            $this->csimwintargets = array_reverse($aWinTargets);
145
        }
146
147
        if (is_array($aAlts)) {
148
            $this->csimalts = array_reverse($aAlts);
149
        }
150
    }
151
152
    public function GetCSIMareas()
153
    {
154
        return $this->csimareas;
155
    }
156
157
    public function AddSliceToCSIM($i, $xc, $yc, $radius, $sa, $ea)
158
    {
159
        //Slice number, ellipse centre (x,y), height, width, start angle, end angle
160
        while ($sa > 2 * M_PI) {
161
            $sa = $sa - 2 * M_PI;
162
        }
163
164
        while ($ea > 2 * M_PI) {
165
            $ea = $ea - 2 * M_PI;
166
        }
167
168
        $sa = 2 * M_PI - $sa;
169
        $ea = 2 * M_PI - $ea;
170
171
        // Special case when we have only one slice since then both start and end
172
        // angle will be == 0
173
        if (abs($sa - $ea) < 0.0001) {
174
            $sa = 2 * M_PI;
175
            $ea = 0;
176
        }
177
178
        //add coordinates of the centre to the map
179
        $xc     = floor($xc);
180
        $yc     = floor($yc);
181
        $coords = "${xc}, ${yc}";
182
183
        //add coordinates of the first point on the arc to the map
184
        $xp = floor(($radius * cos($ea)) + $xc);
185
        $yp = floor($yc - $radius * sin($ea));
186
        $coords .= ", ${xp}, ${yp}";
187
188
        //add coordinates every 0.2 radians
189
        $a = $ea + 0.2;
190
191
        // If we cross the 360-limit with a slice we need to handle
192
        // the fact that end angle is smaller than start
193
        if ($sa < $ea) {
194
            while ($a <= 2 * M_PI) {
195
                $xp = floor($radius * cos($a) + $xc);
196
                $yp = floor($yc - $radius * sin($a));
197
                $coords .= ", ${xp}, ${yp}";
198
                $a += 0.2;
199
            }
200
            $a -= 2 * M_PI;
201
        }
202
203
        while ($a < $sa) {
204
            $xp = floor($radius * cos($a) + $xc);
205
            $yp = floor($yc - $radius * sin($a));
206
            $coords .= ", ${xp}, ${yp}";
207
            $a += 0.2;
208
        }
209
210
        //Add the last point on the arc
211
        $xp = floor($radius * cos($sa) + $xc);
212
        $yp = floor($yc - $radius * sin($sa));
213
        $coords .= ", ${xp}, ${yp}";
214
        if (!empty($this->csimtargets[$i])) {
215
            $this->csimareas .= "<area shape=\"poly\" coords=\"${coords}\" href=\"" . $this->csimtargets[$i] . '"';
216
            $tmp = '';
0 ignored issues
show
The assignment to $tmp is dead and can be removed.
Loading history...
217
            if (!empty($this->csimwintargets[$i])) {
218
                $this->csimareas .= ' target="' . $this->csimwintargets[$i] . '" ';
219
            }
220
            if (!empty($this->csimalts[$i])) {
221
                $tmp = sprintf($this->csimalts[$i], $this->data[$i]);
222
                $this->csimareas .= " title=\"${tmp}\" alt=\"${tmp}\" ";
223
            }
224
            $this->csimareas .= " />\n";
225
        }
226
    }
227
228
    public function SetTheme($aTheme)
229
    {
230
        //        Util\JpGraphError::RaiseL(15012,$aTheme);
231
        //        return;
232
233
        if (in_array($aTheme, array_keys($this->themearr), true)) {
234
            $this->theme               = $aTheme;
235
            $this->is_using_plot_theme = true;
236
        } else {
237
            Util\JpGraphError::RaiseL(15001, $aTheme); //("PiePLot::SetTheme() Unknown theme: $aTheme");
238
        }
239
    }
240
241
    public function ExplodeSlice($e, $radius = 20)
242
    {
243
        if (!is_integer($e)) {
244
            Util\JpGraphError::RaiseL(15002);
245
        }
246
        //('Argument to PiePlot::ExplodeSlice() must be an integer');
247
        $this->explode_radius[$e] = $radius;
248
    }
249
250
    public function ExplodeAll($radius = 20)
251
    {
252
        $this->explode_all = true;
253
        $this->explode_r   = $radius;
254
    }
255
256
    public function Explode($aExplodeArr)
257
    {
258
        if (!is_array($aExplodeArr)) {
259
            Util\JpGraphError::RaiseL(15003);
260
            //("Argument to PiePlot::Explode() must be an array with integer distances.");
261
        }
262
        $this->explode_radius = $aExplodeArr;
263
    }
264
265
    public function SetStartAngle($aStart)
266
    {
267
        if ($aStart < 0 || $aStart > 360) {
268
            Util\JpGraphError::RaiseL(15004); //('Slice start angle must be between 0 and 360 degrees.');
269
        }
270
        if ($aStart == 0) {
271
            $this->startangle = 0;
272
        } else {
273
            $this->startangle = 360 - $aStart;
274
            $this->startangle *= M_PI / 180;
275
        }
276
    }
277
278
    // Size in percentage
279
    public function SetSize($aSize)
280
    {
281
        if (($aSize > 0 && $aSize <= 0.5) || ($aSize > 10 && $aSize < 1000)) {
282
            $this->radius = $aSize;
283
        } else {
284
            Util\JpGraphError::RaiseL(15006);
285
        }
286
287
        //("PiePlot::SetSize() Radius for pie must either be specified as a fraction [0, 0.5] of the size of the image or as an absolute size in pixels  in the range [10, 1000]");
288
    }
289
290
    // Set label arrays
291
    public function SetLegends($aLegend)
292
    {
293
        $this->legends = $aLegend;
294
    }
295
296
    // Set text labels for slices
297
    public function SetLabels($aLabels, $aLblPosAdj = 'auto')
298
    {
299
        $this->labels       = array_reverse($aLabels);
300
        $this->ilabelposadj = $aLblPosAdj;
301
    }
302
303
    public function SetLabelPos($aLblPosAdj)
304
    {
305
        $this->ilabelposadj = $aLblPosAdj;
306
    }
307
308
    // Should we display actual value or percentage?
309
    public function SetLabelType($aType)
310
    {
311
        if ($aType < 0 || $aType > 2) {
312
            Util\JpGraphError::RaiseL(15008, $aType);
313
        }
314
315
        //("PiePlot::SetLabelType() Type for pie plots must be 0 or 1 (not $t).");
316
        $this->labeltype = $aType;
317
    }
318
319
    // Deprecated.
320
    public function SetValueType($aType)
321
    {
322
        $this->SetLabelType($aType);
323
    }
324
325
    // Should the circle around a pie plot be displayed
326
    public function ShowBorder($exterior = true, $interior = true)
327
    {
328
        $this->pie_border          = $exterior;
329
        $this->pie_interior_border = $interior;
330
    }
331
332
    // Setup the legends
333
    public function Legend($graph)
334
    {
335
        $colors = array_keys($graph->img->rgb->rgb_table);
336
        sort($colors);
337
        $ta = $this->themearr[$this->theme];
338
        $n  = safe_count($this->data);
339
340
        if ($this->setslicecolors == null) {
341
            $numcolors = safe_count($ta);
342
            if (($this instanceof PiePlot3D)) {
343
                $ta = array_reverse(array_slice($ta, 0, $n));
344
            }
345
        } else {
346
            $this->setslicecolors = array_slice($this->setslicecolors, 0, $n);
347
            $numcolors            = safe_count($this->setslicecolors);
348
            if ($graph->pieaa && !($this instanceof PiePlot3D)) {
349
                $this->setslicecolors = array_reverse($this->setslicecolors);
350
            }
351
        }
352
353
        $sum = 0;
354
        for ($i = 0; $i < $n; ++$i) {
355
            $sum += $this->data[$i];
356
        }
357
358
        // Bail out with error if the sum is 0
359
        if ($sum == 0) {
0 ignored issues
show
The condition $sum == 0 is always true.
Loading history...
360
            Util\JpGraphError::RaiseL(15009);
361
        }
362
        //("Illegal pie plot. Sum of all data is zero for Pie!");
363
364
        // Make sure we don't plot more values than data points
365
        // (in case the user added more legends than data points)
366
        $n = min(safe_count($this->legends), safe_count($this->data));
367
        if ($this->legends != '') {
368
            $this->legends = array_reverse(array_slice($this->legends, 0, $n));
369
        }
370
        for ($i = $n - 1; $i >= 0; --$i) {
371
            $l = $this->legends[$i];
372
            // Replace possible format with actual values
373
            if (safe_count($this->csimalts) > $i) {
374
                $fmt = $this->csimalts[$i];
375
            } else {
376
                $fmt = '%d'; // Deafult Alt if no other has been specified
377
            }
378
            if ($this->labeltype == 0) {
379
                $l   = sprintf($l, 100 * $this->data[$i] / $sum);
380
                $alt = sprintf($fmt, $this->data[$i]);
381
            } elseif ($this->labeltype == 1) {
382
                $l   = sprintf($l, $this->data[$i]);
383
                $alt = sprintf($fmt, $this->data[$i]);
384
            } else {
385
                $l   = sprintf($l, $this->adjusted_data[$i]);
386
                $alt = sprintf($fmt, $this->adjusted_data[$i]);
387
            }
388
389
            if (empty($this->csimwintargets[$i])) {
390
                $wintarg = '';
391
            } else {
392
                $wintarg = $this->csimwintargets[$i];
393
            }
394
395
            $imageMapTarget = $this->csimtargets[$i] ?? '';
396
            if ($this->setslicecolors == null) {
397
                $graph->legend->Add($l, $colors[$ta[$i % $numcolors]], '', 0, $imageMapTarget, $alt, $wintarg);
398
            } else {
399
                $graph->legend->Add($l, $this->setslicecolors[$i % $numcolors], '', 0, $imageMapTarget, $alt, $wintarg);
400
            }
401
        }
402
    }
403
404
    // Adjust the rounded percetage value so that the sum of
405
    // of the pie slices are always 100%
406
    // Using the Hare/Niemeyer method
407
    public function AdjPercentage($aData, $aPrec = 0)
408
    {
409
        $mul = 100;
410
        if ($aPrec > 0 && $aPrec < 3) {
411
            if ($aPrec == 1) {
412
                $mul = 1000;
413
            } else {
414
                $mul = 10000;
415
            }
416
        }
417
418
        $tmp       = [];
419
        $result    = [];
420
        $quote_sum = 0;
421
        $n         = safe_count($aData);
422
        for ($i = 0, $sum = 0; $i < $n; ++$i) {
423
            $sum += $aData[$i];
424
        }
425
426
        foreach ($aData as $index => $value) {
427
            $tmp_percentage = $value / $sum * $mul;
428
            $result[$index] = floor($tmp_percentage);
429
            $tmp[$index]    = $tmp_percentage - $result[$index];
430
            $quote_sum += $result[$index];
431
        }
432
        if ($quote_sum == $mul) {
433
            if ($mul > 100) {
434
                $tmp = $mul / 100;
435
                for ($i = 0; $i < $n; ++$i) {
436
                    $result[$i] /= $tmp;
437
                }
438
            }
439
440
            return $result;
441
        }
442
        arsort($tmp, SORT_NUMERIC);
443
        reset($tmp);
444
        for ($i = 0; $i < $mul - $quote_sum; ++$i) {
445
            ++$result[key($tmp)];
446
            next($tmp);
447
        }
448
        if ($mul > 100) {
449
            $tmp = $mul / 100;
450
            for ($i = 0; $i < $n; ++$i) {
451
                $result[$i] /= $tmp;
452
            }
453
        }
454
455
        return $result;
456
    }
457
458
    public function Stroke($img, $aaoption = 0)
459
    {
460
        // aaoption is used to handle antialias
461
        // aaoption == 0 a normal pie
462
        // aaoption == 1 just the body
463
        // aaoption == 2 just the values
464
465
        // Explode scaling. If anti alias we scale the image
466
        // twice and we also need to scale the exploding distance
467
        $expscale = $aaoption === 1 ? 2 : 1;
468
469
        if ($this->labeltype == 2) {
470
            // Adjust the data so that it will add up to 100%
471
            $this->adjusted_data = $this->AdjPercentage($this->data);
472
        }
473
474
        if ($this->use_plot_theme_colors) {
475
            $this->setslicecolors = null;
476
        }
477
478
        $colors = array_keys($img->rgb->rgb_table);
479
        sort($colors);
480
        $ta = $this->themearr[$this->theme];
481
        $n  = safe_count($this->data);
482
483
        if ($this->setslicecolors == null) {
484
            $numcolors = safe_count($ta);
485
        } else {
486
            // We need to create an array of colors as long as the data
487
            // since we need to reverse it to get the colors in the right order
488
            $numcolors = safe_count($this->setslicecolors);
489
            $i         = 2 * $numcolors;
490
            while ($n > $i) {
491
                $this->setslicecolors = array_merge($this->setslicecolors, $this->setslicecolors);
492
                $i += $n;
493
            }
494
            $tt                   = array_slice($this->setslicecolors, 0, $n % $numcolors);
495
            $this->setslicecolors = array_merge($this->setslicecolors, $tt);
496
            $this->setslicecolors = array_reverse($this->setslicecolors);
497
        }
498
499
        // Draw the slices
500
        $sum = 0;
501
        for ($i = 0; $i < $n; ++$i) {
502
            $sum += $this->data[$i];
503
        }
504
505
        // Bail out with error if the sum is 0
506
        if ($sum == 0) {
0 ignored issues
show
The condition $sum == 0 is always true.
Loading history...
507
            Util\JpGraphError::RaiseL(15009); //("Sum of all data is 0 for Pie.");
508
        }
509
510
        // Set up the pie-circle
511
        if ($this->radius <= 1) {
512
            $radius = floor($this->radius * min($img->width, $img->height));
513
        } else {
514
            $radius = $aaoption === 1 ? $this->radius * 2 : $this->radius;
515
        }
516
517
        if ($this->posx <= 1 && $this->posx > 0) {
518
            $xc = round($this->posx * $img->width);
519
        } else {
520
            $xc = $this->posx;
521
        }
522
523
        if ($this->posy <= 1 && $this->posy > 0) {
524
            $yc = round($this->posy * $img->height);
525
        } else {
526
            $yc = $this->posy;
527
        }
528
529
        $n = safe_count($this->data);
530
531
        if ($this->explode_all) {
532
            for ($i = 0; $i < $n; ++$i) {
533
                $this->explode_radius[$i] = $this->explode_r;
534
            }
535
        }
536
537
        // If we have a shadow and not just drawing the labels
538
        if ($this->ishadowcolor != '' && $aaoption !== 2) {
539
            $accsum = 0;
540
            $angle2 = $this->startangle;
541
            $img->SetColor($this->ishadowcolor);
542
            for ($i = 0; $sum > 0 && $i < $n; ++$i) {
543
                $j      = $n - $i - 1;
544
                $d      = $this->data[$i];
545
                $angle1 = $angle2;
546
                $accsum += $d;
547
                $angle2 = $this->startangle + 2 * M_PI * $accsum / $sum;
548
                if (empty($this->explode_radius[$j])) {
549
                    $this->explode_radius[$j] = 0;
550
                }
551
552
                if ($d < 0.00001) {
553
                    continue;
554
                }
555
556
                $la = 2 * M_PI - (abs($angle2 - $angle1) / 2.0 + $angle1);
557
558
                $xcm = $xc + $this->explode_radius[$j] * cos($la) * $expscale;
559
                $ycm = $yc - $this->explode_radius[$j] * sin($la) * $expscale;
560
561
                $xcm += $this->ishadowdrop * $expscale;
562
                $ycm += $this->ishadowdrop * $expscale;
563
564
                $_sa = round($angle1 * 180 / M_PI);
565
                $_ea = round($angle2 * 180 / M_PI);
566
567
                // The CakeSlice method draws a full circle in case of start angle = end angle
568
                // for pie slices we don't want this behaviour unless we only have one
569
                // slice in the pie in case it is the wanted behaviour
570
                if ($_ea - $_sa > 0.1 || $n == 1) {
571
                    $img->CakeSlice(
572
                        $xcm,
573
                        $ycm,
574
                        $radius - 1,
575
                        $radius - 1,
576
                        $angle1 * 180 / M_PI,
577
                        $angle2 * 180 / M_PI,
578
                        $this->ishadowcolor
579
                    );
580
                }
581
            }
582
        }
583
584
        /**
585
         * This is the main loop to draw each cake slice.
586
         */
587
        // Set up the accumulated sum, start angle for first slice and border color
588
        $accsum = 0;
589
        $angle2 = $this->startangle;
590
        $img->SetColor($this->color);
591
592
        // Loop though all the slices if there is a pie to draw (sum>0)
593
        // There are n slices in total
594
        for ($i = 0; $sum > 0 && $i < $n; ++$i) {
595
            // $j is the actual index used for the slice
596
            $j = $n - $i - 1;
597
598
            // Make sure we havea  valid distance to explode the slice
599
            if (empty($this->explode_radius[$j])) {
600
                $this->explode_radius[$j] = 0;
601
            }
602
603
            // The actual numeric value for the slice
604
            $d = $this->data[$i];
605
606
            $angle1 = $angle2;
607
608
            // Accumlate the sum
609
            $accsum += $d;
610
611
            // The new angle when we add the "size" of this slice
612
            // angle1 is then the start and angle2 the end of this slice
613
            $angle2 = $this->NormAngle($this->startangle + 2 * M_PI * $accsum / $sum);
614
615
            // We avoid some trouble by not allowing end angle to be 0, in that case
616
            // we translate to 360
617
618
            // la is used to hold the label angle, which is centered on the slice
619
            if ($angle2 < 0.0001 && $angle1 > 0.0001) {
620
                $this->la[$i] = 2 * M_PI - (abs(2 * M_PI - $angle1) / 2.0 + $angle1);
621
            } elseif ($angle1 > $angle2) {
622
                // The case where the slice crosses the 3 a'clock line
623
                // Remember that the slices are counted clockwise and
624
                // labels are counted counter clockwise so we need to revert with 2 PI
625
                $this->la[$i] = 2 * M_PI - $this->NormAngle($angle1 + ((2 * M_PI - $angle1) + $angle2) / 2);
626
            } else {
627
                $this->la[$i] = 2 * M_PI - (abs($angle2 - $angle1) / 2.0 + $angle1);
628
            }
629
630
            // Too avoid rounding problems we skip the slice if it is too small
631
            if ($d < 0.00001) {
632
                continue;
633
            }
634
635
            // If the user has specified an array of colors for each slice then use
636
            // that a color otherwise use the theme array (ta) of colors
637
            if ($this->setslicecolors == null) {
638
                $slicecolor = $colors[$ta[$i % $numcolors]];
639
            } else {
640
                $slicecolor = $this->setslicecolors[$i % $numcolors];
641
            }
642
643
            //            $_sa = round($angle1*180/M_PI);
644
            //            $_ea = round($angle2*180/M_PI);
645
            //            $_la = round($this->la[$i]*180/M_PI);
646
            //            echo "Slice#$i: ang1=$_sa , ang2=$_ea, la=$_la, color=$slicecolor<br>";
647
648
            // If we have enabled antialias then we don't draw any border so
649
            // make the bordedr color the same as the slice color
650
            if ($this->pie_interior_border && $aaoption === 0) {
651
                $img->SetColor($this->color);
652
            } else {
653
                $img->SetColor($slicecolor);
654
            }
655
            $arccolor = $this->pie_border && $aaoption === 0 ? $this->color : '';
656
657
            // Calculate the x,y coordinates for the base of this slice taking
658
            // the exploded distance into account. Here we use the mid angle as the
659
            // ray of extension and we have the mid angle handy as it is also the
660
            // label angle
661
            $xcm = $xc + $this->explode_radius[$j] * cos($this->la[$i]) * $expscale;
662
            $ycm = $yc - $this->explode_radius[$j] * sin($this->la[$i]) * $expscale;
663
664
            // If we are not just drawing the labels then draw this cake slice
665
            if ($aaoption !== 2) {
666
                $_sa = round($angle1 * 180 / M_PI);
667
                $_ea = round($angle2 * 180 / M_PI);
668
                $_la = round($this->la[$i] * 180 / M_PI);
0 ignored issues
show
The assignment to $_la is dead and can be removed.
Loading history...
669
                //echo "[$i] sa=$_sa, ea=$_ea, la[$i]=$_la, (color=$slicecolor)<br>";
670
671
                // The CakeSlice method draws a full circle in case of start angle = end angle
672
                // for pie slices we want this in case the slice have a value larger than 99% of the
673
                // total sum
674
                if (abs($_ea - $_sa) >= 1 || $d == $sum) {
675
                    $img->CakeSlice($xcm, $ycm, $radius - 1, $radius - 1, $_sa, $_ea, $slicecolor, $arccolor);
676
                }
677
            }
678
679
            // If the CSIM is used then make sure we register a CSIM area for this slice as well
680
            if ($this->csimtargets && $aaoption !== 1) {
681
                $this->AddSliceToCSIM($i, $xcm, $ycm, $radius, $angle1, $angle2);
682
            }
683
        }
684
685
        // Format the titles for each slice
686
        if ($aaoption !== 2) {
687
            for ($i = 0; $i < $n; ++$i) {
688
                if ($this->labeltype == 0) {
689
                    if ($sum != 0) {
690
                        $l = 100.0 * $this->data[$i] / $sum;
691
                    } else {
692
                        $l = 0.0;
693
                    }
694
                } elseif ($this->labeltype == 1) {
695
                    $l = $this->data[$i] * 1.0;
696
                } else {
697
                    $l = $this->adjusted_data[$i];
698
                }
699
                if (isset($this->labels[$i]) && is_string($this->labels[$i])) {
700
                    $this->labels[$i] = sprintf($this->labels[$i], $l);
701
                } else {
702
                    $this->labels[$i] = $l;
703
                }
704
            }
705
        }
706
707
        if ($this->value->show && $aaoption !== 1) {
708
            $this->StrokeAllLabels($img, $xc, $yc, $radius);
709
        }
710
711
        // Adjust title position
712
        if ($aaoption !== 1) {
713
            $this->title->SetPos(
714
                $xc,
715
                $yc - $this->title->GetFontHeight($img) - $radius - $this->title->margin,
716
                'center',
717
                'bottom'
718
            );
719
            $this->title->Stroke($img);
720
        }
721
    }
722
723
    /**
724
     * PRIVATE METHODS.
725
     *
726
     * @param mixed $a
727
     */
728
    public function NormAngle($a)
729
    {
730
        while ($a < 0) {
731
            $a += 2 * M_PI;
732
        }
733
734
        while ($a > 2 * M_PI) {
735
            $a -= 2 * M_PI;
736
        }
737
738
        return $a;
739
    }
740
741
    public function Quadrant($a)
742
    {
743
        $a = $this->NormAngle($a);
744
        if ($a > 0 && $a <= M_PI / 2) {
745
            return 0;
746
        }
747
748
        if ($a > M_PI / 2 && $a <= M_PI) {
749
            return 1;
750
        }
751
752
        if ($a > M_PI && $a <= 1.5 * M_PI) {
753
            return 2;
754
        }
755
756
        if ($a > 1.5 * M_PI) {
757
            return 3;
758
        }
759
    }
760
761
    public function StrokeGuideLabels($img, $xc, $yc, $radius)
762
    {
763
        $n = safe_count($this->labels);
764
765
        /**
766
         * Step 1 of the algorithm is to construct a number of clusters
767
         * // a cluster is defined as all slices within the same quadrant (almost)
768
         * // that has an angular distance less than the treshold.
769
         */
770
        $tresh_hold = 25 * M_PI / 180; // 25 degrees difference to be in a cluster
771
        $incluster  = false; // flag if we are currently in a cluster or not
772
        $clusters   = []; // array of clusters
773
        $cidx       = -1; // running cluster index
774
775
        // Go through all the labels and construct a number of clusters
776
        for ($i = 0; $i < $n - 1; ++$i) {
777
            // Calc the angle distance between two consecutive slices
778
            $a1   = $this->la[$i];
779
            $a2   = $this->la[$i + 1];
780
            $q1   = $this->Quadrant($a1);
781
            $q2   = $this->Quadrant($a2);
782
            $diff = abs($a1 - $a2);
783
            if ($diff < $tresh_hold) {
784
                if ($incluster) {
785
                    ++$clusters[$cidx][1];
786
                    // Each cluster can only cover one quadrant
787
                    // Do we cross a quadrant ( and must break the cluster)
788
                    if ($q1 != $q2) {
789
                        // If we cross a quadrant boundary we normally start a
790
                        // new cluster. However we need to take the 12'a clock
791
                        // and 6'a clock positions into a special consideration.
792
                        // Case 1: WE go from q=1 to q=2 if the last slice on
793
                        // the cluster for q=1 is close to 12'a clock and the
794
                        // first slice in q=0 is small we extend the previous
795
                        // cluster
796
                        if ($q1 == 1 && $q2 == 0 && $a2 > (90 - 15) * M_PI / 180) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $q2 of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
797
                            if ($i < $n - 2) {
798
                                $a3 = $this->la[$i + 2];
799
                                // If there isn't a cluster coming up with the next-next slice
800
                                // we extend the previous cluster to cover this slice as well
801
                                if (abs($a3 - $a2) >= $tresh_hold) {
802
                                    ++$clusters[$cidx][1];
803
                                    ++$i;
804
                                }
805
                            }
806
                        } elseif ($q1 == 3 && $q2 == 2 && $a2 > (270 - 15) * M_PI / 180) {
807
                            if ($i < $n - 2) {
808
                                $a3 = $this->la[$i + 2];
809
                                // If there isn't a cluster coming up with the next-next slice
810
                                // we extend the previous cluster to cover this slice as well
811
                                if (abs($a3 - $a2) >= $tresh_hold) {
812
                                    ++$clusters[$cidx][1];
813
                                    ++$i;
814
                                }
815
                            }
816
                        }
817
818
                        if ($q1 == 2 && $q2 == 1 && $a2 > (180 - 15) * M_PI / 180) {
819
                            ++$clusters[$cidx][1];
820
                            ++$i;
821
                        }
822
823
                        $incluster = false;
824
                    }
825
                } elseif ($q1 == $q2) {
826
                    $incluster = true;
827
                    // Now we have a special case for quadrant 0. If we previously
828
                    // have a cluster of one in quadrant 0 we just extend that
829
                    // cluster. If we don't do this then we risk that the label
830
                    // for the cluster of one will cross the guide-line
831
                    if ($q1 == 0 && $cidx > -1 &&
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $q1 of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
832
                        $clusters[$cidx][1] == 1 &&
833
                        $this->Quadrant($this->la[$clusters[$cidx][0]]) == 0) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->Quadrant($this->la[$clusters[$cidx][0]]) of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
834
                        ++$clusters[$cidx][1];
835
                    } else {
836
                        ++$cidx;
837
                        $clusters[$cidx][0] = $i;
838
                        $clusters[$cidx][1] = 1;
839
                    }
840
                } else {
841
                    // Create a "cluster" of one since we are just crossing
842
                    // a quadrant
843
                    ++$cidx;
844
                    $clusters[$cidx][0] = $i;
845
                    $clusters[$cidx][1] = 1;
846
                }
847
            } else {
848
                if ($incluster) {
849
                    // Add the last slice
850
                    ++$clusters[$cidx][1];
851
                    $incluster = false;
852
                } else {
853
                    // Create a "cluster" of one
854
                    ++$cidx;
855
                    $clusters[$cidx][0] = $i;
856
                    $clusters[$cidx][1] = 1;
857
                }
858
            }
859
        }
860
        // Handle the very last slice
861
        if ($incluster) {
862
            ++$clusters[$cidx][1];
863
        } else {
864
            // Create a "cluster" of one
865
            ++$cidx;
866
            $clusters[$cidx][0] = $i;
867
            $clusters[$cidx][1] = 1;
868
        }
869
870
        /*
871
        if( true ) {
872
        // Debug printout in labels
873
        for( $i=0; $i <= $cidx; ++$i ) {
874
        for( $j=0; $j < $clusters[$i][1]; ++$j ) {
875
        $a = $this->la[$clusters[$i][0]+$j];
876
        $aa = round($a*180/M_PI);
877
        $q = $this->Quadrant($a);
878
        $this->labels[$clusters[$i][0]+$j]="[$q:$aa] $i:$j";
879
        }
880
        }
881
        }
882
         */
883
884
        /*
885
         * Step 2 of the algorithm is use the clusters and draw the labels
886
         * // and guidelines
887
         */
888
        // We use the font height as the base factor for how far we need to
889
        // spread the labels in the Y-direction.
890
        $this->value->ApplyFont($img);
891
        $fh        = $img->GetFontHeight();
892
        $origvstep = $fh * $this->iGuideVFactor;
893
        $this->value->SetMargin(0);
894
895
        // Number of clusters found
896
        $nc = safe_count($clusters);
897
898
        // Walk through all the clusters
899
        for ($i = 0; $i < $nc; ++$i) {
900
            // Start angle and number of slices in this cluster
901
            $csize = $clusters[$i][1];
902
            $a     = $this->la[$clusters[$i][0]];
903
            $q     = $this->Quadrant($a);
904
905
            // Now set up the start and end conditions to make sure that
906
            // in each cluster we walk through the all the slices starting with the slice
907
            // closest to the equator. Since all slices are numbered clockwise from "3'a clock"
908
            // we have different conditions depending on in which quadrant the slice lies within.
909
            if ($q == 0) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $q of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
910
                $start = $csize - 1;
911
                $idx   = $start;
912
                $step  = -1;
913
                $vstep = -$origvstep;
914
            } elseif ($q == 1) {
915
                $start = 0;
916
                $idx   = $start;
917
                $step  = 1;
918
                $vstep = -$origvstep;
919
            } elseif ($q == 2) {
920
                $start = $csize - 1;
921
                $idx   = $start;
922
                $step  = -1;
923
                $vstep = $origvstep;
924
            } elseif ($q == 3) {
925
                $start = 0;
926
                $idx   = $start;
927
                $step  = 1;
928
                $vstep = $origvstep;
929
            }
930
931
            // Walk through all slices within this cluster
932
            for ($j = 0; $j < $csize; ++$j) {
933
                // Now adjust the position of the labels in each cluster starting
934
                // with the slice that is closest to the equator of the pie
935
                $a = $this->la[$clusters[$i][0] + $idx];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $idx does not seem to be defined for all execution paths leading up to this point.
Loading history...
936
937
                // Guide line start in the center of the arc of the slice
938
                $r = $radius + $this->explode_radius[$n - 1 - ($clusters[$i][0] + $idx)];
939
                $x = round($r * cos($a) + $xc);
940
                $y = round($yc - $r * sin($a));
941
942
                // The distance from the arc depends on chosen font and the "R-Factor"
943
                $r += $fh * $this->iGuideLineRFactor;
944
945
                // Should the labels be placed curved along the pie or in straight columns
946
                // outside the pie?
947
                if ($this->iGuideLineCurve) {
948
                    $xt = round($r * cos($a) + $xc);
949
                }
950
951
                // If this is the first slice in the cluster we need some first time
952
                // proessing
953
                if ($idx == $start) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $start does not seem to be defined for all execution paths leading up to this point.
Loading history...
954
                    if (!$this->iGuideLineCurve) {
955
                        $xt = round($r * cos($a) + $xc);
956
                    }
957
958
                    $yt = round($yc - $r * sin($a));
959
960
                    // Some special consideration in case this cluster starts
961
                    // in quadrant 1 or 3 very close to the "equator" (< 20 degrees)
962
                    // and the previous clusters last slice is within the tolerance.
963
                    // In that case we add a font height to this labels Y-position
964
                    // so it doesn't collide with
965
                    // the slice in the previous cluster
966
                    $prevcluster = ($i + ($nc - 1)) % $nc;
967
                    $previdx     = $clusters[$prevcluster][0] + $clusters[$prevcluster][1] - 1;
968
                    if ($q == 1 && $a > 160 * M_PI / 180) {
969
                        // Get the angle for the previous clusters last slice
970
                        $diff = abs($a - $this->la[$previdx]);
971
                        if ($diff < $tresh_hold) {
972
                            $yt -= $fh;
973
                        }
974
                    } elseif ($q == 3 && $a > 340 * M_PI / 180) {
975
                        // We need to subtract 360 to compare angle distance between
976
                        // q=0 and q=3
977
                        $diff = abs($a - $this->la[$previdx] - 360 * M_PI / 180);
978
                        if ($diff < $tresh_hold) {
979
                            $yt += $fh;
980
                        }
981
                    }
982
                } else {
983
                    // The step is at minimum $vstep but if the slices are relatively large
984
                    // we make sure that we add at least a step that corresponds to the vertical
985
                    // distance between the centers at the arc on the slice
986
                    $prev_a = $this->la[$clusters[$i][0] + ($idx - $step)];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $step does not seem to be defined for all execution paths leading up to this point.
Loading history...
987
                    $dy     = abs($radius * (sin($a) - sin($prev_a)) * 1.2);
988
                    if ($vstep > 0) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $vstep does not seem to be defined for all execution paths leading up to this point.
Loading history...
989
                        $yt += max($vstep, $dy);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $yt does not seem to be defined for all execution paths leading up to this point.
Loading history...
990
                    } else {
991
                        $yt += min($vstep, -$dy);
992
                    }
993
                }
994
995
                $label = $this->labels[$clusters[$i][0] + $idx];
996
997
                if ($csize == 1) {
998
                    // A "meta" cluster with only one slice
999
                    $r  = $radius + $this->explode_radius[$n - 1 - ($clusters[$i][0] + $idx)];
1000
                    $rr = $r + $img->GetFontHeight() / 2;
1001
                    $xt = round($rr * cos($a) + $xc);
1002
                    $yt = round($yc - $rr * sin($a));
1003
                    $this->StrokeLabel($label, $img, $xc, $yc, $a, $r);
1004
                    if ($this->iShowGuideLineForSingle) {
1005
                        $this->guideline->Stroke($img, $x, $y, $xt, $yt);
1006
                    }
1007
                } else {
1008
                    $this->guideline->Stroke($img, $x, $y, $xt, $yt);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $xt does not seem to be defined for all execution paths leading up to this point.
Loading history...
1009
                    if ($q == 1 || $q == 2) {
1010
                        // Left side of Pie
1011
                        $this->guideline->Stroke($img, $xt, $yt, $xt - $this->guidelinemargin, $yt);
1012
                        $lbladj              = -$this->guidelinemargin - 5;
1013
                        $this->value->halign = 'right';
1014
                        $this->value->valign = 'center';
1015
                    } else {
1016
                        // Right side of pie
1017
                        $this->guideline->Stroke($img, $xt, $yt, $xt + $this->guidelinemargin, $yt);
1018
                        $lbladj              = $this->guidelinemargin + 5;
1019
                        $this->value->halign = 'left';
1020
                        $this->value->valign = 'center';
1021
                    }
1022
                    $this->value->Stroke($img, $label, $xt + $lbladj, $yt);
1023
                }
1024
1025
                // Udate idx to point to next slice in the cluster to process
1026
                $idx += $step;
1027
            }
1028
        }
1029
    }
1030
1031
    public function StrokeAllLabels($img, $xc, $yc, $radius)
1032
    {
1033
        // First normalize all angles for labels
1034
        $n = safe_count($this->la);
1035
        for ($i = 0; $i < $n; ++$i) {
1036
            $this->la[$i] = $this->NormAngle($this->la[$i]);
1037
        }
1038
        if ($this->guideline->iShow) {
1039
            $this->StrokeGuideLabels($img, $xc, $yc, $radius);
1040
        } else {
1041
            $n = safe_count($this->labels);
1042
            for ($i = 0; $i < $n; ++$i) {
1043
                $this->StrokeLabel(
1044
                    $this->labels[$i],
1045
                    $img,
1046
                    $xc,
1047
                    $yc,
1048
                    $this->la[$i],
1049
                    $radius + $this->explode_radius[$n - 1 - $i]
1050
                );
1051
            }
1052
        }
1053
    }
1054
1055
    // Position the labels of each slice
1056
    public function StrokeLabel($label, $img, $xc, $yc, $a, $r)
1057
    {
1058
        // Default value
1059
        if ($this->ilabelposadj === 'auto') {
0 ignored issues
show
The condition $this->ilabelposadj === 'auto' is always false.
Loading history...
1060
            $this->ilabelposadj = 0.65;
1061
        }
1062
1063
        // We position the values diferently depending on if they are inside
1064
        // or outside the pie
1065
        if ($this->ilabelposadj < 1.0) {
1066
            $this->value->SetAlign('center', 'center');
1067
            $this->value->margin = 0;
1068
1069
            $xt = round($this->ilabelposadj * $r * cos($a) + $xc);
1070
            $yt = round($yc - $this->ilabelposadj * $r * sin($a));
1071
1072
            $this->value->Stroke($img, $label, $xt, $yt);
1073
        } else {
1074
            $this->value->halign = 'left';
1075
            $this->value->valign = 'top';
1076
            $this->value->margin = 0;
1077
1078
            // Position the axis title.
1079
            // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
1080
            // that intersects with the extension of the corresponding axis. The code looks a little
1081
            // bit messy but this is really the only way of having a reasonable position of the
1082
            // axis titles.
1083
            $this->value->ApplyFont($img);
1084
            $h = $img->GetTextHeight($label);
1085
            // For numeric values the format of the display value
1086
            // must be taken into account
1087
            if (is_numeric($label)) {
1088
                if ($label > 0) {
1089
                    $w = $img->GetTextWidth(sprintf($this->value->format, $label));
1090
                } else {
1091
                    $w = $img->GetTextWidth(sprintf($this->value->negformat, $label));
1092
                }
1093
            } else {
1094
                $w = $img->GetTextWidth($label);
1095
            }
1096
1097
            if ($this->ilabelposadj > 1.0 && $this->ilabelposadj < 5.0) {
1098
                $r *= $this->ilabelposadj;
1099
            }
1100
1101
            $r += $img->GetFontHeight() / 1.5;
1102
1103
            $xt = round($r * cos($a) + $xc);
1104
            $yt = round($yc - $r * sin($a));
1105
1106
            // Normalize angle
1107
            while ($a < 0) {
1108
                $a += 2 * M_PI;
1109
            }
1110
1111
            while ($a > 2 * M_PI) {
1112
                $a -= 2 * M_PI;
1113
            }
1114
1115
            if ($a >= 7 * M_PI / 4 || $a <= M_PI / 4) {
1116
                $dx = 0;
1117
            }
1118
1119
            if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) {
1120
                $dx = ($a - M_PI / 4) * 2 / M_PI;
1121
            }
1122
1123
            if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) {
1124
                $dx = 1;
1125
            }
1126
1127
            if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) {
1128
                $dx = (1 - ($a - M_PI * 5 / 4) * 2 / M_PI);
1129
            }
1130
1131
            if ($a >= 7 * M_PI / 4) {
1132
                $dy = (($a - M_PI) - 3 * M_PI / 4) * 2 / M_PI;
1133
            }
1134
1135
            if ($a <= M_PI / 4) {
1136
                $dy = (1 - $a * 2 / M_PI);
1137
            }
1138
1139
            if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) {
1140
                $dy = 1;
1141
            }
1142
1143
            if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) {
1144
                $dy = (1 - ($a - 3 * M_PI / 4) * 2 / M_PI);
1145
            }
1146
1147
            if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) {
1148
                $dy = 0;
1149
            }
1150
1151
            $this->value->Stroke($img, $label, $xt - $dx * $w, $yt - $dy * $h);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dy does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $dx does not seem to be defined for all execution paths leading up to this point.
Loading history...
1152
        }
1153
    }
1154
1155
    public function UsePlotThemeColors($flag = true)
1156
    {
1157
        $this->use_plot_theme_colors = $flag;
1158
    }
1159
} // @class
1160
1161
/* EOF */
1162