Completed
Push — master ( 84cc98...56d3d7 )
by Edgar
03:12
created

Path::moveTo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 11
rs 9.4285
cc 2
eloc 6
nc 2
nop 3
1
<?php
2
namespace nstdio\svg\shape;
3
4
use nstdio\svg\container\ContainerInterface;
5
use nstdio\svg\ElementInterface;
6
use nstdio\svg\traits\ElementTrait;
7
8
/**
9
 * Class Path
10
 *
11
 * @property string $d This attribute defines a path to follow. {@link
12
 *           https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d}
13
 * @package shape
14
 * @author  Edgar Asatryan <[email protected]>
15
 */
16
class Path extends Shape implements ContainerInterface
17
{
18
    use ElementTrait;
19
20
    /**
21
     * @var  PathBounds
22
     */
23
    private $boundingBox;
24
25
    /**
26
     * Path constructor.
27
     *
28
     * @param ElementInterface $parent
29
     * @param float            $x
30
     * @param float            $y
31
     * @param bool             $absolute
32
     */
33
    public function __construct(ElementInterface $parent, $x, $y, $absolute = true)
34
    {
35
        parent::__construct($parent);
36
37
        $this->boundingBox = new PathBounds();
38
        $this->moveTo($x, $y, $absolute);
39
    }
40
41
    /**
42
     * Start a new sub-path at the given (x,y) coordinate. M (uppercase) indicates that absolute coordinates will
43
     * follow; m (lowercase) indicates that relative coordinates will follow. If a moveto is followed by multiple pairs
44
     * of coordinates, the subsequent pairs are treated as implicit lineto commands. Hence, implicit lineto commands
45
     * will be relative if the moveto is relative, and absolute if the moveto is absolute. If a relative moveto (m)
46
     * appears as the first element of the path, then it is treated as a pair of absolute coordinates. In this case,
47
     * subsequent pairs of coordinates are treated as relative even though the initial moveto is interpreted as an
48
     * absolute moveto.
49
     *
50
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
51
     *
52
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
53
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
54
     * @param bool  $absolute
55
     *
56
     * @return $this
57
     */
58
    public function moveTo($x, $y, $absolute = true)
59
    {
60
        $this->checkFirstModifier();
61
62
        $modifier = $absolute ? 'M' : 'm';
63
        $this->d = "$modifier $x, $y";
64
65
        $this->boundingBox->addData($modifier, [$x, $y]);
66
67
        return $this;
68
    }
69
70
    /**
71
     * @param string $type
72
     */
73
    private function buildPath($type)
74
    {
75
        $params = array_slice(func_get_args(), 1);
76
        $this->boundingBox->addData($type, $params);
77
78
        $this->d .= " $type";
79
        foreach ($params as $key => $value) {
80
            if (is_array($value)) {
81
                $this->addArrayToPath($value);
82
            } else {
83
                if ($key % 2 !== 0 && !is_array($params[$key - 1])) {
84
                    $this->d .= ", $value";
85
                } else {
86
                    $this->d .= " $value";
87
                }
88
            }
89
        }
90
    }
91
92
    /**
93
     * Draw a line from the current point to the given (x,y) coordinate which becomes the new current point. L
94
     * (uppercase) indicates that absolute coordinates will follow; l (lowercase) indicates that relative coordinates
95
     * will follow. A number of coordinates pairs may be specified to draw a polyline. At the end of the command, the
96
     * new current point is set to the final set of coordinates provided.
97
     *
98
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
99
     *
100
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
101
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
102
     * @param bool  $absolute
103
     *
104
     * @return $this
105
     */
106
    public function lineTo($x, $y, $absolute = true)
107
    {
108
        $this->buildPath($absolute ? 'L' : 'l', $x, $y);
109
110
        return $this;
111
    }
112
113
    /**
114
     * Draws a horizontal line from the current point (cpx, cpy) to (x, cpy). H (uppercase) indicates that absolute
115
     * coordinates will follow; h (lowercase) indicates that relative coordinates will follow. Multiple x values can be
116
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
117
     * (x, cpy) for the final value of x.
118
     *
119
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
120
     *
121
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
122
     *
123
     * @param bool  $absolute
124
     *
125
     * @return $this
126
     */
127
    public function hLineTo($x, $absolute = true)
128
    {
129
        $this->buildPath($absolute ? 'H' : 'h', $x);
130
131
        return $this;
132
    }
133
134
    /**
135
     * Draws a vertical line from the current point (cpx, cpy) to (cpx, y). V (uppercase) indicates that absolute
136
     * coordinates will follow; v (lowercase) indicates that relative coordinates will follow. Multiple y values can be
137
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
138
     * (cpx, y) for the final value of y.
139
     *
140
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
141
     *
142
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
143
     *
144
     * @param bool  $absolute
145
     *
146
     * @return $this
147
     */
148
    public function vLineTo($y, $absolute = true)
149
    {
150
        $this->buildPath($absolute ? 'V' : 'v', $y);
151
152
        return $this;
153
    }
154
155
    /**
156
     * Draws a cubic Bézier curve from the current point to (x,y) using (x1,y1) as the control point at the beginning
157
     * of the curve and (x2,y2) as the control point at the end of the curve.
158
     *
159
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
160
     *
161
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
162
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
163
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
164
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
165
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
166
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
167
     *
168
     * @param bool  $absolute
169
     *
170
     * @return $this
171
     */
172
    public function curveTo($x1, $y1, $x2, $y2, $x, $y, $absolute = true)
173
    {
174
        $this->buildPath($absolute ? 'C' : 'c', $x1, $y1, $x2, $y2, $x, $y);
175
176
        return $this;
177
    }
178
179
    /**
180
     * Draws a cubic Bézier curve from the current point to (x,y). The first control point is assumed to be the
181
     * reflection of the second control point on the previous command relative to the current point. (If there is no
182
     * previous command or if the previous command was not an C, c, S or s, assume the first control point is
183
     * coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at the end of
184
     * the curve). S (uppercase) indicates that absolute coordinates will follow; s (lowercase) indicates that relative
185
     * coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the
186
     * command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
187
     *
188
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
189
     *
190
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
191
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
192
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
193
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
194
     *
195
     * @param bool  $absolute
196
     *
197
     * @return $this
198
     */
199
    public function smoothCurveTo($x2, $y2, $x, $y, $absolute = true)
200
    {
201
        $this->buildPath($absolute ? 'S' : 's', $x2, $y2, $x, $y);
202
203
        return $this;
204
    }
205
206
    /**
207
     * Draws a quadratic Bézier curve from the current point to (x,y) using (x1,y1) as the control point. Q (uppercase)
208
     * indicates that absolute coordinates will follow; q (lowercase) indicates that relative coordinates will follow.
209
     * Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current
210
     * point becomes the final (x,y) coordinate pair used in the polybézier.
211
     *
212
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
213
     *
214
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
215
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
216
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
217
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
218
     *
219
     * @param bool  $absolute
220
     *
221
     * @return $this
222
     */
223
    public function quadraticCurveTo($x1, $y1, $x, $y, $absolute = true)
224
    {
225
        $this->buildPath($absolute ? 'Q' : 'q', $x1, $y1, $x, $y);
226
227
        return $this;
228
    }
229
230
    /**
231
     * Draws a quadratic Bézier curve from the current point to (x,y). The control point is assumed to be the
232
     * reflection of the control point on the previous command relative to the current point. (If there is no previous
233
     * command or if the previous command was not a Q, q, T or t, assume the control point is coincident with the
234
     * current point.) T (uppercase) indicates that absolute coordinates will follow; t (lowercase) indicates that
235
     * relative coordinates will follow. At the end of the command, the new current point becomes the final (x,y)
236
     * coordinate pair used in the polybézier.
237
     *
238
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
239
     *
240
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
241
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
242
     *
243
     * @param bool  $absolute
244
     *
245
     * @return $this
246
     */
247
    public function smoothQuadraticCurveTo($x, $y, $absolute = true)
248
    {
249
        $this->buildPath($absolute ? 'T' : 't', $x, $y);
250
251
        return $this;
252
    }
253
254
    /**
255
     * Draws an elliptical arc from the current point to (x, y). The size and orientation of the ellipse are defined by
256
     * two radii (rx, ry) and an x-axis-rotation, which indicates how the ellipse as a whole is rotated relative to the
257
     * current coordinate system. The center (cx, cy) of the ellipse is calculated automatically to satisfy the
258
     * constraints imposed by the other parameters. large-arc-flag and sweep-flag contribute to the automatic
259
     * calculations and help determine how the arc is drawn.
260
     *
261
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataEllipticalArcCommands
262
     *
263
     * @param float   $rx           The x-axis radius for the ellipse (i.e., r1).
264
     * @param float   $ry           The y-axis radius for the ellipse
265
     * @param float   $xRotation    The rotation angle in degrees for the ellipse's x-axis relative to the x-axis of
266
     *                              the user coordinate system.
267
     * @param boolean $largeArcFlag The value of the large-arc-flag parameter.
268
     * @param boolean $sweepFlag    The value of the sweep-flag parameter.
269
     * @param float   $x            The absolute (relative) X coordinate for the end point of this path segment.
270
     * @param float   $y            The absolute (relative) Y coordinate for the end point of this path segment.
271
     * @param bool    $absolute
272
     *
273
     * @return $this
274
     */
275
    public function arcTo($rx, $ry, $xRotation, $largeArcFlag, $sweepFlag, $x, $y, $absolute = true)
276
    {
277
        $this->buildPath($absolute ? 'A' : 'a', [$rx, $ry], $xRotation, [$largeArcFlag ? 1 : 0, $sweepFlag ? 1 : 0], [$x, $y]);
278
279
        return $this;
280
    }
281
282
    /**
283
     * Close the current subpath by drawing a straight line from the current point to current subpath's initial point.
284
     * Since the Z and z commands take no parameters, they have an identical effect.
285
     *
286
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
287
     *
288
     * @param bool $absolute
289
     */
290
    public function closePath($absolute = true)
291
    {
292
        $this->buildPath($absolute ? 'Z' : 'z');
293
    }
294
295
    public function getName()
296
    {
297
        return 'path';
298
    }
299
300
    public function getBoundingBox()
301
    {
302
        return $this->boundingBox->getBox();
303
    }
304
305
    protected function getCenterX()
306
    {
307
        // TODO: Implement getCenterX() method.
308
    }
309
310
    protected function getCenterY()
311
    {
312
        // TODO: Implement getCenterY() method.
313
    }
314
315
    /**
316
     * @param $value
317
     */
318
    private function addArrayToPath(array $value)
319
    {
320
        foreach ($value as $item) {
321
            $this->d .= " $item,";
322
        }
323
        $this->d = rtrim($this->d, ',');
324
    }
325
326
    /**
327
     *
328
     */
329
    private function checkFirstModifier()
330
    {
331
        if ($this->d !== null) {
332
            throw new \BadMethodCallException("First modifier for path must be: M or m. Youb");
333
        }
334
    }
335
}