PiePlot3D::Add3DSliceToCSIM()   B
last analyzed

Complexity

Conditions 11
Paths 60

Size

Total Lines 56
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 32
c 0
b 0
f 0
nc 60
nop 8
dl 0
loc 56
rs 7.3166

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
    public function __construct($data)
41
    {
42
        $this->radius = 0.5;
43
        $this->data   = $data;
44
        $this->title  = new Text\Text('');
45
        $this->title->SetFont(FF_FONT1, FS_BOLD);
46
        $this->value = new DisplayValue();
47
        $this->value->Show();
48
        $this->value->SetFormat('%.0f%%');
49
    }
50
51
    /**
52
     * PUBLIC METHODS.
53
     *
54
     * @param mixed $aLegend
55
     */
56
    // Set label arrays
57
    public function SetLegends($aLegend)
58
    {
59
        $this->legends = array_reverse(array_slice($aLegend, 0, safe_count($this->data)));
60
    }
61
62
    public function SetSliceColors($aColors)
63
    {
64
        $this->setslicecolors = $aColors;
65
    }
66
67
    public function Legend($aGraph)
68
    {
69
        parent::Legend($aGraph);
70
        $aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol);
71
    }
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
    public function SetEdge($aColor = 'black', $aWeight = 1)
83
    {
84
        $this->edgecolor  = $aColor;
85
        $this->edgeweight = $aWeight;
86
    }
87
88
    // Specify projection angle for 3D in degrees
89
    // Must be between 20 and 70 degrees
90
    public function SetAngle($a)
91
    {
92
        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
            $this->angle = $a;
97
        }
98
    }
99
100
    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
        $sa *= M_PI / 180;
105
        $ea *= M_PI / 180;
106
107
        //add coordinates of the centre to the map
108
        $coords = "${xc}, ${yc}";
109
110
        //add coordinates of the first point on the arc to the map
111
        $xp = floor($width * cos($sa) / 2 + $xc);
112
        $yp = floor($yc - $height * sin($sa) / 2);
113
        $coords .= ", ${xp}, ${yp}";
114
115
        //If on the front half, add the thickness offset
116
        if ($sa >= M_PI && $sa <= 2 * M_PI * 1.01) {
117
            $yp = floor($yp + $thick);
118
            $coords .= ", ${xp}, ${yp}";
119
        }
120
121
        //add coordinates every 0.2 radians
122
        $a = $sa + 0.2;
123
        while ($a < $ea) {
124
            $xp = floor($width * cos($a) / 2 + $xc);
125
            if ($a >= M_PI && $a <= 2 * M_PI * 1.01) {
126
                $yp = floor($yc - ($height * sin($a) / 2) + $thick);
127
            } else {
128
                $yp = floor($yc - $height * sin($a) / 2);
129
            }
130
            $coords .= ", ${xp}, ${yp}";
131
            $a += 0.2;
132
        }
133
134
        //Add the last point on the arc
135
        $xp = floor($width * cos($ea) / 2 + $xc);
136
        $yp = floor($yc - $height * sin($ea) / 2);
137
138
        if ($ea >= M_PI && $ea <= 2 * M_PI * 1.01) {
139
            $coords .= ", ${xp}, " . floor($yp + $thick);
140
        }
141
        $coords .= ", ${xp}, ${yp}";
142
        $alt = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $alt is dead and can be removed.
Loading history...
143
144
        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
    }
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
    public function SetHeight($aHeight)
184
    {
185
        $this->iThickness = $aHeight;
186
    }
187
188
    // Normalize Angle between 0-360
189
    public function NormAngle($a)
190
    {
191
        // Normalize anle to 0 to 2M_PI
192
        //
193
        if ($a > 0) {
194
            while ($a > 360) {
195
                $a -= 360;
196
            }
197
        } else {
198
            while ($a < 0) {
199
                $a += 360;
200
            }
201
        }
202
        if ($a < 0) {
203
            $a = 360 + $a;
204
        }
205
206
        if ($a == 360) {
207
            $a = 0;
208
        }
209
210
        return $a;
211
    }
212
213
    // Draw one 3D pie slice at position ($xc,$yc) with height $z
214
    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
        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
        $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
        $rsa   = $sa / 180 * M_PI; // to Rad
230
        $rea   = $ea / 180 * M_PI; // to Rad
231
        $sinsa = sin($rsa);
232
        $cossa = cos($rsa);
233
        $sinea = sin($rea);
234
        $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
        $step = 0.05;
241
242
        if ($sa >= 270) {
243
            if ($ea > 360 || ($ea > 0 && $ea <= 90)) {
244
                if ($ea > 0 && $ea <= 90) {
245
                    // Adjust angle to simplify conditions in loops
246
                    $rea += 2 * M_PI;
247
                }
248
249
                $p = [$xc, $yc, $xc, $yc + $z,
250
                    $xc + $w * $cossa, $z + $yc - $h * $sinsa, ];
251
                $pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa];
252
253
                for ($a = $rsa; $a < 2 * M_PI; $a += $step) {
254
                    $tca  = cos($a);
255
                    $tsa  = sin($a);
256
                    $p[]  = $xc + $w * $tca;
257
                    $p[]  = $z + $yc - $h * $tsa;
258
                    $pt[] = $xc + $w * $tca;
259
                    $pt[] = $yc - $h * $tsa;
260
                }
261
262
                $pt[] = $xc + $w;
263
                $pt[] = $yc;
264
265
                $p[] = $xc + $w;
266
                $p[] = $z + $yc;
267
                $p[] = $xc + $w;
268
                $p[] = $yc;
269
                $p[] = $xc;
270
                $p[] = $yc;
271
272
                for ($a = 2 * M_PI + $step; $a < $rea; $a += $step) {
273
                    $pt[] = $xc + $w * cos($a);
274
                    $pt[] = $yc - $h * sin($a);
275
                }
276
277
                $pt[] = $xc + $w * $cosea;
278
                $pt[] = $yc - $h * $sinea;
279
                $pt[] = $xc;
280
                $pt[] = $yc;
281
            } else {
282
                $p = [$xc, $yc, $xc, $yc + $z,
283
                    $xc + $w * $cossa, $z + $yc - $h * $sinsa, ];
284
                $pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa];
285
286
                $rea = $rea == 0.0 ? 2 * M_PI : $rea;
287
                for ($a = $rsa; $a < $rea; $a += $step) {
288
                    $tca  = cos($a);
289
                    $tsa  = sin($a);
290
                    $p[]  = $xc + $w * $tca;
291
                    $p[]  = $z + $yc - $h * $tsa;
292
                    $pt[] = $xc + $w * $tca;
293
                    $pt[] = $yc - $h * $tsa;
294
                }
295
296
                $pt[] = $xc + $w * $cosea;
297
                $pt[] = $yc - $h * $sinea;
298
                $pt[] = $xc;
299
                $pt[] = $yc;
300
301
                $p[] = $xc + $w * $cosea;
302
                $p[] = $z + $yc - $h * $sinea;
303
                $p[] = $xc + $w * $cosea;
304
                $p[] = $yc - $h * $sinea;
305
                $p[] = $xc;
306
                $p[] = $yc;
307
            }
308
        } elseif ($sa >= 180) {
309
            $p  = [$xc, $yc, $xc, $yc + $z, $xc + $w * $cosea, $z + $yc - $h * $sinea];
310
            $pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea];
311
312
            for ($a = $rea; $a > $rsa; $a -= $step) {
313
                $tca  = cos($a);
314
                $tsa  = sin($a);
315
                $p[]  = $xc + $w * $tca;
316
                $p[]  = $z + $yc - $h * $tsa;
317
                $pt[] = $xc + $w * $tca;
318
                $pt[] = $yc - $h * $tsa;
319
            }
320
321
            $pt[] = $xc + $w * $cossa;
322
            $pt[] = $yc - $h * $sinsa;
323
            $pt[] = $xc;
324
            $pt[] = $yc;
325
326
            $p[] = $xc + $w * $cossa;
327
            $p[] = $z + $yc - $h * $sinsa;
328
            $p[] = $xc + $w * $cossa;
329
            $p[] = $yc - $h * $sinsa;
330
            $p[] = $xc;
331
            $p[] = $yc;
332
        } elseif ($sa >= 90) {
333
            if ($ea > 180) {
334
                $p  = [$xc, $yc, $xc, $yc + $z, $xc + $w * $cosea, $z + $yc - $h * $sinea];
335
                $pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea];
336
337
                for ($a = $rea; $a > M_PI; $a -= $step) {
338
                    $tca  = cos($a);
339
                    $tsa  = sin($a);
340
                    $p[]  = $xc + $w * $tca;
341
                    $p[]  = $z + $yc - $h * $tsa;
342
                    $pt[] = $xc + $w * $tca;
343
                    $pt[] = $yc - $h * $tsa;
344
                }
345
346
                $p[] = $xc - $w;
347
                $p[] = $z + $yc;
348
                $p[] = $xc - $w;
349
                $p[] = $yc;
350
                $p[] = $xc;
351
                $p[] = $yc;
352
353
                $pt[] = $xc - $w;
354
                $pt[] = $z + $yc;
355
                $pt[] = $xc - $w;
356
                $pt[] = $yc;
357
358
                for ($a = M_PI - $step; $a > $rsa; $a -= $step) {
359
                    $pt[] = $xc + $w * cos($a);
360
                    $pt[] = $yc - $h * sin($a);
361
                }
362
363
                $pt[] = $xc + $w * $cossa;
364
                $pt[] = $yc - $h * $sinsa;
365
                $pt[] = $xc;
366
                $pt[] = $yc;
367
            } else {
368
                // $sa >= 90 && $ea <= 180
369
                $p = [$xc, $yc, $xc, $yc + $z,
370
                    $xc + $w * $cosea, $z + $yc - $h * $sinea,
371
                    $xc + $w * $cosea, $yc - $h * $sinea,
372
                    $xc, $yc, ];
373
374
                $pt = [$xc, $yc, $xc + $w * $cosea, $yc - $h * $sinea];
375
376
                for ($a = $rea; $a > $rsa; $a -= $step) {
377
                    $pt[] = $xc + $w * cos($a);
378
                    $pt[] = $yc - $h * sin($a);
379
                }
380
381
                $pt[] = $xc + $w * $cossa;
382
                $pt[] = $yc - $h * $sinsa;
383
                $pt[] = $xc;
384
                $pt[] = $yc;
385
            }
386
        } else {
387
            // sa > 0 && ea < 90
388
389
            $p = [$xc, $yc, $xc, $yc + $z,
390
                $xc + $w * $cossa, $z + $yc - $h * $sinsa,
391
                $xc + $w * $cossa, $yc - $h * $sinsa,
392
                $xc, $yc, ];
393
394
            $pt = [$xc, $yc, $xc + $w * $cossa, $yc - $h * $sinsa];
395
396
            for ($a = $rsa; $a < $rea; $a += $step) {
397
                $pt[] = $xc + $w * cos($a);
398
                $pt[] = $yc - $h * sin($a);
399
            }
400
401
            $pt[] = $xc + $w * $cosea;
402
            $pt[] = $yc - $h * $sinea;
403
            $pt[] = $xc;
404
            $pt[] = $yc;
405
        }
406
407
        $img->PushColor($fillcolor . ':' . $shadow);
408
        $img->FilledPolygon($p);
409
        $img->PopColor();
410
411
        $img->PushColor($fillcolor);
412
        $img->FilledPolygon($pt);
413
        $img->PopColor();
414
    }
415
416
    public function SetStartAngle($aStart)
417
    {
418
        if ($aStart < 0 || $aStart > 360) {
419
            Util\JpGraphError::RaiseL(14004); //('Slice start angle must be between 0 and 360 degrees.');
420
        }
421
        $this->startangle = $aStart;
422
    }
423
424
    // Draw a 3D Pie
425
    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
        $h   = ($angle / 90.0) * $d;
478
        $sum = 0;
479
        for ($i = 0; $i < safe_count($data); ++$i) {
480
            $sum += $data[$i];
481
        }
482
483
        // Special optimization
484
        if ($sum == 0) {
0 ignored issues
show
introduced by
The condition $sum == 0 is always true.
Loading history...
485
            return;
486
        }
487
488
        if ($this->labeltype == 2) {
489
            $this->adjusted_data = $this->AdjPercentage($data);
490
        }
491
492
        // Setup the start
493
        $accsum = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $accsum is dead and can be removed.
Loading history...
494
        $a      = $startangle;
495
        $a      = $this->NormAngle($a);
496
497
        //
498
        // Step 1 . Split all slices that crosses 90 or 270
499
        //
500
        $idx        = 0;
501
        $adjexplode = [];
502
        $numcolors  = safe_count($colors);
503
        for ($i = 0; $i < safe_count($data); ++$i, ++$idx) {
504
            $da = $data[$i] / $sum * 360;
505
506
            if (empty($this->explode_radius[$i])) {
507
                $this->explode_radius[$i] = 0;
508
            }
509
510
            $expscale = 1;
511
            if ($aaoption == 1) {
512
                $expscale = 2;
513
            }
514
515
            $la      = $a + $da / 2;
516
            $explode = [$xc + $this->explode_radius[$i] * cos($la * M_PI / 180) * $expscale,
517
                $yc - $this->explode_radius[$i] * sin($la * M_PI / 180) * ($h / $d) * $expscale, ];
518
            $adjexplode[$idx]   = $explode;
519
            $labeldata[$i]      = [$la, $explode[0], $explode[1]];
520
            $originalangles[$i] = [$a, $a + $da];
521
522
            $ne = $this->NormAngle($a + $da);
523
            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
                $split = -1; // no split
527
                if (($da <= 90 && ($a <= 90 && $ne > 90)) ||
528
                    (($da <= 180 && $da > 90) && (($a < 90 || $a >= 270) && $ne > 90))) {
529
                    $split = 90;
530
                } elseif (($da <= 90 && ($a <= 270 && $ne > 270)) ||
531
                    (($da <= 180 && $da > 90) && ($a >= 90 && $a < 270 && ($a + $da) > 270))) {
532
                    $split = 270;
533
                }
534
                if ($split > 0) {
535
                    // split in two
536
                    $angles[$idx]     = [$a, $split];
537
                    $adjcolors[$idx]  = $colors[$i % $numcolors];
538
                    $adjexplode[$idx] = $explode;
539
                    $angles[++$idx]   = [$split, $ne];
540
                    $adjcolors[$idx]  = $colors[$i % $numcolors];
541
                    $adjexplode[$idx] = $explode;
542
                } else {
543
                    // no split
544
                    $angles[$idx]     = [$a, $ne];
545
                    $adjcolors[$idx]  = $colors[$i % $numcolors];
546
                    $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
            $a += $da;
595
            $a = $this->NormAngle($a);
596
        }
597
598
        // Total number of slices
599
        $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
        for ($i = 0; $i < $n; ++$i) {
602
            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
        $minval = $angles[0][0];
609
        $min    = 0;
610
        for ($i = 0; $i < $n; ++$i) {
611
            if ($angles[$i][0] < $minval) {
612
                $minval = $angles[$i][0];
613
                $min    = $i;
614
            }
615
        }
616
        $j   = $min;
617
        $cnt = 0;
618
        while ($angles[$j][1] <= 90) {
619
            ++$j;
620
            if ($j >= $n) {
621
                $j = 0;
622
            }
623
            if ($cnt > $n) {
624
                Util\JpGraphError::RaiseL(14005);
625
                //("Pie3D Internal error (#1). Trying to wrap twice when looking for start index");
626
            }
627
            ++$cnt;
628
        }
629
        $start = $j;
630
631
        //
632
        // Step 3. Print slices in z-order
633
        //
634
        $cnt = 0;
635
636
        // First stroke all the slices between 90 and 270 (left half circle)
637
        // counterclockwise
638
639
        while ($angles[$j][0] < 270 && $aaoption !== 2) {
640
            list($x, $y) = $adjexplode[$j];
641
642
            $this->Pie3DSlice(
643
                $img,
644
                $x,
645
                $y,
646
                $d,
647
                $h,
648
                $angles[$j][0],
649
                $angles[$j][1],
650
                $z,
651
                $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
                $shadow
653
            );
654
655
            $last = [$x, $y, $j];
656
657
            ++$j;
658
            if ($j >= $n) {
659
                $j = 0;
660
            }
661
662
            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
            ++$cnt;
667
        }
668
669
        $slice_left = $n - $cnt;
670
        $j          = $start - 1;
671
        if ($j < 0) {
672
            $j = $n - 1;
673
        }
674
675
        $cnt = 0;
676
677
        // The stroke all slices from 90 to -90 (right half circle)
678
        // clockwise
679
        while ($cnt < $slice_left && $aaoption !== 2) {
680
            list($x, $y) = $adjexplode[$j];
681
682
            $this->Pie3DSlice(
683
                $img,
684
                $x,
685
                $y,
686
                $d,
687
                $h,
688
                $angles[$j][0],
689
                $angles[$j][1],
690
                $z,
691
                $adjcolors[$j],
692
                $shadow
693
            );
694
            --$j;
695
            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
            if ($j < 0) {
700
                $j = $n - 1;
701
            }
702
703
            ++$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
        if ($aaoption !== 2) {
712
            $this->Pie3DSlice(
713
                $img,
714
                $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
                $last[1],
716
                $d,
717
                $h,
718
                $angles[$last[2]][0],
719
                $angles[$last[2]][1],
720
                $z,
721
                $adjcolors[$last[2]],
722
                $shadow
723
            );
724
        }
725
726
        if ($aaoption !== 1) {
727
            // Now print possible labels and add csim
728
            $this->value->ApplyFont($img);
729
            $margin = $img->GetFontHeight() / 2 + $this->value->margin;
730
            for ($i = 0; $i < safe_count($data); ++$i) {
731
                $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
                $x  = $labeldata[$i][1] + cos($la * M_PI / 180) * ($d + $margin) * $this->ilabelposadj;
733
                $y  = $labeldata[$i][2] - sin($la * M_PI / 180) * ($h + $margin) * $this->ilabelposadj;
734
                if ($this->ilabelposadj >= 1.0) {
735
                    if ($la > 180 && $la < 360) {
736
                        $y += $z;
737
                    }
738
                }
739
                if ($this->labeltype == 0) {
740
                    if ($sum > 0) {
741
                        $l = 100 * $data[$i] / $sum;
742
                    } else {
743
                        $l = 0;
744
                    }
745
                } elseif ($this->labeltype == 1) {
746
                    $l = $data[$i];
747
                } else {
748
                    $l = $this->adjusted_data[$i];
749
                }
750
                if (isset($this->labels[$i]) && is_string($this->labels[$i])) {
751
                    $l = sprintf($this->labels[$i], $l);
752
                }
753
754
                $this->StrokeLabels($l, $img, $labeldata[$i][0] * M_PI / 180, $x, $y, $z);
755
756
                $this->Add3DSliceToCSIM(
757
                    $i,
758
                    $labeldata[$i][1],
759
                    $labeldata[$i][2],
760
                    $h * 2,
761
                    $d * 2,
762
                    $z,
763
                    $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
                    $originalangles[$i][1]
765
                );
766
            }
767
        }
768
769
        //
770
        // Finally add potential lines in pie
771
        //
772
773
        if ($edgecolor == '' || $aaoption !== 0) {
774
            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
    public function Stroke($img, $aaoption = 0)
877
    {
878
        $n = safe_count($this->data);
879
880
        // If user hasn't set the colors use the theme array
881
        if ($this->setslicecolors == null) {
882
            $colors = array_keys($img->rgb->rgb_table);
883
            sort($colors);
884
            $idx_a = $this->themearr[$this->theme];
885
            $ca    = [];
886
            $m     = safe_count($idx_a);
887
            for ($i = 0; $i < $m; ++$i) {
888
                $ca[$i] = $colors[$idx_a[$i]];
889
            }
890
            $ca = array_reverse(array_slice($ca, 0, $n));
891
        } else {
892
            $ca = $this->setslicecolors;
893
        }
894
895
        if ($this->posx <= 1 && $this->posx > 0) {
896
            $xc = round($this->posx * $img->width);
897
        } else {
898
            $xc = $this->posx;
899
        }
900
901
        if ($this->posy <= 1 && $this->posy > 0) {
902
            $yc = round($this->posy * $img->height);
903
        } else {
904
            $yc = $this->posy;
905
        }
906
907
        if ($this->radius <= 1) {
908
            $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
            $width = min($width, min($xc * 0.9, ($yc * 90 / $this->angle - $width / 4) * 0.9));
913
        } else {
914
            $width = $this->radius * ($aaoption === 1 ? 2 : 1);
915
        }
916
917
        // Add a sanity check for width
918
        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
        if ($this->iThickness) {
931
            $thick = $this->iThickness;
932
            $thick *= ($aaoption === 1 ? 2 : 1);
933
        } else {
934
            $thick = $width / 12;
935
        }
936
        $a = $this->angle;
937
938
        if ($a <= 30) {
939
            $thick *= 1.6;
940
        } elseif ($a <= 40) {
941
            $thick *= 1.4;
942
        } elseif ($a <= 50) {
943
            $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
        $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
        if ($this->explode_all) {
957
            for ($i = 0; $i < $n; ++$i) {
958
                $this->explode_radius[$i] = $this->explode_r;
959
            }
960
        }
961
962
        $this->Pie3D(
963
            $aaoption,
964
            $img,
965
            $this->data,
966
            $ca,
967
            $xc,
968
            $yc,
969
            $width,
970
            $this->angle,
971
            $thick,
972
            0.65,
973
            $this->startangle,
974
            $this->edgecolor,
975
            $this->edgeweight
976
        );
977
978
        // Adjust title position
979
        if ($aaoption != 1) {
980
            $this->title->SetPos($xc, $yc - $this->title->GetFontHeight($img) - $width / 2 - $this->title->margin, 'center', 'bottom');
981
            $this->title->Stroke($img);
982
        }
983
    }
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
    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
        $this->value->halign = 'left';
1000
        $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
        $this->value->ApplyFont($img);
1008
        $h = $img->GetTextHeight($label);
1009
        // For numeric values the format of the display value
1010
        // must be taken into account
1011
        if (is_numeric($label)) {
1012
            if ($label >= 0) {
1013
                $w = $img->GetTextWidth(sprintf($this->value->format, $label));
1014
            } else {
1015
                $w = $img->GetTextWidth(sprintf($this->value->negformat, $label));
1016
            }
1017
        } else {
1018
            $w = $img->GetTextWidth($label);
1019
        }
1020
1021
        while ($a > 2 * M_PI) {
1022
            $a -= 2 * M_PI;
1023
        }
1024
1025
        if ($a >= 7 * M_PI / 4 || $a <= M_PI / 4) {
1026
            $dx = 0;
1027
        }
1028
1029
        if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) {
1030
            $dx = ($a - M_PI / 4) * 2 / M_PI;
1031
        }
1032
1033
        if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) {
1034
            $dx = 1;
1035
        }
1036
1037
        if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) {
1038
            $dx = (1 - ($a - M_PI * 5 / 4) * 2 / M_PI);
1039
        }
1040
1041
        if ($a >= 7 * M_PI / 4) {
1042
            $dy = (($a - M_PI) - 3 * M_PI / 4) * 2 / M_PI;
1043
        }
1044
1045
        if ($a <= M_PI / 4) {
1046
            $dy = (1 - $a * 2 / M_PI);
1047
        }
1048
1049
        if ($a >= M_PI / 4 && $a <= 3 * M_PI / 4) {
1050
            $dy = 1;
1051
        }
1052
1053
        if ($a >= 3 * M_PI / 4 && $a <= 5 * M_PI / 4) {
1054
            $dy = (1 - ($a - 3 * M_PI / 4) * 2 / M_PI);
1055
        }
1056
1057
        if ($a >= 5 * M_PI / 4 && $a <= 7 * M_PI / 4) {
1058
            $dy = 0;
1059
        }
1060
1061
        $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
        $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
        $oldmargin           = $this->value->margin;
1072
        $this->value->margin = 0;
1073
        $this->value->Stroke($img, $label, $x, $y);
1074
        $this->value->margin = $oldmargin;
1075
    }
1076
} // @class
1077
1078
/* EOF */
1079