Path::hLine()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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