Completed
Push — master ( d117bd...a105e9 )
by Edgar
04:07
created

PathBounds::getStart()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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