CartesianCoordinate::asSpherical()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

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

96
        return $this->distanceTo(new CartesianCoordinate($x, $y, /** @scrutinizer ignore-type */ $z), $intScale)->roundToScale($scale);
Loading history...
Bug introduced by
It seems like $y can also be of type integer; however, parameter $y of Samsara\Fermat\Coordinat...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

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