Completed
Push — master ( 1264db...4766ae )
by Edgar
03:54
created

PathBounds::getNearest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 7
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
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
     * The path points positions
19
     *
20
     * @var array
21
     */
22
    private $data = [];
23
24
    private $current;
25
26
    private $rect;
27
28
    private $originalData;
29
30
    public function __construct()
31
    {
32
        $this->rect = [
33
            'width'  => 0,
34
            'height' => 0,
35
            'x'      => null,
36
            'y'      => null,
37
        ];
38
    }
39
40
    /**
41
     * @param string $modifier Path modifier e.g. V, L, C, M
42
     * @param array  $params
43
     */
44
    public function addData($modifier, array $params)
45
    {
46
        $this->originalData = [$modifier => $params];
47
        if ($this->isRelativeModifier($modifier)) {
48
            $this->addRelativeModifier($modifier, $params);
49
        } else {
50
            $this->addAbsoluteModifier($modifier, $params);
51
        }
52
    }
53
54
    /**
55
     * @return array
56
     */
57
    public function getBox()
58
    {
59
        foreach ($this->data as $key => $value) {
60
            $this->modifier = key($value);
61
            $this->index = $key;
62
            $this->current = $value[$this->modifier];
63
64
            if ($this->isAnyKindOfLine()) {
65
                $this->getLBox();
66
            } elseif ($this->modifier === 'Q' || $this->modifier === 'q') {
67
                $this->getQBox();
68
            } elseif ($this->modifier === 'C' || $this->modifier === 'c') {
69
                $this->getCBox();
70
            }
71
        }
72
        unset($this->modifier, $this->index, $this->current);
73
74
        return $this->rect;
75
    }
76
77
    private function getLBox()
78
    {
79
        list($x1, $y1) = $this->getStartPoint();
80
        list($x2, $y2) = $this->current;
81
82
        $this->union($x1, $y1, $x2, $y2);
83
    }
84
85
    private function getPreviousData()
86
    {
87
        $mod = $this->modifierAtIndex($this->index - 1);
88
89
        return $this->data[$this->index - 1][$mod];
90
    }
91
92
    private function modifierAtIndex($index)
93
    {
94
        return key($this->data[$index]);
95
    }
96
97
    private function union($x1, $y1, $x2, $y2)
98
    {
99
        $box = [
100
            'width'  => max($x1, $x2) - min($x1, $x2),
101
            'height' => max($y1, $y2) - min($y1, $y2),
102
            'x'      => min($x1, $x2),
103
            'y'      => min($y1, $y2),
104
        ];
105
106
        if ($this->rect['x'] === null) {
107
            $this->rect['x'] = $box['x'];
108
            $this->rect['y'] = $box['y'];
109
        }
110
        $this->rect = Rect::union($this->rect, $box);
111
    }
112
113
    private function getQBox()
114
    {
115
        list($p0x, $p0y) = $this->getStartPoint();
116
        list($p1x, $p1y, $p2x, $p2y) = $this->current;
117
118
        list($x1, $y1, $x2, $y2) = Bezier::quadraticBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y);
119
120
        $this->union($x1, $y1, $x2, $y2);
121
    }
122
123
    private function getCBox()
124
    {
125
        list($p0x, $p0y) = $this->getStartPoint();
126
        list($p1x, $p1y, $p2x, $p2y, $p3x, $p3y) = $this->current;
127
128
        list($x1, $y1, $x2, $y2) = Bezier::cubicBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y, $p3x, $p3y);
129
130
        $this->union($x1, $y1, $x2, $y2);
131
    }
132
133
    /**
134
     * @return array
135
     */
136
    private function getStartPoint()
137
    {
138
        $x1 = $this->getNearest('x');
139
        $y1 = $this->getNearest('y');
140
141
        return [$x1, $y1];
142
    }
143
144
    private function getNearest($axis)
145
    {
146
        $prevData = $this->getPreviousData();
147
        $coordinate = $axis === 'x' ? $this->getStartX($prevData) : $this->getStartY($prevData);
148
149
        return $coordinate;
150
    }
151
152
    /**
153
     * @param string $mod
154
     *
155
     * @return bool
156
     */
157
    private function isRelativeModifier($mod)
158
    {
159
        return ctype_lower($mod);
160
    }
161
162
    /**
163
     * @param array $data
164
     *
165
     * @return float|false
166
     */
167
    private function getStartX($data)
168
    {
169
        return $data[count($data) - 2];
170
    }
171
172
    /**
173
     * @param array $data
174
     *
175
     * @return float
176
     */
177
    private function getStartY($data)
178
    {
179
        return $data[count($data) - 1];
180
    }
181
182
    /**
183
     * @return array
184
     */
185
    private function getLastData()
186
    {
187
        if (empty($this->data)) return [];
188
        $prevData = $this->data[count($this->data) - 1];
189
        $prevData = $prevData[key($prevData)];
190
191
        return $prevData;
192
    }
193
194
    private function getLastModifier()
195
    {
196
        return key($this->data[count($this->data) - 1]);
197
    }
198
199
    private function isAnyKindOfLine()
200
    {
201
        $mod = strtolower($this->modifier);
202
        return $mod === 'l' || $mod === 'h' || $mod === 'v';
203
    }
204
205
    /**
206
     * @param       $modifier
207
     * @param array $params
208
     */
209
    private function addRelativeModifier($modifier, array $params)
210
    {
211
        $prevData = $this->getLastData();
212
213
        if ($modifier === 'h') {
214
            $params[0] += $this->getStartX($prevData);
215
            $params[1] = $this->getStartY($prevData);
216 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...
217
            $y = $params[0];
218
            $params[0] = $this->getStartX($prevData);
219
            $params[1] = $y + $this->getStartY($prevData);
220
        } elseif ($modifier === 'l') {
221
            $params[0] += $this->getStartX($prevData);
222
            $params[1] += $this->getStartY($prevData);
223
        } elseif ($modifier === 'q') {
224
            $x = $this->getStartX($prevData);
225
            $y = $this->getStartY($prevData);
226
227
            $params[0] += $x;
228
            $params[1] += $y;
229
            $params[2] += $x;
230
            $params[3] += $y;
231
        } elseif ($modifier === 't') {
232
            $lastMod = $this->getLastModifier();
233
            $modifier = 'Q';
234
            $newParams = [];
235
236 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...
237
                $newParams[0] = 2 * $prevData[2] - $prevData[0];
238
                $newParams[1] = 2 * $prevData[3] - $prevData[1];
239
            } else {
240
                $newParams[0] = $this->getStartX($prevData);
241
                $newParams[1] = $this->getStartY($prevData);
242
            }
243
244
            $newParams[2] = $params[0] + $this->getStartX($prevData);
245
            $newParams[3] = $params[1] + $this->getStartY($prevData);
246
247
            $params = $newParams;
248
        } elseif ($modifier === 's') {
249
            $lastMod = $this->getLastModifier();
250
            $modifier = 'C';
251
            $newParams = [];
252 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...
253
                $newParams[0] = 2 * $prevData[4] - $prevData[2];
254
                $newParams[1] = 2 * $prevData[5] - $prevData[3];
255
            } else {
256
                $newParams[0] = $this->getStartX($prevData);
257
                $newParams[1] = $this->getStartY($prevData);
258
            }
259
260
            $x = $this->getStartX($prevData);
261
            $y = $this->getStartY($prevData);
262
263
            $newParams[2] = $params[0] + $x;
264
            $newParams[3] = $params[1] + $y;
265
            $newParams[4] = $params[2] + $x;
266
            $newParams[5] = $params[3] + $y;
267
268
            $params = $newParams;
269
        } elseif ($modifier === 'c') {
270
            $x = $this->getStartX($prevData);
271
            $y = $this->getStartY($prevData);
272
273
            $params[0] += $x;
274
            $params[1] += $y;
275
            $params[2] += $x;
276
            $params[3] += $y;
277
            $params[4] += $x;
278
            $params[5] += $y;
279
        }
280
281
        $this->data[] = [$modifier => $params];
282
    }
283
284
    /**
285
     * @param       $modifier
286
     * @param array $params
287
     */
288
    private function addAbsoluteModifier($modifier, array $params)
289
    {
290
        $prevData = $this->getLastData();
291
        if ($modifier === 'H') {
292
            $params[1] = $this->getStartY($prevData);
293 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...
294
            $y = $params[0];
295
            $params[0] = $this->getStartX($prevData);
296
            $params[1] = $y;
297
        } elseif ($modifier === 'T') {
298
            $lastMod = $this->getLastModifier();
299
            $modifier = 'Q';
300
            $newParams = [];
301 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...
302
                $newParams[0] = 2 * $prevData[2] - $prevData[0];
303
                $newParams[1] = 2 * $prevData[3] - $prevData[1];
304
            } else {
305
                $newParams[0] = $this->getStartX($prevData);
306
                $newParams[1] = $this->getStartY($prevData);
307
            }
308
309
            $newParams[2] = $params[0];
310
            $newParams[3] = $params[1];
311
312
            $params = $newParams;
313
        } elseif ($modifier === 'S') {
314
            $lastMod = $this->getLastModifier();
315
            $modifier = 'C';
316
            $newParams = [];
317 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...
318
                $newParams[0] = 2 * $prevData[4] - $prevData[2];
319
                $newParams[1] = 2 * $prevData[5] - $prevData[3];
320
            } else {
321
                $newParams[0] = $this->getStartX($prevData);
322
                $newParams[1] = $this->getStartY($prevData);
323
            }
324
325
            $newParams[2] = $params[0];
326
            $newParams[3] = $params[1];
327
            $newParams[4] = $params[2];
328
            $newParams[5] = $params[3];
329
330
            $params = $newParams;
331
        }
332
333
        $this->data[] = [$modifier => $params];
334
    }
335
}
336