Test Failed
Push — master ( b95750...93ce75 )
by Edgar
03:22
created

Path::hLine()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 5
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 11
    public function __construct(ElementInterface $parent, $x, $y, $absolute = true)
34
    {
35 11
        parent::__construct($parent);
36
37 11
        $this->boundingBox = new PathBounds();
38 11
        $this->moveTo($x, $y, $absolute);
39 11
    }
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 11
    public function moveTo($x, $y, $absolute = true)
59
    {
60 11
        $this->checkFirstModifier();
61
62 11
        $modifier = $absolute ? 'M' : 'm';
63 11
        $this->d = "$modifier $x, $y";
64
65 11
        $this->boundingBox->addData($modifier, [$x, $y]);
66
67 11
        return $this;
68
    }
69
70
    /**
71
     *
72
     */
73 11
    private function checkFirstModifier()
74
    {
75 11
        if ($this->d !== null) {
76 1
            throw new \BadMethodCallException("First modifier for path must be: M or m.");
77
        }
78 11
    }
79
80
    /**
81
     * @param ElementInterface $parent
82
     * @param                  $x0
83
     * @param                  $y0
84
     * @param                  $x1
85
     * @param                  $y1
86
     * @param                  $x
87
     * @param                  $y
88
     * @param bool             $absolute
89
     *
90
     * @return Path|ContainerInterface
91
     */
92
    public static function quadraticCurve(ElementInterface $parent, $x0, $y0, $x1, $y1, $x, $y, $absolute = true)
93
    {
94
        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...
95
            ->quadraticCurveTo($x1, $y1, $x, $y);
96
    }
97
98
    /**
99
     * Draws a quadratic Bézier curve from the current point to (x,y) using (x1,y1) as the control point. Q (uppercase)
100
     * indicates that absolute coordinates will follow; q (lowercase) indicates that relative coordinates will follow.
101
     * Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current
102
     * point becomes the final (x,y) coordinate pair used in the polybézier.
103
     *
104
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
105
     *
106
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
107
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
108
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
109
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
110
     *
111
     * @param bool  $absolute
112
     *
113
     * @return $this
114
     */
115 1
    public function quadraticCurveTo($x1, $y1, $x, $y, $absolute = true)
116
    {
117 1
        $this->buildPath($absolute ? 'Q' : 'q', $x1, $y1, $x, $y);
118
119 1
        return $this;
120
    }
121
122
    /**
123
     * @param string $type
124
     */
125 7
    private function buildPath($type)
126
    {
127 7
        $params = array_slice(func_get_args(), 1);
128 7
        $this->boundingBox->addData($type, $params);
129
130 7
        $this->d .= " $type";
131 7
        foreach ($params as $key => $value) {
132 7
            if (is_array($value)) {
133 4
                $this->addArrayToPath($value);
134 4
            } else {
135 7
                if ($key % 2 !== 0 && !is_array($params[$key - 1])) {
136 5
                    $this->d .= ", $value";
137 5
                } else {
138 7
                    $this->d .= " $value";
139
                }
140
            }
141 7
        }
142 7
    }
143
144
    /**
145
     * @param $value
146
     */
147 4
    private function addArrayToPath(array $value)
148
    {
149 4
        foreach ($value as $item) {
150 4
            $this->d .= " $item,";
151 4
        }
152 4
        $this->d = rtrim($this->d, ',');
153 4
    }
154
155
    /**
156
     * @param ElementInterface $parent
157
     * @param                  $x
158
     * @param                  $y
159
     * @param bool             $absolute
160
     *
161
     * @return Path|ContainerInterface
162
     */
163 1
    public static function create(ElementInterface $parent, $x, $y, $absolute = true)
164
    {
165 1
        return new Path($parent, $x, $y, $absolute);
166
    }
167
168
    /**
169
     * @param ElementInterface $parent
170
     * @param                  $x
171
     * @param                  $y
172
     * @param                  $x1
173
     * @param                  $y1
174
     * @param bool             $absolute
175
     *
176
     * @return ContainerInterface|Path
177
     */
178 1
    public static function line(ElementInterface $parent, $x, $y, $x1, $y1, $absolute = true)
179
    {
180 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...
181
    }
182
183
    /**
184
     * Draw a line from the current point to the given (x,y) coordinate which becomes the new current point. L
185
     * (uppercase) indicates that absolute coordinates will follow; l (lowercase) indicates that relative coordinates
186
     * will follow. A number of coordinates pairs may be specified to draw a polyline. At the end of the command, the
187
     * new current point is set to the final set of coordinates provided.
188
     *
189
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
190
     *
191
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
192
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
193
     * @param bool  $absolute
194
     *
195
     * @return $this
196
     */
197 5
    public function lineTo($x, $y, $absolute = true)
198
    {
199 5
        $this->buildPath($absolute ? 'L' : 'l', $x, $y);
200
201 5
        return $this;
202
    }
203
204
    /**
205
     * @param ElementInterface $parent
206
     * @param                  $x
207
     * @param                  $y
208
     * @param                  $x1
209
     * @param bool             $absolute
210
     *
211
     * @return ContainerInterface|Path
212
     */
213
    public static function hLine(ElementInterface $parent, $x, $y, $x1, $absolute = true)
214
    {
215
        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...
216
    }
217
218
    /**
219
     * Draws a horizontal line from the current point (cpx, cpy) to (x, cpy). H (uppercase) indicates that absolute
220
     * coordinates will follow; h (lowercase) indicates that relative coordinates will follow. Multiple x values can be
221
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
222
     * (x, cpy) for the final value of x.
223
     *
224
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
225
     *
226
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
227
     *
228
     * @param bool  $absolute
229
     *
230
     * @return $this
231
     */
232 4
    public function hLineTo($x, $absolute = true)
233
    {
234 4
        $this->buildPath($absolute ? 'H' : 'h', $x);
235
236 4
        return $this;
237
    }
238
239
    /**
240
     * @param ElementInterface $parent
241
     * @param                  $x
242
     * @param                  $y
243
     * @param                  $y1
244
     * @param bool             $absolute
245
     *
246
     * @return ContainerInterface|Path
247
     */
248
    public static function vLine(ElementInterface $parent, $x, $y, $y1, $absolute = true)
249
    {
250
        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...
251
    }
252
253
    /**
254
     * Draws a vertical line from the current point (cpx, cpy) to (cpx, y). V (uppercase) indicates that absolute
255
     * coordinates will follow; v (lowercase) indicates that relative coordinates will follow. Multiple y values can be
256
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
257
     * (cpx, y) for the final value of y.
258
     *
259
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
260
     *
261
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
262
     *
263
     * @param bool  $absolute
264
     *
265
     * @return $this
266
     */
267 2
    public function vLineTo($y, $absolute = true)
268
    {
269 2
        $this->buildPath($absolute ? 'V' : 'v', $y);
270
271 2
        return $this;
272
    }
273
274
    /**
275
     * Draws a cubic Bézier curve from the current point to (x,y) using (x1,y1) as the control point at the beginning
276
     * of the curve and (x2,y2) as the control point at the end of the curve.
277
     *
278
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
279
     *
280
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
281
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
282
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
283
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
284
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
285
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
286
     *
287
     * @param bool  $absolute
288
     *
289
     * @return $this
290
     */
291 1
    public function curveTo($x1, $y1, $x2, $y2, $x, $y, $absolute = true)
292
    {
293 1
        $this->buildPath($absolute ? 'C' : 'c', $x1, $y1, $x2, $y2, $x, $y);
294
295 1
        return $this;
296
    }
297
298
    /**
299
     * Draws a cubic Bézier curve from the current point to (x,y). The first control point is assumed to be the
300
     * reflection of the second control point on the previous command relative to the current point. (If there is no
301
     * previous command or if the previous command was not an C, c, S or s, assume the first control point is
302
     * coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at the end of
303
     * the curve). S (uppercase) indicates that absolute coordinates will follow; s (lowercase) indicates that relative
304
     * coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the
305
     * command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
306
     *
307
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
308
     *
309
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
310
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
311
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
312
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
313
     *
314
     * @param bool  $absolute
315
     *
316
     * @return $this
317
     */
318 1
    public function smoothCurveTo($x2, $y2, $x, $y, $absolute = true)
319
    {
320 1
        $this->buildPath($absolute ? 'S' : 's', $x2, $y2, $x, $y);
321
322 1
        return $this;
323
    }
324
325
    /**
326
     * Draws a quadratic Bézier curve from the current point to (x,y). The control point is assumed to be the
327
     * reflection of the control point on the previous command relative to the current point. (If there is no previous
328
     * command or if the previous command was not a Q, q, T or t, assume the control point is coincident with the
329
     * current point.) T (uppercase) indicates that absolute coordinates will follow; t (lowercase) indicates that
330
     * relative coordinates will follow. At the end of the command, the new current point becomes the final (x,y)
331
     * coordinate pair used in the polybézier.
332
     *
333
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
334
     *
335
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
336
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
337
     *
338
     * @param bool  $absolute
339
     *
340
     * @return $this
341
     */
342 1
    public function smoothQuadraticCurveTo($x, $y, $absolute = true)
343
    {
344 1
        $this->buildPath($absolute ? 'T' : 't', $x, $y);
345
346 1
        return $this;
347
    }
348
349
    /**
350
     * Draws an elliptical arc from the current point to (x, y). The size and orientation of the ellipse are defined by
351
     * two radii (rx, ry) and an x-axis-rotation, which indicates how the ellipse as a whole is rotated relative to the
352
     * current coordinate system. The center (cx, cy) of the ellipse is calculated automatically to satisfy the
353
     * constraints imposed by the other parameters. large-arc-flag and sweep-flag contribute to the automatic
354
     * calculations and help determine how the arc is drawn.
355
     *
356
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataEllipticalArcCommands
357
     *
358
     * @param float   $rx           The x-axis radius for the ellipse (i.e., r1).
359
     * @param float   $ry           The y-axis radius for the ellipse
360
     * @param float   $xRotation    The rotation angle in degrees for the ellipse's x-axis relative to the x-axis of
361
     *                              the user coordinate system.
362
     * @param boolean $largeArcFlag The value of the large-arc-flag parameter.
363
     * @param boolean $sweepFlag    The value of the sweep-flag parameter.
364
     * @param float   $x            The absolute (relative) X coordinate for the end point of this path segment.
365
     * @param float   $y            The absolute (relative) Y coordinate for the end point of this path segment.
366
     * @param bool    $absolute
367
     *
368
     * @return $this
369
     */
370 4
    public function arcTo($rx, $ry, $xRotation, $largeArcFlag, $sweepFlag, $x, $y, $absolute = true)
371
    {
372 4
        $this->buildPath($absolute ? 'A' : 'a', [$rx, $ry], $xRotation, [$largeArcFlag ? 1 : 0, $sweepFlag ? 1 : 0], [$x, $y]);
373
374 4
        return $this;
375
    }
376
377
    /**
378
     * Close the current subpath by drawing a straight line from the current point to current subpath's initial point.
379
     * Since the Z and z commands take no parameters, they have an identical effect.
380
     *
381
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
382
     *
383
     * @param bool $absolute
384
     */
385 3
    public function closePath($absolute = true)
386
    {
387 3
        $this->buildPath($absolute ? 'Z' : 'z');
388 3
    }
389
390 11
    public function getName()
391
    {
392 11
        return 'path';
393
    }
394
395 1
    public function getBoundingBox()
396
    {
397 1
        return $this->boundingBox->getBox();
398
    }
399
400 1
    protected function getCenterX()
401
    {
402 1
        return $this->boundingBox->getBox()['width'] / 2;
403
    }
404
405 1
    protected function getCenterY()
406
    {
407 1
        return $this->boundingBox->getBox()['height'] / 2;
408
    }
409
}