PiePlot3D   F
last analyzed

Complexity

Total Complexity 182

Size/Duplication

Total Lines 1049
Duplicated Lines 0 %

Test Coverage

Coverage 74.7%

Importance

Changes 0
Metric Value
eloc 566
c 0
b 0
f 0
dl 0
loc 1049
ccs 431
cts 577
cp 0.747
rs 2
wmc 182

20 Methods

Rating   Name   Duplication   Size   Complexity  
A SetEdge() 0 4 1
A Legend() 0 4 1
A SetSliceColors() 0 3 1
A SetLegends() 0 3 1
A SetAngle() 0 7 3
A __construct() 0 9 1
A SetCSIMTargets() 0 5 1
A SetLabelMargin() 0 3 1
A SetHeight() 0 3 1
B Add3DSliceToCSIM() 0 56 11
A ShowLabelHint() 0 3 1
A SetStartAngle() 0 6 3
C StrokeFullSliceFrame() 0 55 16
A SetLabels() 0 4 1
A SetLabelHintColor() 0 3 1
A NormAngle() 0 22 6
F Pie3DSlice() 0 200 24
F StrokeLabels() 0 78 20
F Stroke() 0 106 21
F Pie3D() 0 390 67

How to fix   Complexity   

Complex Class

Complex classes like PiePlot3D often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PiePlot3D, and based on these observations, apply Extract Interface, too.

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