PathBounds   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 319
Duplicated Lines 11.91 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 2
dl 38
loc 319
ccs 183
cts 183
cp 1
rs 8.72
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A addData() 0 8 2
B getBox() 0 19 7
A getLBox() 0 7 1
A getPreviousData() 0 6 1
A modifierAtIndex() 0 4 1
A union() 0 10 2
A getQBox() 0 9 1
A getCBox() 0 9 1
A getStartPoint() 0 7 1
A getNearest() 0 7 2
A isRelativeModifier() 0 4 1
A getStartX() 0 4 1
A getStartY() 0 4 1
A getLastData() 0 10 2
A getLastModifier() 0 4 1
A isAnyKindOfLine() 0 5 3
C addRelativeModifier() 19 74 10
B addAbsoluteModifier() 19 47 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PathBounds 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 PathBounds, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace nstdio\svg\shape;
3
4
use nstdio\svg\util\Bezier;
5
6
/**
7
 * Class PathBounds
8
 *
9
 * @package nstdio\svg\shape
10
 * @author  Edgar Asatryan <[email protected]>
11
 */
12
class PathBounds
13
{
14
    private $index;
15
16
    private $modifier;
17
18
    /**
19
     * The path points positions
20
     *
21
     * @var array
22
     */
23
    private $data = [];
24
25
    private $current;
26
27
    private $rect;
28
29 26
    public function __construct()
30
    {
31 26
        $this->rect = [
32 26
            'width'  => 0,
33 26
            'height' => 0,
34 26
            'x'      => null,
35 26
            'y'      => null,
36
        ];
37 26
    }
38
39
    /**
40
     * @param string $modifier Path modifier e.g. V, L, C, M
41
     * @param array  $params
42
     */
43 26
    public function addData($modifier, array $params)
44
    {
45 26
        if ($this->isRelativeModifier($modifier)) {
46 10
            $this->addRelativeModifier($modifier, $params);
47 10
        } else {
48 26
            $this->addAbsoluteModifier($modifier, $params);
49
        }
50 26
    }
51
52
    /**
53
     * @return array
54
     */
55 16
    public function getBox()
56
    {
57 16
        foreach ($this->data as $key => $value) {
58 16
            $this->modifier = key($value);
59 16
            $this->index = $key;
60 16
            $this->current = $value[$this->modifier];
61
62 16
            if ($this->isAnyKindOfLine()) {
63 7
                $this->getLBox();
64 16
            } elseif ($this->modifier === 'Q' || $this->modifier === 'q') {
65 5
                $this->getQBox();
66 16
            } elseif ($this->modifier === 'C' || $this->modifier === 'c') {
67 7
                $this->getCBox();
68 7
            }
69 16
        }
70 16
        unset($this->modifier, $this->index, $this->current);
71
72 16
        return $this->rect;
73
    }
74
75 7
    private function getLBox()
76
    {
77 7
        list($x1, $y1) = $this->getStartPoint();
78 7
        list($x2, $y2) = $this->current;
79
80 7
        $this->union($x1, $y1, $x2, $y2);
81 7
    }
82
83 15
    private function getPreviousData()
84
    {
85 15
        $mod = $this->modifierAtIndex($this->index - 1);
86
87 15
        return $this->data[$this->index - 1][$mod];
88
    }
89
90 15
    private function modifierAtIndex($index)
91
    {
92 15
        return key($this->data[$index]);
93
    }
94
95 15
    private function union($x1, $y1, $x2, $y2)
96
    {
97 15
        $box = Rect::boxFromPoints($x1, $y1, $x2, $y2);
98
99 15
        if ($this->rect['x'] === null) {
100 15
            $this->rect['x'] = $box['x'];
101 15
            $this->rect['y'] = $box['y'];
102 15
        }
103 15
        $this->rect = Rect::union($this->rect, $box);
104 15
    }
105
106 5
    private function getQBox()
107
    {
108 5
        list($p0x, $p0y) = $this->getStartPoint();
109 5
        list($p1x, $p1y, $p2x, $p2y) = $this->current;
110
111 5
        list($x1, $y1, $x2, $y2) = Bezier::quadraticBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y);
112
113 5
        $this->union($x1, $y1, $x2, $y2);
114 5
    }
115
116 7
    private function getCBox()
117
    {
118 7
        list($p0x, $p0y) = $this->getStartPoint();
119 7
        list($p1x, $p1y, $p2x, $p2y, $p3x, $p3y) = $this->current;
120
121 7
        list($x1, $y1, $x2, $y2) = Bezier::cubicBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y, $p3x, $p3y);
122
123 7
        $this->union($x1, $y1, $x2, $y2);
124 7
    }
125
126
    /**
127
     * @return array
128
     */
129 15
    private function getStartPoint()
130
    {
131 15
        $x1 = $this->getNearest('x');
132 15
        $y1 = $this->getNearest('y');
133
134 15
        return [$x1, $y1];
135
    }
136
137 15
    private function getNearest($axis)
138
    {
139 15
        $prevData = $this->getPreviousData();
140 15
        $coordinate = $axis === 'x' ? $this->getStartX($prevData) : $this->getStartY($prevData);
141
142 15
        return $coordinate;
143
    }
144
145
    /**
146
     * @param string $mod
147
     *
148
     * @return bool
149
     */
150 26
    private function isRelativeModifier($mod)
151
    {
152 26
        return ctype_lower($mod);
153
    }
154
155
    /**
156
     * @param array $data
157
     *
158
     * @return float|false
159
     */
160 16
    private function getStartX($data)
161
    {
162 16
        return $data[count($data) - 2];
163
    }
164
165
    /**
166
     * @param array $data
167
     *
168
     * @return float
169
     */
170 18
    private function getStartY($data)
171
    {
172 18
        return $data[count($data) - 1];
173
    }
174
175
    /**
176
     * @return array
177
     */
178 26
    private function getLastData()
179
    {
180 26
        if (empty($this->data)) {
181 26
            return [];
182
        }
183 22
        $prevData = $this->data[count($this->data) - 1];
184 22
        $prevData = $prevData[key($prevData)];
185
186 22
        return $prevData;
187
    }
188
189 9
    private function getLastModifier()
190
    {
191 9
        return key($this->data[count($this->data) - 1]);
192
    }
193
194 16
    private function isAnyKindOfLine()
195
    {
196 16
        $mod = strtolower($this->modifier);
197 16
        return $mod === 'l' || $mod === 'h' || $mod === 'v';
198
    }
199
200
    /**
201
     * @param       $modifier
202
     * @param array $params
203
     */
204 10
    private function addRelativeModifier($modifier, array $params)
205
    {
206 10
        $prevData = $this->getLastData();
207
208 10
        if ($modifier === 'h') {
209 2
            $params[0] += $this->getStartX($prevData);
210 2
            $params[1] = $this->getStartY($prevData);
211 10 View Code Duplication
        } elseif ($modifier === 'v') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
212 4
            $y = $params[0];
213 4
            $params[0] = $this->getStartX($prevData);
214 4
            $params[1] = $y + $this->getStartY($prevData);
215 10
        } elseif ($modifier === 'l') {
216 1
            $params[0] += $this->getStartX($prevData);
217 1
            $params[1] += $this->getStartY($prevData);
218 8
        } elseif ($modifier === 'q') {
219 1
            $x = $this->getStartX($prevData);
220 1
            $y = $this->getStartY($prevData);
221
222 1
            $params[0] += $x;
223 1
            $params[1] += $y;
224 1
            $params[2] += $x;
225 1
            $params[3] += $y;
226 7
        } elseif ($modifier === 't') {
227 3
            $lastMod = $this->getLastModifier();
228 3
            $modifier = 'Q';
229 3
            $newParams = [];
230
231 3 View Code Duplication
            if ($lastMod === 'q') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
232 1
                $newParams[0] = 2 * $prevData[2] - $prevData[0];
233 1
                $newParams[1] = 2 * $prevData[3] - $prevData[1];
234 1
            } else {
235 2
                $newParams[0] = $this->getStartX($prevData);
236 2
                $newParams[1] = $this->getStartY($prevData);
237
            }
238
239 3
            $newParams[2] = $params[0] + $this->getStartX($prevData);
240 3
            $newParams[3] = $params[1] + $this->getStartY($prevData);
241
242 3
            $params = $newParams;
243 7
        } elseif ($modifier === 's') {
244 2
            $lastMod = $this->getLastModifier();
245 2
            $modifier = 'C';
246 2
            $newParams = [];
247 2 View Code Duplication
            if ($lastMod === 'c') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248 1
                $newParams[0] = 2 * $prevData[4] - $prevData[2];
249 1
                $newParams[1] = 2 * $prevData[5] - $prevData[3];
250 1
            } else {
251 1
                $newParams[0] = $this->getStartX($prevData);
252 1
                $newParams[1] = $this->getStartY($prevData);
253
            }
254
255 2
            $x = $this->getStartX($prevData);
256 2
            $y = $this->getStartY($prevData);
257
258 2
            $newParams[2] = $params[0] + $x;
259 2
            $newParams[3] = $params[1] + $y;
260 2
            $newParams[4] = $params[2] + $x;
261 2
            $newParams[5] = $params[3] + $y;
262
263 2
            $params = $newParams;
264 5
        } elseif ($modifier === 'c') {
265 2
            $x = $this->getStartX($prevData);
266 2
            $y = $this->getStartY($prevData);
267
268 2
            $params[0] += $x;
269 2
            $params[1] += $y;
270 2
            $params[2] += $x;
271 2
            $params[3] += $y;
272 2
            $params[4] += $x;
273 2
            $params[5] += $y;
274 2
        }
275
276 10
        $this->data[] = [$modifier => $params];
277 10
    }
278
279
    /**
280
     * @param       $modifier
281
     * @param array $params
282
     */
283 26
    private function addAbsoluteModifier($modifier, array $params)
284
    {
285 26
        $prevData = $this->getLastData();
286 26
        if ($modifier === 'H') {
287 7
            $params[1] = $this->getStartY($prevData);
288 26 View Code Duplication
        } elseif ($modifier === 'V') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
289 1
            $y = $params[0];
290 1
            $params[0] = $this->getStartX($prevData);
291 1
            $params[1] = $y;
292 26
        } elseif ($modifier === 'T') {
293 3
            $lastMod = $this->getLastModifier();
294 3
            $modifier = 'Q';
295 3
            $newParams = [];
296 3 View Code Duplication
            if ($lastMod === 'Q') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
297 3
                $newParams[0] = 2 * $prevData[2] - $prevData[0];
298 3
                $newParams[1] = 2 * $prevData[3] - $prevData[1];
299 3
            } else {
300 1
                $newParams[0] = $this->getStartX($prevData);
301 1
                $newParams[1] = $this->getStartY($prevData);
302
            }
303
304 3
            $newParams[2] = $params[0];
305 3
            $newParams[3] = $params[1];
306
307 3
            $params = $newParams;
308 26
        } elseif ($modifier === 'S') {
309 3
            $lastMod = $this->getLastModifier();
310 3
            $modifier = 'C';
311 3
            $newParams = [];
312 3 View Code Duplication
            if ($lastMod === 'C') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
313 2
                $newParams[0] = 2 * $prevData[4] - $prevData[2];
314 2
                $newParams[1] = 2 * $prevData[5] - $prevData[3];
315 2
            } else {
316 1
                $newParams[0] = $this->getStartX($prevData);
317 1
                $newParams[1] = $this->getStartY($prevData);
318
            }
319
320 3
            $newParams[2] = $params[0];
321 3
            $newParams[3] = $params[1];
322 3
            $newParams[4] = $params[2];
323 3
            $newParams[5] = $params[3];
324
325 3
            $params = $newParams;
326 3
        }
327
328 26
        $this->data[] = [$modifier => $params];
329 26
    }
330
}
331