Test Failed
Push — master ( 40f013...45c23a )
by Edgar
10:04
created

Path::curveTo()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 1
nop 7
crap 2
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 10
    public function __construct(ElementInterface $parent, $x, $y, $absolute = true)
34
    {
35 10
        parent::__construct($parent);
36
37 10
        $this->boundingBox = new PathBounds();
38 10
        $this->moveTo($x, $y, $absolute);
39 10
    }
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 10
    public function moveTo($x, $y, $absolute = true)
59
    {
60 10
        $modifier = $absolute ? 'M' : 'm';
61 10
        $this->d = "$modifier $x, $y";
62
63 10
        $this->boundingBox->addData($modifier, [$x, $y]);
64
65 10
        return $this;
66
    }
67
68
    /**
69
     * @param ElementInterface $parent
70
     * @param                  $x0
71
     * @param                  $y0
72
     * @param                  $x1
73
     * @param                  $y1
74
     * @param                  $x
75
     * @param                  $y
76
     * @param bool             $absolute
77
     *
78
     * @return Path|ContainerInterface
79
     */
80
    public static function quadraticCurve(ElementInterface $parent, $x0, $y0, $x1, $y1, $x, $y, $absolute = true)
81
    {
82
        return self::create($parent, $x0, $y0, $absolute)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\container\ContainerInterface as the method quadraticCurveTo() does only exist in the following implementations of said interface: nstdio\svg\shape\Path.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
83
            ->quadraticCurveTo($x1, $y1, $x, $y);
84
    }
85
86
    /**
87
     * Draws a quadratic Bézier curve from the current point to (x,y) using (x1,y1) as the control point. Q (uppercase)
88
     * indicates that absolute coordinates will follow; q (lowercase) indicates that relative coordinates will follow.
89
     * Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current
90
     * point becomes the final (x,y) coordinate pair used in the polybézier.
91
     *
92
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
93
     *
94
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
95
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
96
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
97
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
98
     *
99
     * @param bool  $absolute
100
     *
101
     * @return $this
102
     */
103 1
    public function quadraticCurveTo($x1, $y1, $x, $y, $absolute = true)
104
    {
105 1
        $this->buildPath($absolute ? 'Q' : 'q', $x1, $y1, $x, $y);
106
107 1
        return $this;
108
    }
109
110
    /**
111
     * @param string $type
112
     */
113 7
    private function buildPath($type)
114
    {
115 7
        $params = array_slice(func_get_args(), 1);
116 7
        $this->boundingBox->addData($type, $params);
117
118 7
        $this->d .= " $type";
119 7
        foreach ($params as $key => $value) {
120 7
            if (is_array($value)) {
121 4
                $this->addArrayToPath($value);
122 4
            } else {
123 7
                if ($key % 2 !== 0 && !is_array($params[$key - 1])) {
124 5
                    $this->d .= ", $value";
125 5
                } else {
126 7
                    $this->d .= " $value";
127
                }
128
            }
129 7
        }
130 7
    }
131
132
    /**
133
     * @param $value
134
     */
135 4
    private function addArrayToPath(array $value)
136
    {
137 4
        foreach ($value as $item) {
138 4
            $this->d .= " $item,";
139 4
        }
140 4
        $this->d = rtrim($this->d, ',');
141 4
    }
142
143
    /**
144
     * @param ElementInterface $parent
145
     * @param                  $x
146
     * @param                  $y
147
     * @param bool             $absolute
148
     *
149
     * @return Path|ContainerInterface
150
     */
151 1
    public static function create(ElementInterface $parent, $x, $y, $absolute = true)
152
    {
153 1
        return new Path($parent, $x, $y, $absolute);
154
    }
155
156
    /**
157
     * @param ElementInterface $parent
158
     * @param                  $x
159
     * @param                  $y
160
     * @param                  $x1
161
     * @param                  $y1
162
     * @param bool             $absolute
163
     *
164
     * @return ContainerInterface|Path
165
     */
166 1
    public static function line(ElementInterface $parent, $x, $y, $x1, $y1, $absolute = true)
167
    {
168 1
        return self::create($parent, $x, $y, $absolute)->lineTo($x1, $y1, $absolute);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\container\ContainerInterface as the method lineTo() does only exist in the following implementations of said interface: nstdio\svg\shape\Path.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
169
    }
170
171
    /**
172
     * Draw a line from the current point to the given (x,y) coordinate which becomes the new current point. L
173
     * (uppercase) indicates that absolute coordinates will follow; l (lowercase) indicates that relative coordinates
174
     * will follow. A number of coordinates pairs may be specified to draw a polyline. At the end of the command, the
175
     * new current point is set to the final set of coordinates provided.
176
     *
177
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
178
     *
179
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
180
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
181
     * @param bool  $absolute
182
     *
183
     * @return $this
184
     */
185 5
    public function lineTo($x, $y, $absolute = true)
186
    {
187 5
        $this->buildPath($absolute ? 'L' : 'l', $x, $y);
188
189 5
        return $this;
190
    }
191
192
    /**
193
     * @param ElementInterface $parent
194
     * @param                  $x
195
     * @param                  $y
196
     * @param                  $x1
197
     * @param bool             $absolute
198
     *
199
     * @return ContainerInterface|Path
200
     */
201
    public static function hLine(ElementInterface $parent, $x, $y, $x1, $absolute = true)
202
    {
203
        return self::create($parent, $x, $y, $absolute)->hLineTo($x1, $absolute);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\container\ContainerInterface as the method hLineTo() does only exist in the following implementations of said interface: nstdio\svg\shape\Path.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
204
    }
205
206
    /**
207
     * Draws a horizontal line from the current point (cpx, cpy) to (x, cpy). H (uppercase) indicates that absolute
208
     * coordinates will follow; h (lowercase) indicates that relative coordinates will follow. Multiple x values can be
209
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
210
     * (x, cpy) for the final value of x.
211
     *
212
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
213
     *
214
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
215
     *
216
     * @param bool  $absolute
217
     *
218
     * @return $this
219
     */
220 4
    public function hLineTo($x, $absolute = true)
221
    {
222 4
        $this->buildPath($absolute ? 'H' : 'h', $x);
223
224 4
        return $this;
225
    }
226
227
    /**
228
     * @param ElementInterface $parent
229
     * @param                  $x
230
     * @param                  $y
231
     * @param                  $y1
232
     * @param bool             $absolute
233
     *
234
     * @return ContainerInterface|Path
235
     */
236
    public static function vLine(ElementInterface $parent, $x, $y, $y1, $absolute = true)
237
    {
238
        return self::create($parent, $x, $y, $absolute)->vLineTo($y1, $absolute);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\container\ContainerInterface as the method vLineTo() does only exist in the following implementations of said interface: nstdio\svg\shape\Path.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
239
    }
240
241
    /**
242
     * Draws a vertical line from the current point (cpx, cpy) to (cpx, y). V (uppercase) indicates that absolute
243
     * coordinates will follow; v (lowercase) indicates that relative coordinates will follow. Multiple y values can be
244
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
245
     * (cpx, y) for the final value of y.
246
     *
247
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
248
     *
249
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
250
     *
251
     * @param bool  $absolute
252
     *
253
     * @return $this
254
     */
255 2
    public function vLineTo($y, $absolute = true)
256
    {
257 2
        $this->buildPath($absolute ? 'V' : 'v', $y);
258
259 2
        return $this;
260
    }
261
262
    /**
263
     * Draws a cubic Bézier curve from the current point to (x,y) using (x1,y1) as the control point at the beginning
264
     * of the curve and (x2,y2) as the control point at the end of the curve.
265
     *
266
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
267
     *
268
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
269
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
270
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
271
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
272
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
273
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
274
     *
275
     * @param bool  $absolute
276
     *
277
     * @return $this
278
     */
279 1
    public function curveTo($x1, $y1, $x2, $y2, $x, $y, $absolute = true)
280
    {
281 1
        $this->buildPath($absolute ? 'C' : 'c', $x1, $y1, $x2, $y2, $x, $y);
282
283 1
        return $this;
284
    }
285
286
    /**
287
     * Draws a cubic Bézier curve from the current point to (x,y). The first control point is assumed to be the
288
     * reflection of the second control point on the previous command relative to the current point. (If there is no
289
     * previous command or if the previous command was not an C, c, S or s, assume the first control point is
290
     * coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at the end of
291
     * the curve). S (uppercase) indicates that absolute coordinates will follow; s (lowercase) indicates that relative
292
     * coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the
293
     * command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
294
     *
295
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
296
     *
297
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
298
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
299
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
300
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
301
     *
302
     * @param bool  $absolute
303
     *
304
     * @return $this
305
     */
306 1
    public function smoothCurveTo($x2, $y2, $x, $y, $absolute = true)
307
    {
308 1
        $this->buildPath($absolute ? 'S' : 's', $x2, $y2, $x, $y);
309
310 1
        return $this;
311
    }
312
313
    /**
314
     * Draws a quadratic Bézier curve from the current point to (x,y). The control point is assumed to be the
315
     * reflection of the control point on the previous command relative to the current point. (If there is no previous
316
     * command or if the previous command was not a Q, q, T or t, assume the control point is coincident with the
317
     * current point.) T (uppercase) indicates that absolute coordinates will follow; t (lowercase) indicates that
318
     * relative coordinates will follow. At the end of the command, the new current point becomes the final (x,y)
319
     * coordinate pair used in the polybézier.
320
     *
321
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
322
     *
323
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
324
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
325
     *
326
     * @param bool  $absolute
327
     *
328
     * @return $this
329
     */
330 1
    public function smoothQuadraticCurveTo($x, $y, $absolute = true)
331
    {
332 1
        $this->buildPath($absolute ? 'T' : 't', $x, $y);
333
334 1
        return $this;
335
    }
336
337
    /**
338
     * Draws an elliptical arc from the current point to (x, y). The size and orientation of the ellipse are defined by
339
     * two radii (rx, ry) and an x-axis-rotation, which indicates how the ellipse as a whole is rotated relative to the
340
     * current coordinate system. The center (cx, cy) of the ellipse is calculated automatically to satisfy the
341
     * constraints imposed by the other parameters. large-arc-flag and sweep-flag contribute to the automatic
342
     * calculations and help determine how the arc is drawn.
343
     *
344
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataEllipticalArcCommands
345
     *
346
     * @param float   $rx           The x-axis radius for the ellipse (i.e., r1).
347
     * @param float   $ry           The y-axis radius for the ellipse
348
     * @param float   $xRotation    The rotation angle in degrees for the ellipse's x-axis relative to the x-axis of
349
     *                              the user coordinate system.
350
     * @param boolean $largeArcFlag The value of the large-arc-flag parameter.
351
     * @param boolean $sweepFlag    The value of the sweep-flag parameter.
352
     * @param float   $x            The absolute (relative) X coordinate for the end point of this path segment.
353
     * @param float   $y            The absolute (relative) Y coordinate for the end point of this path segment.
354
     * @param bool    $absolute
355
     *
356
     * @return $this
357
     */
358 4
    public function arcTo($rx, $ry, $xRotation, $largeArcFlag, $sweepFlag, $x, $y, $absolute = true)
359
    {
360 4
        $this->buildPath($absolute ? 'A' : 'a', [$rx, $ry], $xRotation, [$largeArcFlag ? 1 : 0, $sweepFlag ? 1 : 0], [$x, $y]);
361
362 4
        return $this;
363
    }
364
365
    /**
366
     * Close the current subpath by drawing a straight line from the current point to current subpath's initial point.
367
     * Since the Z and z commands take no parameters, they have an identical effect.
368
     *
369
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
370
     *
371
     * @param bool $absolute
372
     */
373 3
    public function closePath($absolute = true)
374
    {
375 3
        $this->buildPath($absolute ? 'Z' : 'z');
376 3
    }
377
378 10
    public function getName()
379
    {
380 10
        return 'path';
381
    }
382
383 1
    public function getBoundingBox()
384
    {
385 1
        return $this->boundingBox->getBox();
386
    }
387
388 1
    protected function getCenterX()
389
    {
390 1
        return $this->boundingBox->getBox()['width'] / 2;
391
    }
392
393 1
    protected function getCenterY()
394
    {
395 1
        return $this->boundingBox->getBox()['height'] / 2;
396
    }
397
}