Completed
Push — master ( ca28b6...1264db )
by Edgar
82:42
created

PathBounds::getNearest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
c 0
b 0
f 0
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
            $prevData = $this->getLastData();
49
50
            if ($modifier === 'h') {
51
                $params[0] += $this->getStartX($prevData);
52
                $params[1] = $this->getStartY($prevData);
53 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...
54
                $y = $params[0];
55
                $params[0] = $this->getStartX($prevData);
56
                $params[1] = $y + $this->getStartY($prevData);
57
            } elseif ($modifier === 'l') {
58
                $params[0] += $this->getStartX($prevData);
59
                $params[1] += $this->getStartY($prevData);
60
            }
61
62
            $this->data[] = [$modifier => $params];
63
        } else {
64
            $prevData = $this->getLastData();
65
            if ($modifier === 'H') {
66
                $params[1] = $this->getStartY($prevData);
67 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...
68
                $y = $params[0];
69
                $params[0] = $this->getStartX($prevData);
70
                $params[1] = $y;
71
            }
72
73
            $this->data[] = [$modifier => $params];
74
        }
75
    }
76
77
    /**
78
     * @return array
79
     */
80
    public function getBox()
81
    {
82
        foreach ($this->data as $key => $value) {
83
            $this->modifier = key($value);
84
            $this->index = $key;
85
            $this->current = $value[$this->modifier];
86
87
            if ($this->isAnyKindOfLine()) {
88
                $this->getLBox();
89
            } elseif ($this->modifier === 'Q') {
90
                $this->getQBox();
91
            } elseif ($this->modifier === 'C') {
92
                $this->getCBox();
93
            }
94
        }
95
        unset($this->modifier, $this->index, $this->current);
96
97
        return $this->rect;
98
    }
99
100
    private function getLBox()
101
    {
102
        list($x1, $y1) = $this->getStartPoint();
103
        list($x2, $y2) = $this->current;
104
105
        $this->union($x1, $y1, $x2, $y2);
106
    }
107
108
    private function getPreviousData()
109
    {
110
        $mod = $this->modifierAtIndex($this->index - 1);
111
112
        return $this->data[$this->index - 1][$mod];
113
    }
114
115
    private function modifierAtIndex($index)
116
    {
117
        return key($this->data[$index]);
118
    }
119
120
    private function union($x1, $y1, $x2, $y2)
121
    {
122
        $box = [
123
            'width'  => max($x1, $x2) - min($x1, $x2),
124
            'height' => max($y1, $y2) - min($y1, $y2),
125
            'x'      => min($x1, $x2),
126
            'y'      => min($y1, $y2),
127
        ];
128
129
        if ($this->rect['x'] === null) {
130
            $this->rect['x'] = $box['x'];
131
            $this->rect['y'] = $box['y'];
132
        }
133
        $this->rect = Rect::union($this->rect, $box);
134
    }
135
136
    private function getQBox()
137
    {
138
        list($p0x, $p0y) = $this->getStartPoint();
139
        list($p1x, $p1y, $p2x, $p2y) = $this->current;
140
141
        list($x1, $y1, $x2, $y2) = Bezier::quadraticBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y);
142
143
        $this->union($x1, $y1, $x2, $y2);
144
    }
145
146
    private function getCBox()
147
    {
148
        list($p0x, $p0y) = $this->getStartPoint();
149
        list($p1x, $p1y, $p2x, $p2y, $p3x, $p3y) = $this->current;
150
151
        list($x1, $y1, $x2, $y2) = Bezier::cubicBBox($p0x, $p0y, $p1x, $p1y, $p2x, $p2y, $p3x, $p3y);
152
153
        $this->union($x1, $y1, $x2, $y2);
154
    }
155
156
    /**
157
     * @return array
158
     */
159
    public function getData()
160
    {
161
        return $this->data;
162
    }
163
164
    /**
165
     * @return array
166
     */
167
    private function getStartPoint()
168
    {
169
        $x1 = $this->getNearest('x');
170
        $y1 = $this->getNearest('y');
171
172
        return [$x1, $y1];
173
    }
174
175
    private function getNearest($axis)
176
    {
177
        $prevData = $this->getPreviousData();
178
        $coordinate = $axis === 'x' ? $this->getStartX($prevData) : $this->getStartY($prevData);
179
180
        return $coordinate;
181
    }
182
183
    /**
184
     * @param string $mod
185
     *
186
     * @return bool
187
     */
188
    private function isRelativeModifier($mod)
189
    {
190
        return ctype_lower($mod);
191
    }
192
193
    /**
194
     * @param array $data
195
     *
196
     * @return float|false
197
     */
198
    private function getStartX($data)
199
    {
200
        return $data[count($data) - 2];
201
    }
202
203
    /**
204
     * @param array $data
205
     *
206
     * @return float
207
     */
208
    private function getStartY($data)
209
    {
210
        return $data[count($data) - 1];
211
    }
212
213
    /**
214
     * @return array
215
     */
216
    private function getLastData()
217
    {
218
        if (empty($this->data)) return [];
219
        $prevData = $this->data[count($this->data) - 1];
220
        $prevData = $prevData[key($prevData)];
221
222
        return $prevData;
223
    }
224
225
    private function isAnyKindOfLine()
226
    {
227
        $mod = strtolower($this->modifier);
228
        return $mod === 'l' || $mod === 'h' || $mod === 'v';
229
    }
230
}
231