Passed
Push — master ( 372311...b093ab )
by Jordan
02:26
created

CartesianCoordinate::asCartesian()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Samsara\Fermat\Values\Geometry\CoordinateSystems;
4
5
use ReflectionException;
6
use Samsara\Exceptions\SystemError\LogicalError\IncompatibleObjectState;
7
use Samsara\Exceptions\UsageError\IntegrityConstraint;
8
use Samsara\Fermat\Numbers;
9
use Samsara\Fermat\Types\Base\Interfaces\Coordinates\CoordinateInterface;
10
use Samsara\Fermat\Types\Base\Interfaces\Coordinates\ThreeDCoordinateInterface;
11
use Samsara\Fermat\Types\Base\Interfaces\Coordinates\TwoDCoordinateInterface;
12
use Samsara\Fermat\Types\Coordinate;
13
use Samsara\Fermat\Values\ImmutableDecimal;
14
15
class CartesianCoordinate extends Coordinate implements TwoDCoordinateInterface, ThreeDCoordinateInterface
16
{
17
18
    /**
19
     * CartesianCoordinate constructor.
20
     *
21
     * @param $x
22
     * @param null $y
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $y is correct as it would always require null to be passed?
Loading history...
23
     * @param null $z
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $z is correct as it would always require null to be passed?
Loading history...
24
     */
25 13
    public function __construct($x, $y = null, $z = null)
26
    {
27 13
        $data = [
28 13
            'x' => $x
29
        ];
30
31 13
        if (!is_null($y)) {
0 ignored issues
show
introduced by
The condition is_null($y) is always true.
Loading history...
32 13
            $data['y'] = $y;
33 13
            if (!is_null($z)) {
34 13
                $data['z'] = $z;
35
            }
36 4
        } else if (!is_null($z)) {
0 ignored issues
show
introduced by
The condition is_null($z) is always true.
Loading history...
37 1
            $data['y'] = $z;
38
        }
39
40 13
        parent::__construct($data);
41
42 13
        if (!array_key_exists('y', $this->parameters)) {
43 4
            $this->parameters['y'] = 1;
44
        }
45
46 13
        if (!array_key_exists('z', $this->parameters)) {
47 10
            $this->parameters['z'] = 2;
48
        }
49 13
    }
50
51
    /**
52
     * @param $axis
53
     *
54
     * @return ImmutableDecimal
55
     * @throws ReflectionException
56
     * @throws IntegrityConstraint
57
     */
58 6
    public function getAxis($axis): ImmutableDecimal
59
    {
60 6
        if (is_int($axis)) {
61 1
            $axisIndex = $axis;
62
        } else {
63 6
            $axisIndex = $this->parameters[$axis];
64
        }
65
66 6
        if (!$this->values->hasIndex($axisIndex)) {
67 1
            return Numbers::makeZero();
68
        }
69
70 6
        return $this->getAxisByIndex($axisIndex);
71
    }
72
73
    /**
74
     * @return ImmutableDecimal
75
     */
76 6
    public function getDistanceFromOrigin(): ImmutableDecimal
77
    {
78 6
        $x = 0;
79
80 6
        if ($this->numberOfDimensions() > 1) {
81 6
            $y = 0;
82
        } else {
83 1
            $y = null;
84
        }
85
86 6
        if ($this->numberOfDimensions() > 2) {
87 4
            $z = 0;
88
        } else {
89 5
            $z = null;
90
        }
91
92 6
        return $this->distanceTo(new CartesianCoordinate($x, $y, $z));
0 ignored issues
show
Bug introduced by
It seems like $y can also be of type integer; however, parameter $y of Samsara\Fermat\Values\Ge...ordinate::__construct() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

92
        return $this->distanceTo(new CartesianCoordinate($x, /** @scrutinizer ignore-type */ $y, $z));
Loading history...
Bug introduced by
It seems like $z can also be of type integer; however, parameter $z of Samsara\Fermat\Values\Ge...ordinate::__construct() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

92
        return $this->distanceTo(new CartesianCoordinate($x, $y, /** @scrutinizer ignore-type */ $z));
Loading history...
93
    }
94
95
    /**
96
     * @param CoordinateInterface $coordinate
97
     *
98
     * @return ImmutableDecimal
99
     * @throws IntegrityConstraint
100
     * @throws ReflectionException
101
     */
102 10
    public function distanceTo(CoordinateInterface $coordinate): ImmutableDecimal
103
    {
104 10
        if (!($coordinate instanceof CartesianCoordinate)) {
105 3
            $coordinate = $coordinate->asCartesian();
106
        }
107
108
        /** @var ImmutableDecimal $n */
109 10
        $n = Numbers::makeZero();
110
111 10
        $firstValues = ($this->numberOfDimensions() >= $coordinate->numberOfDimensions()) ? $this->axesValues() : $coordinate->axesValues();
112 10
        $secondValues = ($this->numberOfDimensions() >= $coordinate->numberOfDimensions()) ? $coordinate->axesValues() : $this->axesValues();
113
114 10
        foreach ($firstValues as $index => $value) {
115 10
            $n = $n->add($secondValues[$index]->subtract($value)->pow(2));
116
        }
117
118 10
        $n = $n->sqrt();
119
120 10
        return $n;
121
    }
122
123
    /**
124
     * @return CartesianCoordinate
125
     */
126 1
    public function asCartesian(): CartesianCoordinate
127
    {
128 1
        return $this;
129
    }
130
131
    /**
132
     * @return ImmutableDecimal
133
     * @throws IncompatibleObjectState
134
     */
135 2
    public function getPolarAngle(): ImmutableDecimal
136
    {
137 2
        if ($this->numberOfDimensions() === 2) {
138 2
            return $this->asPolar()->getPolarAngle();
139
        }
140
141 1
        if ($this->numberOfDimensions() === 3) {
142 1
            return $this->asSpherical()->getPolarAngle();
143
        }
144
145 1
        throw new IncompatibleObjectState(
146 1
            'Can only get a polar angle for a CartesianCoordinate of 2 or 3 dimensions.',
147 1
            'Ensure the CartesianCoordinate has 2 or 3 dimensions.',
148 1
            'Cannot get the polar angle for a CartesianCoordinate unless it has exactly 2 or 3 dimensions.'
149
        );
150
    }
151
152
    /**
153
     * @return ImmutableDecimal
154
     * @throws IncompatibleObjectState
155
     */
156 1
    public function getPlanarAngle(): ImmutableDecimal
157
    {
158 1
        if ($this->numberOfDimensions() === 2) {
159 1
            return $this->getPolarAngle();
160
        }
161
162 1
        if ($this->numberOfDimensions() === 3) {
163 1
            return $this->asSpherical()->getPlanarAngle();
164
        }
165
166 1
        throw new IncompatibleObjectState(
167 1
            'Can only get a polar angle for a CartesianCoordinate of 2 or 3 dimensions.',
168 1
            'Ensure the CartesianCoordinate has 2 or 3 dimensions.',
169 1
            'Cannot get the polar angle for a CartesianCoordinate unless it has exactly 2 or 3 dimensions.'
170
        );
171
    }
172
173
    /**
174
     * @return SphericalCoordinate
175
     * @throws IncompatibleObjectState
176
     */
177 3
    public function asSpherical(): SphericalCoordinate
178
    {
179 3
        if ($this->numberOfDimensions() !== 3) {
180 1
            throw new IncompatibleObjectState(
181 1
                'Can only get SphericalCoordinate for a CartesianCoordinate of 3 dimensions.',
182 1
                'Ensure the CartesianCoordinate has 3 dimensions.',
183 1
                'Cannot get the SphericalCoordinate for a CartesianCoordinate unless it has exactly 3 dimensions.'
184
            );
185
        }
186
187 3
        $rho = $this->getDistanceFromOrigin();
188 3
        $theta = $this->getAxis('y')->divide($this->getAxis('x'))->arctan();
189 3
        $phi = $this->getAxis('z')->divide($rho)->arccos();
190
191 3
        return new SphericalCoordinate($rho, $theta, $phi);
192
    }
193
194
    /**
195
     * @return CylindricalCoordinate
196
     * @throws IncompatibleObjectState
197
     * @throws IntegrityConstraint
198
     * @throws ReflectionException
199
     */
200 1
    public function asCylindrical(): CylindricalCoordinate
201
    {
202 1
        if ($this->numberOfDimensions() !== 3) {
203 1
            throw new IncompatibleObjectState(
204 1
                'Can only get CylindricalCoordinate for a CartesianCoordinate of 3 dimensions.',
205 1
                'Ensure the CartesianCoordinate has 3 dimensions.',
206 1
                'Cannot get the CylindricalCoordinate for a CartesianCoordinate unless it has exactly 3 dimensions.'
207
            );
208
        }
209
210 1
        $z = $this->getAxis('z');
211 1
        $r = $this->distanceTo(new CartesianCoordinate(0, 0, $z));
212 1
        $theta = $this->getAxis('y')->divide($this->getAxis('x'))->arctan();
213
214 1
        return new CylindricalCoordinate($r, $theta, $z);
215
    }
216
217
    /**
218
     * @return PolarCoordinate
219
     * @throws IncompatibleObjectState
220
     */
221 5
    public function asPolar(): PolarCoordinate
222
    {
223 5
        if ($this->numberOfDimensions() !== 2) {
224 1
            throw new IncompatibleObjectState(
225 1
                'Can only get PolarCoordinate for a CartesianCoordinate of 2 dimensions.',
226 1
                'Ensure the CartesianCoordinate has 2 dimensions.',
227 1
                'Cannot get the PolarCoordinate for a CartesianCoordinate unless it has exactly 2 dimensions.'
228
            );
229
        }
230
231 4
        $rho = $this->getDistanceFromOrigin();
232
233 4
        if ($rho->isEqual(0)) {
234 1
            throw new IncompatibleObjectState(
235 1
                'Attempted to convert a CartesianCoordinate at the origin into PolarCoordinate',
236 1
                'Do not attempt to do this.',
237 1
                'The origin has an undefined polar angle in the polar coordinate system.'
238
            );
239
        }
240
241
        /** @var ImmutableDecimal $theta */
242 3
        $theta = $this->getAxis('x')->divide($rho)->arccos();
243 3
        if ($this->getAxis('y')->isNegative()) {
244
            $theta = $theta->multiply(-1);
245
        }
246
247 3
        return new PolarCoordinate($rho, $theta);
248
    }
249
}