Completed
Push — master ( a105e9...c17b9b )
by Edgar
03:15
created

PathBounds::getHRelBox()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 10
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 10
loc 10
rs 9.4285
cc 1
eloc 6
nc 1
nop 0
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($x1, $y1, $x2, $y2);
91
    }
92
93
    private function getLRelBox()
94
    {
95
        list($x1, $y1) = $this->getStartPoint();
96
        list($x2, $y2) = $this->current;
97
98
        $x2 += $x1;
99
        $y2 += $y1;
100
101
        $this->union($x1, $y1, $x2, $y2);
102
    }
103
104
    private function getPreviousData()
105
    {
106
        $mod = $this->modifierAtIndex($this->index - 1);
107
108
        return $this->data[$this->index - 1][$mod];
109
    }
110
111
    private function modifierAtIndex($index)
112
    {
113
        return key($this->data[$index]);
114
    }
115
116
    private function union($x1, $y1, $x2, $y2)
117
    {
118
        $box = [
119
            'width'  => max($x1, $x2) - min($x1, $x2),
120
            'height' => max($y1, $y2) - min($y1, $y2),
121
            'x'      => min($x1, $x2),
122
            'y'      => min($y1, $y2),
123
        ];
124
125
        if ($this->rect['x'] === null) {
126
            $this->rect['x'] = $box['x'];
127
            $this->rect['y'] = $box['y'];
128
        }
129
        $this->rect = Rect::union($this->rect, $box);
130
    }
131
132
    private function getQBox()
133
    {
134
        list($p0x, $p0y) = $this->getStartPoint();
135
        list($p1x, $p1y, $p2x, $p2y) = $this->current;
136
137
        list($x1, $y1, $x2, $y2) = Bezier::quadraticBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y);
138
139
        $this->union($x1, $y1, $x2, $y2);
140
    }
141
142
    private function getCBox()
143
    {
144
        list($p0x, $p0y) = $this->getStartPoint();
145
        list($p1x, $p1y, $p2x, $p2y, $p3x, $p3y) = $this->current;
146
147
        list($x1, $y1, $x2, $y2) = Bezier::cubicBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y, $p3x, $p3y);
148
149
        $this->union($x1, $y1, $x2, $y2);
150
    }
151
152
    /**
153
     * @return array
154
     */
155
    public function getData()
156
    {
157
        return $this->data;
158
    }
159
160 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...
161
    {
162
        $x1 = $this->vhValue('x');
163
        $y1 = $this->vhValue('y');
164
        $x2 = $this->current[0];
165
166
        $this->union($x1, $y1, $x2, $y1);
167
    }
168
169
170 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...
171
    {
172
        $x1 = $this->vhValue('x');
173
        $y1 = $this->vhValue('y');
174
        $x2 = $this->current[0];
175
176
        $x2 += $x1;
177
178
        $this->union($x1, $y1, $x2, $y1);
179
    }
180
181 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...
182
    {
183
        $x1 = $this->vhValue('x');
184
        $y1 = $this->vhValue('y');
185
        $y2 = $this->current[0];
186
187
        $this->union($x1, $y1, $x1, $y2);
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($x1, $y1, $x1, $y2);
200
    }
201
202
    /**
203
     * @return array
204
     */
205
    private function getStartPoint()
206
    {
207
        $x1 = $this->getNearest('x');
208
        $y1 = $this->getNearest('y');
209
210
        return [$x1, $y1];
211
    }
212
213
    private function getNearest($axis)
214
    {
215
        $prevData = $this->getPreviousData();
216
        $coordinate = $axis === 'x' ? $this->getStartX($prevData) : $this->getStartY($prevData);
217
218
        if ($this->isRelativeModifier($this->index - 1)) {
219
            for ($i = $this->index - 2; $i >= 0; $i--) {
220
                $data = $this->data[$i][$this->modifierAtIndex($i)];
221
                if (!$this->isRelativeModifier($i)) {
222
                    $ret = $this->getStart($axis, $data);
223
                    $coordinate += $ret;
224
                    if ($axis === 'x' || $axis === 'y') {
225
                        $coordinate -= $this->getFirst($axis); // need for proper computation when relative modifier has negative value.
226
                        break;
227
                    }
228
                }
229
            }
230
        }
231
232
        return $coordinate;
233
    }
234
235
    private function getFirst($axis)
236
    {
237
        $mod = $this->modifierAtIndex(0);
238
239
        return $axis === 'x' ? $this->data[0][$mod][0] : $this->data[0][$mod][1];
240
    }
241
242
    private function getStart($axis, $data)
243
    {
244
        return $axis === 'x' ? $this->getStartX($data) : $this->getStartY($data);
245
    }
246
247
    /**
248
     * @param $index
249
     *
250
     * @return bool
251
     */
252
    private function isRelativeModifier($index)
253
    {
254
        return ctype_lower($this->modifierAtIndex($index));
255
    }
256
257
    /**
258
     * @param $axis
259
     *
260
     * @return mixed
261
     */
262
    private function vhValue($axis)
263
    {
264
        for ($i = $this->index - 1; $i >= 0; $i--) {
265
            $data = $this->data[$i];
266
            $modifier = key($data);
267
            $data = $data[$modifier];
268
            $modifier = strtolower($modifier);
269
            if ($modifier !== 'h' && $modifier !== 'v') {
270
                return $this->getStart($axis, $data);
271
            }
272
        }
273
274
        throw new \RuntimeException("Cannot found nearest {$axis} coordinate");
275
    }
276
277
    /**
278
     * @param $data
279
     *
280
     * @return float|false
281
     */
282
    private function getStartX($data)
283
    {
284
        reset($data);
285
        end($data);
286
        $x = prev($data);
287
288
        return $x;
289
    }
290
291
    /**
292
     * @param $data
293
     *
294
     * @return float
295
     */
296
    private function getStartY($data)
297
    {
298
        reset($data);
299
        $y = end($data);
300
301
        return $y;
302
    }
303
}
304