Completed
Push — master ( 9016b2...9b9106 )
by Edgar
03:29
created

Transform::matchSkew()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
namespace nstdio\svg\util;
3
use Doctrine\Instantiator\Exception\InvalidArgumentException;
4
5
/**
6
 * Class Transform
7
 *
8
 * @package nstdio\svg\util
9
 * @author  Edgar Asatryan <[email protected]>
10
 */
11
final class Transform implements TransformInterface
12
{
13
    private $trans;
14
15
    private $data;
16
17
    private $sequence;
18
19
    private $argDelimiter = TransformInterface::ARG_DELIM_SPACE;
20
21
    const ROTATE_PATTERN = "/rotate\s*\(\s*(?<a>[+-]?\d+(?:\.\d+)?)((?:\s{1,}\,?\s*|\,\s*)(?<x>[+-]?\d+(?:\.\d+)?)(?:\s{1,}\,?\s*|\,\s*)(?<y>[+-]?\d+(?:\.\d+)?))?\s*\)/";
22
23
    const TRANSLATE_PATTERN = "/translate\s*\(\s*(?<x>[+-]?\d+(?:\.\d+)?)((?:\s{1,}\,?\s*|\,\s*)(?<y>[+-]?\d+(?:\.\d+)?))?\)/";
24
25
    const SCALE_PATTERN = "/scale\s*\(\s*(?<x>[+-]?\d+(?:\.\d+)?)((?:\s{1,}\,?\s*|\,\s*)(?<y>[+-]?\d+(?:\.\d+)?))?\)/";
26
27
    const SKEW_PATTERN = "/skew([XY])\s*\(\s*(?<x>[+-]?\d+(?:\.\d+)?)?\)/";
28
29
    const MATRIX_PATTERN = "/matrix\s*\(\s*((([+-]?\d+(?:\.\d+)?)(?:\s+,?\s*|,\s*)){5}([+-]?\d+(?:\.\d+)?)\s*)\)/";
30
31
    private function __construct()
32
    {
33
    }
34
35
    /**
36
     * Use this method to instantiate Transform class.
37
     *
38
     * @param $transformString
39
     *
40
     * @uses matchRotate()
41
     * @uses matchSkewX()
42
     * @uses matchSkewY()
43
     * @uses matchTranslate()
44
     * @uses matchScale()
45
     * @uses matchMatrix()
46
     *
47
     * @return Transform
48
     */
49
    public static function newInstance($transformString = null)
50
    {
51
        $instance = new Transform();
52
        $instance->trans = $transformString;
53
        $instance->sequence = $instance->makeSequence();
54
55
        foreach ($instance->sequence as $value) {
56
            $method = 'match' . ucfirst($value);
57
            $instance->data[$value] = $instance->$method();
58
        }
59
60
        return $instance;
61
    }
62
63
    /**
64
     * @inheritdoc
65
     */
66
    public function result()
67
    {
68
        return $this->trans;
69
    }
70
71
    /**
72
     * @inheritdoc
73
     */
74
    public function setArgumentDelimiter($delim)
75
    {
76
        if ($delim !== TransformInterface::ARG_DELIM_COMMA &&
77
            $delim !== TransformInterface::ARG_DELIM_COMMA_SPACE &&
78
            $delim !== TransformInterface::ARG_DELIM_SPACE
79
        ) {
80
            throw new \InvalidArgumentException("Invalid delimiter. See TransformInterface::setArgumentDelimiter documentation for valid value.");
81
        }
82
83
        $this->argDelimiter = $delim;
84
    }
85
86
    /**
87
     * @inheritdoc
88
     */
89
    public function rotate($angle, $cx = null, $cy = null)
90
    {
91
        if ($cx !== null && $cy === null) {
92
            $cy = $cx;
93
        }
94
        if ($cy !== null && $cx === null) {
95
            $cx = $cy;
96
        }
97
98
        return $this->shortcutBuild('rotate', [$angle, $cx, $cy]);
99
    }
100
101
    /**
102
     * @inheritdoc
103
     */
104
    public function translate($x, $y = null)
105
    {
106
        return $this->shortcutBuild('translate', [$x, $y]);
107
    }
108
109
    /**
110
     * @inheritdoc
111
     */
112
    public function scale($x, $y = null)
113
    {
114
        return $this->shortcutBuild('scale', [$x, $y]);
115
    }
116
117
    /**
118
     * @inheritdoc
119
     */
120
    public function skewX($x)
121
    {
122
        return $this->shortcutBuild('skewX', [$x]);
123
    }
124
125
    /**
126
     * @inheritdoc
127
     */
128
    public function skewY($y)
129
    {
130
        return $this->shortcutBuild('skewY', [$y]);
131
    }
132
133
    /**
134
     * @inheritdoc
135
     */
136
    public function matrix(array $matrix)
137
    {
138
        if (count($matrix) !== 6) {
139
            throw new InvalidArgumentException("Invalid matrix size. You must provide en array with 6 elements. " . count($matrix) . " elements given.");
140
        }
141
142
        return $this->shortcutBuild('matrix', $matrix);
143
    }
144
145
    /**
146
     * @return mixed
147
     */
148
    private function matchRotate()
149
    {
150
        return $this->matchPattern(self::ROTATE_PATTERN, ['a', 'x', 'y']);
151
    }
152
153
    private function matchSkewX()
154
    {
155
        return $this->matchSkew();
156
    }
157
158
    private function matchSkewY()
159
    {
160
        return $this->matchSkew();
161
    }
162
163
    private function matchSkew()
164
    {
165
        return $this->matchPattern(self::SKEW_PATTERN, ['x']);
166
    }
167
168
    private function matchScale()
169
    {
170
        return $this->matchPattern(self::SCALE_PATTERN, ['x', 'y']);
171
    }
172
173
    private function matchMatrix()
174
    {
175
        preg_match(self::MATRIX_PATTERN, $this->trans, $matches);
176
        if (isset($matches[1]) === false) {
177
            throw new \InvalidArgumentException("Cannot match matrix transformation.");
178
        }
179
180
        $matrix = explode(' ', preg_replace(['/\s+/', '/\,+/'], [' ', ''], $matches[1]), 6);
181
182
        return $matrix;
183
    }
184
185
    private function matchTranslate()
186
    {
187
        return $this->matchPattern(self::TRANSLATE_PATTERN, ['x', 'y']);
188
    }
189
190
    /**
191
     * @return array
192
     */
193
    public function getData()
194
    {
195
        return $this->data;
196
    }
197
198
    private function hasTransform($transform)
199
    {
200
        return in_array($transform, $this->sequence);
201
    }
202
203
    private function getTransform($transform)
204
    {
205
        if ($this->hasTransform($transform)) {
206
            return $this->data[$transform];
207
        }
208
209
        return null;
210
    }
211
212
    private function makeSequence()
213
    {
214
        preg_match_all("/\s*(matrix|translate|scale|rotate|skew[XY])/i", $this->trans, $matches);
215
216
        return $matches[1];
217
    }
218
219
    private function addTransformSequence($transform)
220
    {
221
        $this->sequence[] = $transform;
222
    }
223
224
    private function buildTransformString()
225
    {
226
        $ret = '';
227
        foreach ($this->sequence as $transform) {
228
            $ret .= $transform . "(" . rtrim(implode($this->argDelimiter, $this->data[$transform])) . ") ";
229
        }
230
        $this->trans = rtrim($ret);
231
232
        return $this->trans;
233
    }
234
235
    private function setTransformData($transform, $data)
236
    {
237
        if (isset($this->data[$transform]) === true) {
238
            $oldData = $this->data[$transform];
239
            foreach ($data as $key => $item) {
240
                if ($item === null) {
241
                    $data[$key] = $oldData[$key];
242
                }
243
            }
244
        }
245
        $this->data[$transform] = $data;
246
    }
247
248
    private function addTransformIfNeeded($transform)
249
    {
250
        if ($this->getTransform($transform) === null) {
251
            $this->addTransformSequence($transform);
252
        }
253
    }
254
255
    private function matchPattern($pattern, $named)
256
    {
257
        preg_match($pattern, $this->trans, $matches);
258
        $ret = [];
259
        foreach ($named as $value) {
260
            $ret[] = isset($matches[$value]) ? $matches[$value] : null;
261
        }
262
263
        return $ret;
264
    }
265
266
    private function shortcutBuild($transform, $data)
267
    {
268
        $this->addTransformIfNeeded($transform);
269
270
        $this->setTransformData($transform, $data);
271
272
        return $this->buildTransformString();
273
    }
274
}