PiePlot3D::Pie3DSlice()   F
last analyzed

Complexity

Conditions 24
Paths 17

Size

Total Lines 200
Code Lines 142

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 138
CRAP Score 24.0016

Importance

Changes 0
Metric Value
cc 24
eloc 142
nc 17
nop 10
dl 0
loc 200
ccs 138
cts 140
cp 0.9857
crap 24.0016
rs 3.3333
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * JPGraph v4.0.3
5
 */
6
7
namespace Amenadiel\JpGraph\Plot;
8
9
use Amenadiel\JpGraph\Text;
10
use Amenadiel\JpGraph\Util;
11
12
/**
13
 * File:        JPGRAPH_PIE3D.PHP
14
 * // Description: 3D Pie plot extension for JpGraph
15
 * // Created:     2001-03-24
16
 * // Ver:         $Id: jpgraph_pie3d.php 1329 2009-06-20 19:23:30Z ljp $
17
 * //
18
 * // Copyright (c) Asial Corporation. All rights reserved.
19
 */
20
21
/**
22
 * @class PiePlot3D
23
 * // Description: Plots a 3D pie with a specified projection
24
 * // angle between 20 and 70 degrees.
25
 */
26
class PiePlot3D extends PiePlot
27
{
28
    private $labelhintcolor = 'red';
29
    private $showlabelhint  = true;
30
    private $angle          = 50;
31
    private $edgecolor      = '';
32
    private $edgeweight     = 1;
33
    private $iThickness     = false;
34
35
    /**
36
     * CONSTRUCTOR.
37
     *
38
     * @param mixed $data
39
     */
40 2
    public function __construct($data)
41
    {
42 2
        $this->radius = 0.5;
43 2
        $this->data   = $data;
44 2
        $this->title  = new Text\Text('');
45 2
        $this->title->SetFont(FF_FONT1, FS_BOLD);
46 2
        $this->value = new DisplayValue();
47 2
        $this->value->Show();
48 2
        $this->value->SetFormat('%.0f%%');
49 2
    }
50
51
    /**
52
     * PUBLIC METHODS.
53
     *
54
     * @param mixed $aLegend
55
     */
56
    // Set label arrays
57 2
    public function SetLegends($aLegend)
58
    {
59 2
        $this->legends = array_reverse(array_slice($aLegend, 0, safe_count($this->data)));
60 2
    }
61
62
    public function SetSliceColors($aColors)
63
    {
64
        $this->setslicecolors = $aColors;
65
    }
66
67 2
    public function Legend($aGraph)
68
    {
69 2
        parent::Legend($aGraph);
70 2
        $aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol);
71 2
    }
72
73
    public function SetCSIMTargets($aTargets, $aAlts = '', $aWinTargets = '')
74
    {
75
        $this->csimtargets    = $aTargets;
76
        $this->csimwintargets = $aWinTargets;
77
        $this->csimalts       = $aAlts;
78
    }
79
80
    // Should the slices be separated by a line? If color is specified as "" no line
81
    // will be used to separate pie slices.
82 1
    public function SetEdge($aColor = 'black', $aWeight = 1)
83
    {
84 1
        $this->edgecolor  = $aColor;
85 1
        $this->edgeweight = $aWeight;
86 1
    }
87
88
    // Specify projection angle for 3D in degrees
89
    // Must be between 20 and 70 degrees
90 2
    public function SetAngle($a)
91
    {
92 2
        if ($a < 5 || $a > 90) {
93
            Util\JpGraphError::RaiseL(14002);
94
        //("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees.");
95
        } else {
96 2
            $this->angle = $a;
97
        }
98 2
    }
99
100 2
    public function Add3DSliceToCSIM($i, $xc, $yc, $height, $width, $thick, $sa, $ea)
101
    {
102
        //Slice number, ellipse centre (x,y), height, width, start angle, end angle
103
104 2
        $sa *= M_PI / 180;
105 2
        $ea *= M_PI / 180;
106
107
        //add coordinates of the centre to the map
108 2
        $coords = "${xc}, ${yc}";
109
110
        //add coordinates of the first point on the arc to the map
111 2
        $xp = floor($width * cos($sa) / 2 + $xc);
112 2
        $yp = floor($yc - $height * sin($sa) / 2);
113 2
        $coords .= ", ${xp}, ${yp}";
114
115
        //If on the front half, add the thickness offset
116 2
        if ($sa >= M_PI && $sa <= 2 * M_PI * 1.01) {
117 2
            $yp = floor($yp + $thick);
118 2
            $coords .= ", ${xp}, ${yp}";
119
        }
120
121
        //add coordinates every 0.2 radians
122 2
        $a = $sa + 0.2;
123 2
        while ($a < $ea) {
124 2
            $xp = floor($width * cos($a) / 2 + $xc);
125 2
            if ($a >= M_PI && $a <= 2 * M_PI * 1.01) {
126 2
                $yp = floor($yc - ($height * sin($a) / 2) + $thick);
127
            } else {
128 2
                $yp = floor($yc - $height * sin($a) / 2);
129
            }
130 2
            $coords .= ", ${xp}, ${yp}";
131 2
            $a += 0.2;
132
        }
133
134
        //Add the last point on the arc
135 2
        $xp = floor($width * cos($ea) / 2 + $xc);
136 2
        $yp = floor($yc - $height * sin($ea) / 2);
137
138 2
        if ($ea >= M_PI && $ea <= 2 * M_PI * 1.01) {
139 2
            $coords .= ", ${xp}, " . floor($yp + $thick);
140
        }
141 2
        $coords .= ", ${xp}, ${yp}";
142 2
        $alt = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $alt is dead and can be removed.
Loading history...
143
144 2
        if (!empty($this->csimtargets[$i])) {
145
            $this->csimareas .= "<area shape=\"poly\" coords=\"${coords}\" href=\"" . $this->csimtargets[$i] . '"';
146
147
            if (!empty($this->csimwintargets[$i])) {
148
                $this->csimareas .= ' target="' . $this->csimwintargets[$i] . '" ';
149
            }
150
151
            if (!empty($this->csimalts[$i])) {
152
                $tmp = sprintf($this->csimalts[$i], $this->data[$i]);
153
                $this->csimareas .= "alt=\"${tmp}\" title=\"${tmp}\" ";
154
            }
155
            $this->csimareas .= " />\n";
156
        }
157 2
    }
158
159
    public function SetLabels($aLabels, $aLblPosAdj = 'auto')
160
    {
161
        $this->labels       = $aLabels;
162
        $this->ilabelposadj = $aLblPosAdj;
163
    }
164
165
    // Distance from the pie to the labels
166
    public function SetLabelMargin($m)
167
    {
168
        $this->value->SetMargin($m);
169
    }
170
171
    // Show a thin line from the pie to the label for a specific slice
172
    public function ShowLabelHint($f = true)
173
    {
174
        $this->showlabelhint = $f;
175
    }
176
177
    // Set color of hint line to label for each slice
178
    public function SetLabelHintColor($c)
179
    {
180
        $this->labelhintcolor = $c;
181
    }
182
183 1
    public function SetHeight($aHeight)
184
    {
185 1
        $this->iThickness = $aHeight;
186 1
    }
187
188
    // Normalize Angle between 0-360
189 2
    public function NormAngle($a)
190
    {
191
        // Normalize anle to 0 to 2M_PI
192
        //
193 2
        if ($a > 0) {
194 2
            while ($a > 360) {
195 1
                $a -= 360;
196
            }
197
        } else {
198 2
            while ($a < 0) {
199
                $a += 360;
200
            }
201
        }
202 2
        if ($a < 0) {
203
            $a = 360 + $a;
204
        }
205
206 2
        if ($a == 360) {
207 2
            $a = 0;
208
        }
209
210 2
        return $a;
211
    }
212
213
    // Draw one 3D pie slice at position ($xc,$yc) with height $z
214 2
    public function Pie3DSlice($img, $xc, $yc, $w, $h, $sa, $ea, $z, $fillcolor, $shadow = 0.65)
215
    {
216
        // Due to the way the 3D Pie algorithm works we are
217
        // guaranteed that any slice we get into this method
218
        // belongs to either the left or right side of the
219
        // pie ellipse. Hence, no slice will cross 90 or 270
220
        // point.
221 2
        if (($sa < 90 && $ea > 90) || (($sa > 90 && $sa < 270) && $ea > 270)) {
222
            Util\JpGraphError::RaiseL(14003); //('Internal assertion failed. Pie3D::Pie3DSlice');
223
            exit(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
224
        }
225
226 2
        $p[] = [];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$p was never initialized. Although not strictly required by PHP, it is generally a good practice to add $p = array(); before regardless.
Loading history...
227
228
        // Setup pre-calculated values
229 2
        $rsa   = $sa / 180 * M_PI; // to Rad
230 2
        $rea   = $ea / 180 * M_PI; // to Rad
231 2
        $sinsa = sin($rsa);
232 2
        $cossa = cos($rsa);
233 2
        $sinea = sin($rea);
234 2
        $cosea = cos($rea);
235
236
        // p[] is the points for the overall slice and
237
        // pt[] is the points for the top pie
238
239
        // Angular step when approximating the arc with a polygon train.
240 2
        $step = 0.05;
241
242 2
        if ($sa >= 270) {
243 2
            if ($ea > 360 || ($ea > 0 && $ea <= 90)) {
244 1
                if ($ea > 0 && $ea <= 90) {
245
                    // Adjust angle to simplify conditions in loops
246 1
                    $rea += 2 * M_PI;
247
                }
248
249 1
                $p = [$xc, $yc, $xc, $yc + $z,
250 1
                    $xc + $w * $cossa, $z + $yc - $h * $sinsa, ];
251 1
                $pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa];
252
253 1
                for ($a = $rsa; $a < 2 * M_PI; $a += $step) {
254 1
                    $tca  = cos($a);
255 1
                    $tsa  = sin($a);
256 1
                    $p[]  = $xc + $w * $tca;
257 1
                    $p[]  = $z + $yc - $h * $tsa;
258 1
                    $pt[] = $xc + $w * $tca;
259 1
                    $pt[] = $yc - $h * $tsa;
260
                }
261
262 1
                $pt[] = $xc + $w;
263 1
                $pt[] = $yc;
264
265 1
                $p[] = $xc + $w;
266 1
                $p[] = $z + $yc;
267 1
                $p[] = $xc + $w;
268 1
                $p[] = $yc;
269 1
                $p[] = $xc;
270 1
                $p[] = $yc;
271
272 1
                for ($a = 2 * M_PI + $step; $a < $rea; $a += $step) {
273 1
                    $pt[] = $xc + $w * cos($a);
274 1
                    $pt[] = $yc - $h * sin($a);
275
                }
276
277 1
                $pt[] = $xc + $w * $cosea;
278 1
                $pt[] = $yc - $h * $sinea;
279 1
                $pt[] = $xc;
280 1
                $pt[] = $yc;
281
            } else {
282 2
                $p = [$xc, $yc, $xc, $yc + $z,
283 2
                    $xc + $w * $cossa, $z + $yc - $h * $sinsa, ];
284 2
                $pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa];
285
286 2
                $rea = $rea == 0.0 ? 2 * M_PI : $rea;
287 2
                for ($a = $rsa; $a < $rea; $a += $step) {
288 2
                    $tca  = cos($a);
289 2
                    $tsa  = sin($a);
290 2
                    $p[]  = $xc + $w * $tca;
291 2
                    $p[]  = $z + $yc - $h * $tsa;
292 2
                    $pt[] = $xc + $w * $tca;
293 2
                    $pt[] = $yc - $h * $tsa;
294
                }
295
296 2
                $pt[] = $xc + $w * $cosea;
297 2
                $pt[] = $yc - $h * $sinea;
298 2
                $pt[] = $xc;
299 2
                $pt[] = $yc;
300
301 2
                $p[] = $xc + $w * $cosea;
302 2
                $p[] = $z + $yc - $h * $sinea;
303 2
                $p[] = $xc + $w * $cosea;
304 2
                $p[] = $yc - $h * $sinea;
305 2
                $p[] = $xc;
306 2
                $p[] = $yc;
307
            }
308 2
        } elseif ($sa >= 180) {
309 2
            $p  = [$xc, $yc, $xc, $yc + $z, $xc + $w * $cosea, $z + $yc - $h * $sinea];
310 2
            $pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea];
311
312 2
            for ($a = $rea; $a > $rsa; $a -= $step) {
313 2
                $tca  = cos($a);
314 2
                $tsa  = sin($a);
315 2
                $p[]  = $xc + $w * $tca;
316 2
                $p[]  = $z + $yc - $h * $tsa;
317 2
                $pt[] = $xc + $w * $tca;
318 2
                $pt[] = $yc - $h * $tsa;
319
            }
320
321 2
            $pt[] = $xc + $w * $cossa;
322 2
            $pt[] = $yc - $h * $sinsa;
323 2
            $pt[] = $xc;
324 2
            $pt[] = $yc;
325
326 2
            $p[] = $xc + $w * $cossa;
327 2
            $p[] = $z + $yc - $h * $sinsa;
328 2
            $p[] = $xc + $w * $cossa;
329 2
            $p[] = $yc - $h * $sinsa;
330 2
            $p[] = $xc;
331 2
            $p[] = $yc;
332 2
        } elseif ($sa >= 90) {
333 2
            if ($ea > 180) {
334 2
                $p  = [$xc, $yc, $xc, $yc + $z, $xc + $w * $cosea, $z + $yc - $h * $sinea];
335 2
                $pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea];
336
337 2
                for ($a = $rea; $a > M_PI; $a -= $step) {
338 2
                    $tca  = cos($a);
339 2
                    $tsa  = sin($a);
340 2
                    $p[]  = $xc + $w * $tca;
341 2
                    $p[]  = $z + $yc - $h * $tsa;
342 2
                    $pt[] = $xc + $w * $tca;
343 2
                    $pt[] = $yc - $h * $tsa;
344
                }
345
346 2
                $p[] = $xc - $w;
347 2
                $p[] = $z + $yc;
348 2
                $p[] = $xc - $w;
349 2
                $p[] = $yc;
350 2
                $p[] = $xc;
351 2
                $p[] = $yc;
352
353 2
                $pt[] = $xc - $w;
354 2
                $pt[] = $z + $yc;
355 2
                $pt[] = $xc - $w;
356 2
                $pt[] = $yc;
357
358 2
                for ($a = M_PI - $step; $a > $rsa; $a -= $step) {
359 2
                    $pt[] = $xc + $w * cos($a);
360 2
                    $pt[] = $yc - $h * sin($a);
361
                }
362
363 2
                $pt[] = $xc + $w * $cossa;
364 2
                $pt[] = $yc - $h * $sinsa;
365 2
                $pt[] = $xc;
366 2
                $pt[] = $yc;
367
            } else {
368
                // $sa >= 90 && $ea <= 180
369 2
                $p = [$xc, $yc, $xc, $yc + $z,
370 2
                    $xc + $w * $cosea, $z + $yc - $h * $sinea,
371 2
                    $xc + $w * $cosea, $yc - $h * $sinea,
372 2
                    $xc, $yc, ];
373
374 2
                $pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea];
375
376 2
                for ($a = $rea; $a > $rsa; $a -= $step) {
377 2
                    $pt[] = $xc + $w * cos($a);
378 2
                    $pt[] = $yc - $h * sin($a);
379
                }
380
381 2
                $pt[] = $xc + $w * $cossa;
382 2
                $pt[] = $yc - $h * $sinsa;
383 2
                $pt[] = $xc;
384 2
                $pt[] = $yc;
385
            }
386
        } else {
387
            // sa > 0 && ea < 90
388
389 2
            $p = [$xc, $yc, $xc, $yc + $z,
390 2
                $xc + $w * $cossa, $z + $yc - $h * $sinsa,
391 2
                $xc + $w * $cossa, $yc - $h * $sinsa,
392 2
                $xc, $yc, ];
393
394 2
            $pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa];
395
396 2
            for ($a = $rsa; $a < $rea; $a += $step) {
397 2
                $pt[] = $xc + $w * cos($a);
398 2
                $pt[] = $yc - $h * sin($a);
399
            }
400
401 2
            $pt[] = $xc + $w * $cosea;
402 2
            $pt[] = $yc - $h * $sinea;
403 2
            $pt[] = $xc;
404 2
            $pt[] = $yc;
405
        }
406
407 2
        $img->PushColor($fillcolor . ':' . $shadow);
408 2
        $img->FilledPolygon($p);
409 2
        $img->PopColor();
410
411 2
        $img->PushColor($fillcolor);
412 2
        $img->FilledPolygon($pt);
413 2
        $img->PopColor();
414 2
    }
415
416 1
    public function SetStartAngle($aStart)
417
    {
418 1
        if ($aStart < 0 || $aStart > 360) {
419
            Util\JpGraphError::RaiseL(14004); //('Slice start angle must be between 0 and 360 degrees.');
420
        }
421 1
        $this->startangle = $aStart;
422 1
    }
423
424
    // Draw a 3D Pie
425 2
    public function Pie3D(
426
        $aaoption,
427
        $img,
428
        $data,
429
        $colors,
430
        $xc,
431
        $yc,
432
        $d,
433
        $angle,
434
        $z,
435
        $shadow = 0.65,
436
        $startangle = 0,
437
        $edgecolor = '',
438
        $edgeweight = 1
439
    ) {
440
        /**
441
         * As usual the algorithm get more complicated than I originally
442
         * // envisioned. I believe that this is as simple as it is possible
443
         * // to do it with the features I want. It's a good exercise to start
444
         * // thinking on how to do this to convince your self that all this
445
         * // is really needed for the general case.
446
         * //
447
         * // The algorithm two draw 3D pies without "real 3D" is done in
448
         * // two steps.
449
         * // First imagine the pie cut in half through a thought line between
450
         * // 12'a clock and 6'a clock. It now easy to imagine that we can plot
451
         * // the individual slices for each half by starting with the topmost
452
         * // pie slice and continue down to 6'a clock.
453
         * //
454
         * // In the algortithm this is done in three principal steps
455
         * // Step 1. Do the knife cut to ensure by splitting slices that extends
456
         * // over the cut line. This is done by splitting the original slices into
457
         * // upto 3 subslices.
458
         * // Step 2. Find the top slice for each half
459
         * // Step 3. Draw the slices from top to bottom
460
         * //
461
         * // The thing that slightly complicates this scheme with all the
462
         * // angle comparisons below is that we can have an arbitrary start
463
         * // angle so we must take into account the different equivalence classes.
464
         * // For the same reason we must walk through the angle array in a
465
         * // modulo fashion.
466
         * //
467
         * // Limitations of algorithm:
468
         * // * A small exploded slice which crosses the 270 degree point
469
         * //   will get slightly nagged close to the center due to the fact that
470
         * //   we print the slices in Z-order and that the slice left part
471
         * //   get printed first and might get slightly nagged by a larger
472
         * //   slice on the right side just before the right part of the small
473
         * //   slice. Not a major problem though.
474
         */
475
        // Determine the height of the ellippse which gives an
476
        // indication of the inclination angle
477 2
        $h   = ($angle / 90.0) * $d;
478 2
        $sum = 0;
479 2
        for ($i = 0; $i < safe_count($data); ++$i) {
480 2
            $sum += $data[$i];
481
        }
482
483
        // Special optimization
484 2
        if ($sum == 0) {
0 ignored issues
show
introduced by
The condition $sum == 0 is always true.
Loading history...
485
            return;
486
        }
487
488 2
        if ($this->labeltype == 2) {
489
            $this->adjusted_data = $this->AdjPercentage($data);
490
        }
491
492
        // Setup the start
493 2
        $accsum = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $accsum is dead and can be removed.
Loading history...
494 2
        $a      = $startangle;
495 2
        $a      = $this->NormAngle($a);
496
497
        //
498
        // Step 1 . Split all slices that crosses 90 or 270
499
        //
500 2
        $idx        = 0;
501 2
        $adjexplode = [];
502 2
        $numcolors  = safe_count($colors);
503 2
        for ($i = 0; $i < safe_count($data); ++$i, ++$idx) {
504 2
            $da = $data[$i] / $sum * 360;
505
506 2
            if (empty($this->explode_radius[$i])) {
507 2
                $this->explode_radius[$i] = 0;
508
            }
509
510 2
            $expscale = 1;
511 2
            if ($aaoption == 1) {
512 2
                $expscale = 2;
513
            }
514
515 2
            $la      = $a + $da / 2;
516 2
            $explode = [$xc + $this->explode_radius[$i] * cos($la * M_PI / 180) * $expscale,
517 2
                $yc - $this->explode_radius[$i] * sin($la * M_PI / 180) * ($h / $d) * $expscale, ];
518 2
            $adjexplode[$idx]   = $explode;
519 2
            $labeldata[$i]      = [$la, $explode[0], $explode[1]];
520 2
            $originalangles[$i] = [$a, $a + $da];
521
522 2
            $ne = $this->NormAngle($a + $da);
523 2
            if ($da <= 180) {
524
                // If the slice size is <= 90 it can at maximum cut across
525
                // one boundary (either 90 or 270) where it needs to be split
526 2
                $split = -1; // no split
527 2
                if (($da <= 90 && ($a <= 90 && $ne > 90)) ||
528 2
                    (($da <= 180 && $da > 90) && (($a < 90 || $a >= 270) && $ne > 90))) {
529 2
                    $split = 90;
530 2
                } elseif (($da <= 90 && ($a <= 270 && $ne > 270)) ||
531 2
                    (($da <= 180 && $da > 90) && ($a >= 90 && $a < 270 && ($a + $da) > 270))) {
532 2
                    $split = 270;
533
                }
534 2
                if ($split > 0) {
535
                    // split in two
536 2
                    $angles[$idx]     = [$a, $split];
537 2
                    $adjcolors[$idx]  = $colors[$i % $numcolors];
538 2
                    $adjexplode[$idx] = $explode;
539 2
                    $angles[++$idx]   = [$split, $ne];
540 2
                    $adjcolors[$idx]  = $colors[$i % $numcolors];
541 2
                    $adjexplode[$idx] = $explode;
542
                } else {
543
                    // no split
544 2
                    $angles[$idx]     = [$a, $ne];
545 2
                    $adjcolors[$idx]  = $colors[$i % $numcolors];
546 2
                    $adjexplode[$idx] = $explode;
547
                }
548
            } else {
549
                // da>180
550
                // Slice may, depending on position, cross one or two
551
                // bonudaries
552
553
                if ($a < 90) {
554
                    $split = 90;
555
                } elseif ($a <= 270) {
556
                    $split = 270;
557
                } else {
558
                    $split = 90;
559
                }
560
561
                $angles[$idx]     = [$a, $split];
562
                $adjcolors[$idx]  = $colors[$i % $numcolors];
563
                $adjexplode[$idx] = $explode;
564
                //if( $a+$da > 360-$split ) {
565
                // For slices larger than 270 degrees we might cross
566
                // another boundary as well. This means that we must
567
                // split the slice further. The comparison gets a little
568
                // bit complicated since we must take into accound that
569
                // a pie might have a startangle >0 and hence a slice might
570
                // wrap around the 0 angle.
571
                // Three cases:
572
                //  a) Slice starts before 90 and hence gets a split=90, but
573
                //     we must also check if we need to split at 270
574
                //  b) Slice starts after 90 but before 270 and slices
575
                //     crosses 90 (after a wrap around of 0)
576
                //  c) If start is > 270 (hence the firstr split is at 90)
577
                //     and the slice is so large that it goes all the way
578
                //     around 270.
579
                if (($a < 90 && ($a + $da > 270)) || ($a > 90 && $a <= 270 && ($a + $da > 360 + 90)) || ($a > 270 && $this->NormAngle($a + $da) > 270)) {
580
                    $angles[++$idx]   = [$split, 360 - $split];
581
                    $adjcolors[$idx]  = $colors[$i % $numcolors];
582
                    $adjexplode[$idx] = $explode;
583
                    $angles[++$idx]   = [360 - $split, $ne];
584
                    $adjcolors[$idx]  = $colors[$i % $numcolors];
585
                    $adjexplode[$idx] = $explode;
586
                } else {
587
                    // Just a simple split to the previous decided
588
                    // angle.
589
                    $angles[++$idx]   = [$split, $ne];
590
                    $adjcolors[$idx]  = $colors[$i % $numcolors];
591
                    $adjexplode[$idx] = $explode;
592
                }
593
            }
594 2
            $a += $da;
595 2
            $a = $this->NormAngle($a);
596
        }
597
598
        // Total number of slices
599 2
        $n = safe_count($angles);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $angles does not seem to be defined for all execution paths leading up to this point.
Loading history...
600
601 2
        for ($i = 0; $i < $n; ++$i) {
602 2
            list($dbgs, $dbge) = $angles[$i];
0 ignored issues
show
Comprehensibility Best Practice introduced by
This list assign is not used and could be removed.
Loading history...
603
        }
604
605
        //
606
        // Step 2. Find start index (first pie that starts in upper left quadrant)
607
        //
608 2
        $minval = $angles[0][0];
609 2
        $min    = 0;
610 2
        for ($i = 0; $i < $n; ++$i) {
611 2
            if ($angles[$i][0] < $minval) {
612
                $minval = $angles[$i][0];
613
                $min    = $i;
614
            }
615
        }
616 2
        $j   = $min;
617 2
        $cnt = 0;
618 2
        while ($angles[$j][1] <= 90) {
619 2
            ++$j;
620 2
            if ($j >= $n) {
621
                $j = 0;
622
            }
623 2
            if ($cnt > $n) {
624
                Util\JpGraphError::RaiseL(14005);
625
                //("Pie3D Internal error (#1). Trying to wrap twice when looking for start index");
626
            }
627 2
            ++$cnt;
628
        }
629 2
        $start = $j;
630
631
        //
632
        // Step 3. Print slices in z-order
633
        //
634 2
        $cnt = 0;
635
636
        // First stroke all the slices between 90 and 270 (left half circle)
637
        // counterclockwise
638
639 2
        while ($angles[$j][0] < 270 && $aaoption !== 2) {
640 2
            list($x, $y) = $adjexplode[$j];
641
642 2
            $this->Pie3DSlice(
643 2
                $img,
644 2
                $x,
645 2
                $y,
646 2
                $d,
647 2
                $h,
648 2
                $angles[$j][0],
649 2
                $angles[$j][1],
650 2
                $z,
651 2
                $adjcolors[$j],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $adjcolors does not seem to be defined for all execution paths leading up to this point.
Loading history...
652 2
                $shadow
653
            );
654
655 2
            $last = [$x, $y, $j];
656
657 2
            ++$j;
658 2
            if ($j >= $n) {
659
                $j = 0;
660
            }
661
662 2
            if ($cnt > $n) {
663
                Util\JpGraphError::RaiseL(14006);
664
                //("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
665
            }
666 2
            ++$cnt;
667
        }
668
669 2
        $slice_left = $n - $cnt;
670 2
        $j          = $start - 1;
671 2
        if ($j < 0) {
672
            $j = $n - 1;
673
        }
674
675 2
        $cnt = 0;
676
677
        // The stroke all slices from 90 to -90 (right half circle)
678
        // clockwise
679 2
        while ($cnt < $slice_left && $aaoption !== 2) {
680 2
            list($x, $y) = $adjexplode[$j];
681
682 2
            $this->Pie3DSlice(
683 2
                $img,
684 2
                $x,
685 2
                $y,
686 2
                $d,
687 2
                $h,
688 2
                $angles[$j][0],
689 2
                $angles[$j][1],
690 2
                $z,
691 2
                $adjcolors[$j],
692 2
                $shadow
693
            );
694 2
            --$j;
695 2
            if ($cnt > $n) {
696
                Util\JpGraphError::RaiseL(14006);
697
                //("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
698
            }
699 2
            if ($j < 0) {
700 2
                $j = $n - 1;
701
            }
702
703 2
            ++$cnt;
704
        }
705
706
        // Now do a special thing. Stroke the last slice on the left
707
        // halfcircle one more time.  This is needed in the case where
708
        // the slice close to 270 have been exploded. In that case the
709
        // part of the slice close to the center of the pie might be
710
        // slightly nagged.
711 2
        if ($aaoption !== 2) {
712 2
            $this->Pie3DSlice(
713 2
                $img,
714 2
                $last[0],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $last does not seem to be defined for all execution paths leading up to this point.
Loading history...
715 2
                $last[1],
716 2
                $d,
717 2
                $h,
718 2
                $angles[$last[2]][0],
719 2
                $angles[$last[2]][1],
720 2
                $z,
721 2
                $adjcolors[$last[2]],
722 2
                $shadow
723
            );
724
        }
725
726 2
        if ($aaoption !== 1) {
727
            // Now print possible labels and add csim
728 2
            $this->value->ApplyFont($img);
729 2
            $margin = $img->GetFontHeight() / 2 + $this->value->margin;
730 2
            for ($i = 0; $i < safe_count($data); ++$i) {
731 2
                $la = $labeldata[$i][0];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $labeldata does not seem to be defined for all execution paths leading up to this point.
Loading history...
732 2
                $x  = $labeldata[$i][1] + cos($la * M_PI / 180) * ($d + $margin) * $this->ilabelposadj;
733 2
                $y  = $labeldata[$i][2] - sin($la * M_PI / 180) * ($h + $margin) * $this->ilabelposadj;
734 2
                if ($this->ilabelposadj >= 1.0) {
735 2
                    if ($la > 180 && $la < 360) {
736 2
                        $y += $z;
737
                    }
738
                }
739 2
                if ($this->labeltype == 0) {
740 2
                    if ($sum > 0) {
741 2
                        $l = 100 * $data[$i] / $sum;
742
                    } else {
743 2
                        $l = 0;
744
                    }
745
                } elseif ($this->labeltype == 1) {
746
                    $l = $data[$i];
747
                } else {
748
                    $l = $this->adjusted_data[$i];
749
                }
750 2
                if (isset($this->labels[$i]) && is_string($this->labels[$i])) {
751
                    $l = sprintf($this->labels[$i], $l);
752
                }
753
754 2
                $this->StrokeLabels($l, $img, $labeldata[$i][0] * M_PI / 180, $x, $y, $z);
755
756 2
                $this->Add3DSliceToCSIM(
757 2
                    $i,
758 2
                    $labeldata[$i][1],
759 2
                    $labeldata[$i][2],
760 2
                    $h * 2,
761 2
                    $d * 2,
762 2
                    $z,
763 2
                    $originalangles[$i][0],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $originalangles does not seem to be defined for all execution paths leading up to this point.
Loading history...
764 2
                    $originalangles[$i][1]
765
                );
766
            }
767
        }
768
769
        //
770
        // Finally add potential lines in pie
771
        //
772
773 2
        if ($edgecolor == '' || $aaoption !== 0) {
774 2
            return;
775
        }
776
777
        $accsum = 0;
778
        $a      = $startangle;
779
        $a      = $this->NormAngle($a);
780
781
        $a *= M_PI / 180.0;
782
783
        $idx = 0;
784
        $img->PushColor($edgecolor);
785
        $img->SetLineWeight($edgeweight);
786
787
        $fulledge = true;
788
        for ($i = 0; $i < safe_count($data) && $fulledge; ++$i) {
789
            if (empty($this->explode_radius[$i])) {
790
                $this->explode_radius[$i] = 0;
791
            }
792
            if ($this->explode_radius[$i] > 0) {
793
                $fulledge = false;
794
            }
795
        }
796
797
        for ($i = 0; $i < safe_count($data); ++$i, ++$idx) {
798
            $da = $data[$i] / $sum * 2 * M_PI;
799
            $this->StrokeFullSliceFrame(
800
                $img,
801
                $xc,
802
                $yc,
803
                $a,
804
                $a + $da,
805
                $d,
806
                $h,
807
                $z,
808
                $edgecolor,
809
                $this->explode_radius[$i],
810
                $fulledge
811
            );
812
            $a += $da;
813
        }
814
        $img->PopColor();
815
    }
816
817
    public function StrokeFullSliceFrame($img, $xc, $yc, $sa, $ea, $w, $h, $z, $edgecolor, $exploderadius, $fulledge)
818
    {
819
        $step = 0.02;
820
821
        if ($exploderadius > 0) {
822
            $la = ($sa + $ea) / 2;
823
            $xc += $exploderadius * cos($la);
824
            $yc -= $exploderadius * sin($la) * ($h / $w);
825
        }
826
827
        $p = [$xc, $yc, $xc + $w * cos($sa), $yc - $h * sin($sa)];
828
829
        for ($a = $sa; $a < $ea; $a += $step) {
830
            $p[] = $xc + $w * cos($a);
831
            $p[] = $yc - $h * sin($a);
832
        }
833
834
        $p[] = $xc + $w * cos($ea);
835
        $p[] = $yc - $h * sin($ea);
836
        $p[] = $xc;
837
        $p[] = $yc;
838
839
        $img->SetColor($edgecolor);
840
        $img->Polygon($p);
841
842
        // Unfortunately we can't really draw the full edge around the whole of
843
        // of the slice if any of the slices are exploded. The reason is that
844
        // this algorithm is to simply. There are cases where the edges will
845
        // "overwrite" other slices when they have been exploded.
846
        // Doing the full, proper 3D hidden lines stiff is actually quite
847
        // tricky. So for exploded pies we only draw the top edge. Not perfect
848
        // but the "real" solution is much more complicated.
849
        if ($fulledge && !($sa > 0 && $sa < M_PI && $ea < M_PI)) {
850
            if ($sa < M_PI && $ea > M_PI) {
851
                $sa = M_PI;
852
            }
853
854
            if ($sa < 2 * M_PI && (($ea >= 2 * M_PI) || ($ea > 0 && $ea < $sa))) {
855
                $ea = 2 * M_PI;
856
            }
857
858
            if ($sa >= M_PI && $ea <= 2 * M_PI) {
859
                $p = [$xc + $w * cos($sa), $yc - $h * sin($sa),
860
                    $xc + $w * cos($sa), $z + $yc - $h * sin($sa), ];
861
862
                for ($a = $sa + $step; $a < $ea; $a += $step) {
863
                    $p[] = $xc + $w * cos($a);
864
                    $p[] = $z + $yc - $h * sin($a);
865
                }
866
                $p[] = $xc + $w * cos($ea);
867
                $p[] = $z + $yc - $h * sin($ea);
868
                $p[] = $xc + $w * cos($ea);
869
                $p[] = $yc - $h * sin($ea);
870
                $img->SetColor($edgecolor);
871
                $img->Polygon($p);
872
            }
873
        }
874
    }
875
876 2
    public function Stroke($img, $aaoption = 0)
877
    {
878 2
        $n = safe_count($this->data);
879
880
        // If user hasn't set the colors use the theme array
881 2
        if ($this->setslicecolors == null) {
882 2
            $colors = array_keys($img->rgb->rgb_table);
883 2
            sort($colors);
884 2
            $idx_a = $this->themearr[$this->theme];
885 2
            $ca    = [];
886 2
            $m     = safe_count($idx_a);
887 2
            for ($i = 0; $i < $m; ++$i) {
888 2
                $ca[$i] = $colors[$idx_a[$i]];
889
            }
890 2
            $ca = array_reverse(array_slice($ca, 0, $n));
891
        } else {
892
            $ca = $this->setslicecolors;
893
        }
894
895 2
        if ($this->posx <= 1 && $this->posx > 0) {
896 2
            $xc = round($this->posx * $img->width);
897
        } else {
898
            $xc = $this->posx;
899
        }
900
901 2
        if ($this->posy <= 1 && $this->posy > 0) {
902 2
            $yc = round($this->posy * $img->height);
903
        } else {
904
            $yc = $this->posy;
905
        }
906
907 2
        if ($this->radius <= 1) {
908 2
            $width = floor($this->radius * min($img->width, $img->height));
909
            // Make sure that the pie doesn't overflow the image border
910
            // The 0.9 factor is simply an extra margin to leave some space
911
            // between the pie an the border of the image.
912 2
            $width = min($width, min($xc * 0.9, ($yc * 90 / $this->angle - $width / 4) * 0.9));
913
        } else {
914 1
            $width = $this->radius * ($aaoption === 1 ? 2 : 1);
915
        }
916
917
        // Add a sanity check for width
918 2
        if ($width < 1) {
919
            Util\JpGraphError::RaiseL(14007); //("Width for 3D Pie is 0. Specify a size > 0");
920
        }
921
922
        // Establish a thickness. By default the thickness is a fifth of the
923
        // pie slice width (=pie radius) but since the perspective depends
924
        // on the inclination angle we use some heuristics to make the edge
925
        // slightly thicker the less the angle.
926
927
        // Has user specified an absolute thickness? In that case use
928
        // that instead
929
930 2
        if ($this->iThickness) {
931 1
            $thick = $this->iThickness;
932 1
            $thick *= ($aaoption === 1 ? 2 : 1);
933
        } else {
934 2
            $thick = $width / 12;
935
        }
936 2
        $a = $this->angle;
937
938 2
        if ($a <= 30) {
939 2
            $thick *= 1.6;
940 2
        } elseif ($a <= 40) {
941
            $thick *= 1.4;
942 2
        } elseif ($a <= 50) {
943 2
            $thick *= 1.2;
944
        } elseif ($a <= 60) {
945
            $thick *= 1.0;
946
        } elseif ($a <= 70) {
947
            $thick *= 0.8;
948
        } elseif ($a <= 80) {
949
            $thick *= 0.7;
950
        } else {
951
            $thick *= 0.6;
952
        }
953
954 2
        $thick = floor($thick);
0 ignored issues
show
Bug introduced by
It seems like $thick can also be of type true; however, parameter $num of floor() does only seem to accept double|integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

954
        $thick = floor(/** @scrutinizer ignore-type */ $thick);
Loading history...
955
956 2
        if ($this->explode_all) {
957
            for ($i = 0; $i < $n; ++$i) {
958
                $this->explode_radius[$i] = $this->explode_r;
959
            }
960
        }
961
962 2
        $this->Pie3D(
963 2
            $aaoption,
964 2
            $img,
965 2
            $this->data,
966 2
            $ca,
967 2
            $xc,
968 2
            $yc,
969 2
            $width,
970 2
            $this->angle,
971 2
            $thick,
972 2
            0.65,
973 2
            $this->startangle,
974 2
            $this->edgecolor,
975 2
            $this->edgeweight
976
        );
977
978
        // Adjust title position
979 2
        if ($aaoption != 1) {
980 2
            $this->title->SetPos($xc, $yc - $this->title->GetFontHeight($img) - $width / 2 - $this->title->margin, 'center', 'bottom');
981 2
            $this->title->Stroke($img);
982
        }
983 2
    }
984
985
    /**
986
     * PRIVATE METHODS.
987
     *
988
     * @param mixed $label
989
     * @param mixed $img
990
     * @param mixed $a
991
     * @param mixed $xp
992
     * @param mixed $yp
993
     * @param mixed $z
994
     */
995
996
    // Position the labels of each slice
997 2
    public function StrokeLabels($label, $img, $a, $xp, $yp, $z)
0 ignored issues
show
Unused Code introduced by
The parameter $z is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

997
    public function StrokeLabels($label, $img, $a, $xp, $yp, /** @scrutinizer ignore-unused */ $z)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
998
    {
999 2
        $this->value->halign = 'left';
1000 2
        $this->value->valign = 'top';
1001
1002
        // Position the axis title.
1003
        // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
1004
        // that intersects with the extension of the corresponding axis. The code looks a little
1005
        // bit messy but this is really the only way of having a reasonable position of the
1006
        // axis titles.
1007 2
        $this->value->ApplyFont($img);
1008 2
        $h = $img->GetTextHeight($label);
1009
        // For numeric values the format of the display value
1010
        // must be taken into account
1011 2
        if (is_numeric($label)) {
1012 2
            if ($label >= 0) {
1013 2
                $w = $img->GetTextWidth(sprintf($this->value->format, $label));
1014
            } else {
1015 2
                $w = $img->GetTextWidth(sprintf($this->value->negformat, $label));
1016
            }
1017
        } else {
1018
            $w = $img->GetTextWidth($label);
1019
        }
1020
1021 2
        while ($a > 2 * M_PI) {
1022
            $a -= 2 * M_PI;
1023
        }
1024
1025 2
        if ($a >= 7 * M_PI / 4 || $a <= M_PI / 4) {
1026 2
            $dx = 0;
1027
        }
1028
1029 2
        if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) {
1030 2
            $dx = ($a - M_PI / 4) * 2 / M_PI;
1031
        }
1032
1033 2
        if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) {
1034 2
            $dx = 1;
1035
        }
1036
1037 2
        if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) {
1038 2
            $dx = (1 - ($a - M_PI * 5 / 4) * 2 / M_PI);
1039
        }
1040
1041 2
        if ($a >= 7 * M_PI / 4) {
1042 2
            $dy = (($a - M_PI) - 3 * M_PI / 4) * 2 / M_PI;
1043
        }
1044
1045 2
        if ($a <= M_PI / 4) {
1046 1
            $dy = (1 - $a * 2 / M_PI);
1047
        }
1048
1049 2
        if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) {
1050 2
            $dy = 1;
1051
        }
1052
1053 2
        if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) {
1054 2
            $dy = (1 - ($a - 3 * M_PI / 4) * 2 / M_PI);
1055
        }
1056
1057 2
        if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) {
1058 2
            $dy = 0;
1059
        }
1060
1061 2
        $x = round($xp - $dx * $w);
0 ignored issues
show
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...
1062 2
        $y = round($yp - $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...
1063
1064
        // Mark anchor point for debugging
1065
        /*
1066
        $img->SetColor('red');
1067
        $img->Line($xp-10,$yp,$xp+10,$yp);
1068
        $img->Line($xp,$yp-10,$xp,$yp+10);
1069
         */
1070
1071 2
        $oldmargin           = $this->value->margin;
1072 2
        $this->value->margin = 0;
1073 2
        $this->value->Stroke($img, $label, $x, $y);
1074 2
        $this->value->margin = $oldmargin;
1075 2
    }
1076
} // @class
1077
1078
/* EOF */
1079