From fc0d744bc5ac8725fce235e00db555706cb91bdb Mon Sep 17 00:00:00 2001 From: Mirko Janssen Date: Sat, 21 Sep 2019 20:36:29 +0200 Subject: [PATCH] refactored Tuple and created class for Point and Vector --- src/Primitives/Point.php | 60 +++++ src/Primitives/Tuple.php | 154 +------------ src/Primitives/Vector.php | 111 +++++++++ src/virtual_cannon.php | 23 +- tests/Primitives/PointTest.php | 93 ++++++++ tests/Primitives/TupleTest.php | 389 ++------------------------------ tests/Primitives/VectorTest.php | 215 ++++++++++++++++++ 7 files changed, 508 insertions(+), 537 deletions(-) create mode 100644 src/Primitives/Point.php create mode 100644 src/Primitives/Vector.php create mode 100644 tests/Primitives/PointTest.php create mode 100644 tests/Primitives/VectorTest.php diff --git a/src/Primitives/Point.php b/src/Primitives/Point.php new file mode 100644 index 0000000..50c69ef --- /dev/null +++ b/src/Primitives/Point.php @@ -0,0 +1,60 @@ +x + $vector->x(), $this->y + $vector->y(), $this->z + $vector->z()); + } + + + /** + * @param Vector $vector + * + * @return Point + */ + public function minusVector(Vector $vector): Point + { + return new Point($this->x - $vector->x(), $this->y - $vector->y(), $this->z - $vector->z()); + } + + + /** + * @param Point $point + * + * @return Vector + */ + public function minusPoint(Point $point): Vector + { + return new Vector($this->x - $point->x(), $this->y - $point->y(), $this->z - $point->z()); + } + + +} \ No newline at end of file diff --git a/src/Primitives/Tuple.php b/src/Primitives/Tuple.php index a1c98ea..fe500b5 100644 --- a/src/Primitives/Tuple.php +++ b/src/Primitives/Tuple.php @@ -5,9 +5,6 @@ declare(strict_types=1); namespace ItsAMirko\RayTracer\Primitives; use InvalidArgumentException; -use RuntimeException; -use function pow; -use function sqrt; /** * Class Tuple @@ -31,11 +28,6 @@ class Tuple */ protected $z; - /** - * @var float - */ - protected $w; - /** * Tuple constructor. @@ -43,40 +35,12 @@ class Tuple * @param float $x * @param float $y * @param float $z - * @param float $w */ - public function __construct(float $x, float $y, float $z, float $w) + public function __construct(float $x, float $y, float $z) { $this->x = $x; $this->y = $y; $this->z = $z; - $this->w = $w; - } - - - /** - * @param float $x - * @param float $y - * @param float $z - * - * @return Tuple - */ - public static function createPoint(float $x, float $y, float $z): Tuple - { - return new self($x, $y, $z, 1.0); - } - - - /** - * @param float $x - * @param float $y - * @param float $z - * - * @return Tuple - */ - public static function createVector(float $x, float $y, float $z): Tuple - { - return new self($x, $y, $z, 0.0); } @@ -107,69 +71,12 @@ class Tuple } - /** - * @return float - */ - public function w(): float - { - return $this->w; - } - - - /** - * @return bool - */ - public function isPoint(): bool - { - return $this->w === 1.0; - } - - - /** - * @return bool - */ - public function isVector(): bool - { - return $this->w === 0.0; - } - - - /** - * @param Tuple $other - * - * @return Tuple - */ - public function plus(Tuple $other): Tuple - { - if ($this->isPoint() && $other->isPoint()) { - throw new InvalidArgumentException('Two point can not be added together'); - } - - return new self($this->x + $other->x(), $this->y + $other->y(), $this->z + $other->z(), $this->w + $other->w()); - } - - - /** - * @param Tuple $other - * - * @return Tuple - */ - public function minus(Tuple $other): Tuple - { - if ($this->isVector() && $other->isPoint()) { - throw new InvalidArgumentException('Can not subtract a point from a vector'); - } - - return new self($this->x - $other->x(), $this->y - $other->y(), $this->z - $other->z(), $this->w - $other->w()); - } - - /** * @return Tuple */ public function negate(): Tuple { - return new self(-1 * $this->x, -1 * $this->y, -1 * $this->z, -1 * $this->w); + return new static(-1 * $this->x, -1 * $this->y, -1 * $this->z); } @@ -180,7 +87,7 @@ class Tuple */ public function multiplyWith(float $multiplier): Tuple { - return new self($multiplier * $this->x, $multiplier * $this->y, $multiplier * $this->z, $multiplier * $this->w); + return new static($multiplier * $this->x, $multiplier * $this->y, $multiplier * $this->z); } @@ -195,59 +102,6 @@ class Tuple throw new InvalidArgumentException('Can not divided with zero'); } - return new self($this->x / $divider, $this->y / $divider, $this->z / $divider, $this->w / $divider); - } - - - /** - * @return float - */ - public function magnitude(): float - { - if ($this->isVector() === false) { - throw new RuntimeException('This tuple needs to be a vector'); - } - - return sqrt(pow($this->x, 2) + pow($this->y, 2) + pow($this->z, 2)); - } - - - /** - * @return Tuple - */ - public function normalize(): Tuple - { - return $this->divideWith($this->magnitude()); - } - - - /** - * @param Tuple $other - * - * @return float - */ - public function dot(Tuple $other): float - { - if ($this->isVector() === false || $other->isVector() === false) { - throw new RuntimeException('Dot product can only be build for vectors'); - } - - return $this->x * $other->x() + $this->y * $other->y() + $this->z * $other->z; - } - - - /** - * @param Tuple $other - * - * @return Tuple - */ - public function cross(Tuple $other): Tuple - { - if ($this->isVector() === false || $other->isVector() === false) { - throw new RuntimeException('Cross product can only be build for vectors'); - } - - return new self($this->y * $other->z() - $this->z * $other->y, $this->z * $other->x() - $this->x * $other->z, - $this->x * $other->y() - $this->y * $other->x, 0.0); + return new static($this->x / $divider, $this->y / $divider, $this->z / $divider); } } \ No newline at end of file diff --git a/src/Primitives/Vector.php b/src/Primitives/Vector.php new file mode 100644 index 0000000..6bce971 --- /dev/null +++ b/src/Primitives/Vector.php @@ -0,0 +1,111 @@ +x + $vector->x(), $this->y + $vector->y(), $this->z + $vector->z()); + } + + + /** + * @param Point $vector + * + * @return Point + */ + public function plusPoint(Point $vector): Point + { + return new Point($this->x + $vector->x(), $this->y + $vector->y(), $this->z + $vector->z()); + } + + + /** + * @param Vector $vector + * + * @return Vector + */ + public function minusVector(Vector $vector): Vector + { + return new Vector($this->x - $vector->x(), $this->y - $vector->y(), $this->z - $vector->z()); + } + + + /** + * @param Point $point + * + * @return Point + */ + public function minusPoint(Point $point): Point + { + return new Point($this->x - $point->x(), $this->y - $point->y(), $this->z - $point->z()); + } + + + /** + * @return float + */ + public function magnitude(): float + { + return sqrt(pow($this->x, 2) + pow($this->y, 2) + pow($this->z, 2)); + } + + + /** + * @return Tuple + */ + public function normalize(): Tuple + { + return $this->divideWith($this->magnitude()); + } + + + /** + * @param Vector $vector + * + * @return float + */ + public function dot(Vector $vector): float + { + return $this->x * $vector->x() + $this->y * $vector->y() + $this->z * $vector->z; + } + + + /** + * @param Vector $vector + * + * @return Vector + */ + public function cross(Vector $vector): Vector + { + return new Vector($this->y * $vector->z() - $this->z * $vector->y, + $this->z * $vector->x() - $this->x * $vector->z, $this->x * $vector->y() - $this->y * $vector->x); + } + +} \ No newline at end of file diff --git a/src/virtual_cannon.php b/src/virtual_cannon.php index 9ae25ea..42a14c5 100644 --- a/src/virtual_cannon.php +++ b/src/virtual_cannon.php @@ -4,7 +4,8 @@ declare(strict_types=1); include __DIR__ . '/../vendor/autoload.php'; -use ItsAMirko\RayTracer\Primitives\Tuple; +use ItsAMirko\RayTracer\Primitives\Point; +use ItsAMirko\RayTracer\Primitives\Vector; /** * Class Projectile @@ -12,12 +13,12 @@ use ItsAMirko\RayTracer\Primitives\Tuple; class Projectile { /** - * @var Tuple + * @var Point */ public $position; /** - * @var Tuple + * @var Vector */ public $velocity; } @@ -28,12 +29,12 @@ class Projectile class Environment { /** - * @var Tuple + * @var Vector */ public $gravity; /** - * @var Tuple + * @var Vector */ public $wind; } @@ -41,19 +42,19 @@ class Environment function tick(Environment $environment, Projectile $projectile): Projectile { - $projectile->position = $projectile->position->plus($projectile->velocity); - $projectile->velocity = $projectile->velocity->plus($environment->gravity)->plus($environment->wind); + $projectile->position = $projectile->position->plusVector($projectile->velocity); + $projectile->velocity = $projectile->velocity->plusVector($environment->gravity)->plusVector($environment->wind); return $projectile; } $environment = new Environment(); -$environment->gravity = Tuple::createVector(0.0, -0.1, 0.0); -$environment->wind = Tuple::createVector(-0.01, 0.0, 0.0); +$environment->gravity = new Vector(0.0, -0.1, 0.0); +$environment->wind = new Vector(-0.01, 0.0, 0.0); $projectile = new Projectile(); -$projectile->position = Tuple::createPoint(0.0, 1.0, 0.0); -$projectile->velocity = Tuple::createVector(1.0, 1.0, 0.0)->normalize(); +$projectile->position = new Point(0.0, 1.0, 0.0); +$projectile->velocity = (new Vector(1.0, 1.0, 0.0))->normalize(); while ($projectile->position->y() > 0) { echo $projectile->position->x() . ' | ' . $projectile->position->y() . PHP_EOL; diff --git a/tests/Primitives/PointTest.php b/tests/Primitives/PointTest.php new file mode 100644 index 0000000..b14b7a5 --- /dev/null +++ b/tests/Primitives/PointTest.php @@ -0,0 +1,93 @@ +x = random_int(100, 10000) / 100; + $this->y = random_int(100, 10000) / 100; + $this->z = random_int(100, 10000) / 100; + } + + + public function testCanBeCreatedAsTuple(): void + { + $point = new Point($this->x, $this->y, $this->z); + + $this->assertInstanceOf(Tuple::class, $point); + $this->assertSame($this->x, $point->x()); + $this->assertSame($this->y, $point->y()); + $this->assertSame($this->z, $point->z()); + } + + + public function testCreatesANewPointByAddingAVector(): void + { + $point = new Point(1.0, 2.0, 3.0); + $vector = new Vector($this->x, $this->y, $this->z); + + $actualPoint = $point->plusVector($vector); + + $this->assertInstanceOf(Point::class, $actualPoint); + $this->assertSame(1 + $this->x, $actualPoint->x()); + $this->assertSame(2 + $this->y, $actualPoint->y()); + $this->assertSame(3 + $this->z, $actualPoint->z()); + } + + + public function testCreatesANewPointBySubtractingAVector(): void + { + $point = new Point(1.0, 2.0, 3.0); + $vector = new Vector($this->x, $this->y, $this->z); + + $actualPoint = $point->minusVector($vector); + + $this->assertInstanceOf(Point::class, $actualPoint); + $this->assertSame(1 - $this->x, $actualPoint->x()); + $this->assertSame(2 - $this->y, $actualPoint->y()); + $this->assertSame(3 - $this->z, $actualPoint->z()); + } + + + public function testCreatesANewVectorBySubtractingAPoint(): void + { + $point1 = new Point(1.0, 2.0, 3.0); + $point2 = new Point($this->x, $this->y, $this->z); + + $actualVector = $point1->minusPoint($point2); + + $this->assertInstanceOf(Vector::class, $actualVector); + $this->assertSame(1 - $this->x, $actualVector->x()); + $this->assertSame(2 - $this->y, $actualVector->y()); + $this->assertSame(3 - $this->z, $actualVector->z()); + } +} diff --git a/tests/Primitives/TupleTest.php b/tests/Primitives/TupleTest.php index 5056717..8b491e9 100644 --- a/tests/Primitives/TupleTest.php +++ b/tests/Primitives/TupleTest.php @@ -4,252 +4,33 @@ declare(strict_types=1); namespace ItsAMirko\RayTracer\Primitives; -use InvalidArgumentException; use PHPUnit\Framework\TestCase; -use RuntimeException; -use function sqrt; class TupleTest extends TestCase { - /** - * @testdox A tuple with w=1.0 is a point - */ - public function testCanBeAPoint(): void + public function testProvidedCoordinates(): void { - $tuple = new Tuple(4.3, -4.2, 3.1, 1.0); + $tuple = new Tuple(4.3, -4.2, 3.1); $this->assertSame(4.3, $tuple->x()); $this->assertSame(-4.2, $tuple->y()); $this->assertSame(3.1, $tuple->z()); - $this->assertSame(1.0, $tuple->w()); - $this->assertTrue($tuple->isPoint()); - $this->assertFalse($tuple->isVector()); } - /** - * @testdox A tuple with w=0.0 is a vector - */ - public function testCanBeAVector(): void + public function testCanBeNegated(): void { - $tuple = new Tuple(4.3, -4.2, 3.1, 0.0); + $tuple = new Tuple(3.0, -2.0, 5.0); - $this->assertSame(4.3, $tuple->x()); - $this->assertSame(-4.2, $tuple->y()); - $this->assertSame(3.1, $tuple->z()); - $this->assertSame(0.0, $tuple->w()); - $this->assertFalse($tuple->isPoint()); - $this->assertTrue($tuple->isVector()); - } - - - /** - * @testdox creates tuples with w=1 - */ - public function testCanBeCreatedAsPoint(): void - { - $point = Tuple::createPoint(4.0, -4.0, 3.0); + $negatedTuple = $tuple->negate(); - $this->assertInstanceOf(Tuple::class, $point); - $this->assertSame(4.0, $point->x()); - $this->assertSame(-4.0, $point->y()); - $this->assertSame(3.0, $point->z()); - $this->assertSame(1.0, $point->w()); + $this->assertSame(-3.0, $negatedTuple->x()); + $this->assertSame(2.0, $negatedTuple->y()); + $this->assertSame(-5.0, $negatedTuple->z()); } /** - * @testdox creates tuples with w=0 - */ - public function testCanBeCreatedAsVector(): void - { - $vector = Tuple::createVector(4.0, -4.0, 3.0); - - $this->assertInstanceOf(Tuple::class, $vector); - $this->assertSame(4.0, $vector->x()); - $this->assertSame(-4.0, $vector->y()); - $this->assertSame(3.0, $vector->z()); - $this->assertSame(0.0, $vector->w()); - } - - - /** - * @testdox Adding two tuples - * @dataProvider parametersForTupleAddition - * - * @param Tuple $tuple1 - * @param Tuple $tuple2 - * @param Tuple $expectedTuple - */ - public function testCanBeAddedTogether(Tuple $tuple1, Tuple $tuple2, Tuple $expectedTuple): void - { - $actualTuple = $tuple1->plus($tuple2); - - $this->assertSame($expectedTuple->x(), $actualTuple->x()); - $this->assertSame($expectedTuple->y(), $actualTuple->y()); - $this->assertSame($expectedTuple->z(), $actualTuple->z()); - $this->assertSame($expectedTuple->w(), $actualTuple->w()); - } - - - /** - * @return array - */ - public function parametersForTupleAddition(): array - { - return [ - [ - new Tuple(3.0, -2.0, 5.0, 1.0), - new Tuple(-2.0, 3.0, 1.0, 0.0), - new Tuple(1.0, 1.0, 6.0, 1.0), - ], - [ - new Tuple(-2.0, 3.0, 1.0, 0.0), - new Tuple(3.0, -2.0, 5.0, 1.0), - new Tuple(1.0, 1.0, 6.0, 1.0), - ], - [ - new Tuple(1.1, 2.2, 3.3, 0.0), - new Tuple(0.9, 0.8, 0.7, 0.0), - new Tuple(2.0, 3.0, 4.0, 0.0), - ], - [ - new Tuple(0.9, 0.8, 0.7, 0.0), - new Tuple(1.1, 2.2, 3.3, 0.0), - new Tuple(2.0, 3.0, 4.0, 0.0), - ], - ]; - } - - - /** - * @testdox Throw exception if two point should be added together - */ - public function testCanNotAddPointToAnotherPoint(): void - { - $this->expectException(InvalidArgumentException::class); - - $point1 = new Tuple(1.0, 1.0, 1.0, 1.0); - $point2 = new Tuple(1.0, 1.0, 1.0, 1.0); - - $point1->plus($point2); - } - - - /** - * @testdox Subtracting two tuples - * @dataProvider parametersForTupleSubtraction - * - * @param Tuple $tuple1 - * @param Tuple $tuple2 - * @param Tuple $expectedTuple - */ - public function testCanBeSubtractedFromEachOther(Tuple $tuple1, Tuple $tuple2, Tuple $expectedTuple): void - { - $actualTuple = $tuple1->minus($tuple2); - - $this->assertSame($expectedTuple->x(), $actualTuple->x()); - $this->assertSame($expectedTuple->y(), $actualTuple->y()); - $this->assertSame($expectedTuple->z(), $actualTuple->z()); - $this->assertSame($expectedTuple->w(), $actualTuple->w()); - } - - - /** - * @return array - */ - public function parametersForTupleSubtraction(): array - { - return [ - [ - new Tuple(3.0, -2.0, 5.0, 1.0), - new Tuple(1.0, 1.0, 6.0, 1.0), - new Tuple(2.0, -3.0, -1.0, 0.0), - ], - [ - new Tuple(1.0, 1.0, 6.0, 1.0), - new Tuple(3.0, -2.0, 5.0, 1.0), - new Tuple(-2.0, 3.0, 1.0, 0.0), - ], - [ - new Tuple(1.1, 2.2, 3.3, 1.0), - new Tuple(2.0, 3.0, 4.0, 0.0), - new Tuple(-0.9, -0.8, -0.7, 1.0), - ], - [ - new Tuple(0.0, 0.0, 0.0, 0.0), - new Tuple(2.0, 3.0, 4.0, 0.0), - new Tuple(-2.0, -3.0, -4.0, 0.0), - ], - [ - new Tuple(2.0, 3.0, 4.0, 0.0), - new Tuple(0.9, 0.8, 0.7, 0.0), - new Tuple(1.1, 2.2, 3.3, 0.0), - ], - ]; - } - - - /** - * @testdox Throw exception if a point should be subtracted from a vector - */ - public function testCanNotSubtractPointFromAVector(): void - { - $this->expectException(InvalidArgumentException::class); - - $point1 = new Tuple(1.0, 1.0, 1.0, 0.0); - $point2 = new Tuple(1.0, 1.0, 1.0, 1.0); - - $point1->minus($point2); - } - - - /** - * @testdox Negating a tuple - * @dataProvider parametersForTupleNegation - * - * @param Tuple $tuple1 - * @param Tuple $expectedTuple - */ - public function testCanBeNegated(Tuple $tuple1, Tuple $expectedTuple): void - { - $actualTuple = $tuple1->negate(); - - $this->assertSame($expectedTuple->x(), $actualTuple->x()); - $this->assertSame($expectedTuple->y(), $actualTuple->y()); - $this->assertSame($expectedTuple->z(), $actualTuple->z()); - $this->assertSame($expectedTuple->w(), $actualTuple->w()); - } - - - /** - * @return array - */ - public function parametersForTupleNegation(): array - { - return [ - [ - new Tuple(3.0, -2.0, 5.0, 1.0), - new Tuple(-3.0, 2.0, -5.0, -1.0), - ], - [ - new Tuple(-3.0, 2.0, -5.0, -1.0), - new Tuple(3.0, -2.0, 5.0, 1.0), - ], - [ - new Tuple(1.1, 2.2, 3.3, 0.0), - new Tuple(-1.1, -2.2, -3.3, 0.0), - ], - [ - new Tuple(-1.1, -2.2, -3.3, 0.0), - new Tuple(1.1, 2.2, 3.3, 0.0), - ], - ]; - } - - - /** - * @testdox Multiplying a tuple * @dataProvider parametersForTupleMultiplication * * @param Tuple $tuple1 @@ -263,7 +44,6 @@ class TupleTest extends TestCase $this->assertSame($expectedTuple->x(), $actualTuple->x()); $this->assertSame($expectedTuple->y(), $actualTuple->y()); $this->assertSame($expectedTuple->z(), $actualTuple->z()); - $this->assertSame($expectedTuple->w(), $actualTuple->w()); } @@ -273,27 +53,14 @@ class TupleTest extends TestCase public function parametersForTupleMultiplication(): array { return [ - [ - new Tuple(1.0, -2.0, 3.0, -4.0), - 3.5, - new Tuple(3.5, -7.0, 10.5, -14), - ], - [ - new Tuple(1.0, -2.0, 3.0, -4.0), - 0.5, - new Tuple(0.5, -1.0, 1.5, -2.0), - ], - [ - new Tuple(1.0, -2.0, 3.0, -4.0), - -1, - new Tuple(-1.0, 2.0, -3.0, 4.0), - ], + [new Tuple(1.0, -2.0, 3.0), 3.5, new Tuple(3.5, -7.0, 10.5)], + [new Tuple(1.0, -2.0, 3.0), 0.5, new Tuple(0.5, -1.0, 1.5)], + [new Tuple(1.0, -2.0, 3.0), -1, new Tuple(-1.0, 2.0, -3.0)], ]; } /** - * @testdox Dividing a tuple * @dataProvider parametersForTupleDivision * * @param Tuple $tuple1 @@ -307,7 +74,6 @@ class TupleTest extends TestCase $this->assertSame($expectedTuple->x(), $actualTuple->x()); $this->assertSame($expectedTuple->y(), $actualTuple->y()); $this->assertSame($expectedTuple->z(), $actualTuple->z()); - $this->assertSame($expectedTuple->w(), $actualTuple->w()); } @@ -317,138 +83,9 @@ class TupleTest extends TestCase public function parametersForTupleDivision(): array { return [ - [ - new Tuple(1.0, -2.0, 3.0, -4.0), - 2, - new Tuple(0.5, -1.0, 1.5, -2.0), - ], - [ - new Tuple(1.0, -2.0, 3.0, -4.0), - 0.5, - new Tuple(2.0, -4.0, 6.0, -8.0), - ], + [new Tuple(1.0, -2.0, 3.0), 2, new Tuple(0.5, -1.0, 1.5)], + [new Tuple(1.0, -2.0, 3.0), 0.5, new Tuple(2.0, -4.0, 6.0)], ]; } - - /** - * @testdox Computing the magnitude of a vector - */ - public function testHasAMagnitude(): void - { - $this->assertSame(1.0, (new Tuple(1.0, 0.0, 0.0, 0.0))->magnitude()); - $this->assertSame(1.0, (new Tuple(0.0, 1.0, 0.0, 0.0))->magnitude()); - $this->assertSame(1.0, (new Tuple(0.0, 0.0, 1.0, 0.0))->magnitude()); - $this->assertSame(sqrt(14), (new Tuple(1.0, 2.0, 3.0, 0.0))->magnitude()); - $this->assertSame(sqrt(14), (new Tuple(-1.0, -2.0, -3.0, 0.0))->magnitude()); - } - - - /** - * @testdox Throws an exception if the magnitude should be computed for a point - */ - public function testCanNotComputeMagnitudeForAPoint(): void - { - $this->expectException(RuntimeException::class); - - Tuple::createPoint(1.0, 0.0, 0.0)->magnitude(); - } - - - /** - * @testdox Normalizing vector - * @dataProvider parametersForVectorNormalization - * - * @param Tuple $vector - * @param Tuple $expectedVector - */ - public function testCanBeNormalized(Tuple $vector, Tuple $expectedVector): void - { - $normalizedVector = $vector->normalize(); - - $this->assertSame($expectedVector->x(), $normalizedVector->x()); - $this->assertSame($expectedVector->y(), $normalizedVector->y()); - $this->assertSame($expectedVector->z(), $normalizedVector->z()); - $this->assertSame(1.0, $normalizedVector->magnitude()); - } - - - /** - * @return array - */ - public function parametersForVectorNormalization(): array - { - return [ - [ - Tuple::createVector(4.0, 0.0, 0.0), - Tuple::createVector(1.0, 0.0, 0.0), - ], - [ - Tuple::createVector(1.0, 2.0, 3.0), - Tuple::createVector(1 / sqrt(14), 2 / sqrt(14), 3 / sqrt(14)), - ], - ]; - } - - - /** - * @testdox Throws an exception if a point should be normalized - */ - public function testCanNotNormalizeAPoint(): void - { - $this->expectException(RuntimeException::class); - - Tuple::createPoint(1.0, 0.0, 0.0)->normalize(); - } - - - /** - * @testdox The dot product of two vectors - */ - public function testCanBuildDotProduct(): void - { - $vector1 = new Tuple(1.0, 2.0, 3.0, 0.0); - $vector2 = new Tuple(2.0, 3.0, 4.0, 0.0); - - $this->assertSame(20.0, $vector1->dot($vector2)); - } - - - /** - * @testdox The cross product of two vectors - * @dataProvider parametersForVectorCrossProduct - * - * @param Tuple $vector1 - * @param Tuple $vector2 - * @param Tuple $expectedVector - */ - public function testCanBuildCrossProduct(Tuple $vector1, Tuple $vector2, Tuple $expectedVector): void - { - $actualTuple = $vector1->cross($vector2); - - $this->assertSame($expectedVector->x(), $actualTuple->x()); - $this->assertSame($expectedVector->y(), $actualTuple->y()); - $this->assertSame($expectedVector->z(), $actualTuple->z()); - $this->assertSame($expectedVector->w(), $actualTuple->w()); - } - - - /** - * @return array - */ - public function parametersForVectorCrossProduct(): array - { - return [ - [ - new Tuple(1.0, 2.0, 3.0, 0.0), - new Tuple(2.0, 3.0, 4.0, 0.0), - new Tuple(-1.0, 2.0, -1.0, 0.0), - ], - [ - new Tuple(2.0, 3.0, 4.0, 0.0), - new Tuple(1.0, 2.0, 3.0, 0.0), - new Tuple(1.0, -2.0, 1.0, 0.0), - ], - ]; - } } diff --git a/tests/Primitives/VectorTest.php b/tests/Primitives/VectorTest.php new file mode 100644 index 0000000..845f905 --- /dev/null +++ b/tests/Primitives/VectorTest.php @@ -0,0 +1,215 @@ +x = random_int(100, 10000) / 100; + $this->y = random_int(100, 10000) / 100; + $this->z = random_int(100, 10000) / 100; + } + + + public function testCanBeCreatedAsTuple(): void + { + $vector = new Vector($this->x, $this->y, $this->z); + + $this->assertInstanceOf(Tuple::class, $vector); + $this->assertSame($this->x, $vector->x()); + $this->assertSame($this->y, $vector->y()); + $this->assertSame($this->z, $vector->z()); + } + + + public function testCreatesANewPointByAddingAPoint(): void + { + $vector = new Vector(1.0, 2.0, 3.0); + $point = new Point($this->x, $this->y, $this->z); + + $actualPoint = $vector->plusPoint($point); + + $this->assertInstanceOf(Point::class, $actualPoint); + $this->assertSame(1 + $this->x, $actualPoint->x()); + $this->assertSame(2 + $this->y, $actualPoint->y()); + $this->assertSame(3 + $this->z, $actualPoint->z()); + } + + + public function testCreatesANewVectorByAddingAVector(): void + { + $vector1 = new Vector(1.0, 2.0, 3.0); + $vector2 = new Vector($this->x, $this->y, $this->z); + + $actualVector = $vector1->plusVector($vector2); + + $this->assertInstanceOf(Vector::class, $actualVector); + $this->assertSame(1 + $this->x, $actualVector->x()); + $this->assertSame(2 + $this->y, $actualVector->y()); + $this->assertSame(3 + $this->z, $actualVector->z()); + } + + + public function testCreatesANewVectorBySubtractingAVector(): void + { + $vector1 = new Vector(1.0, 2.0, 3.0); + $vector2 = new Vector($this->x, $this->y, $this->z); + + $actualVector = $vector1->minusVector($vector2); + + $this->assertInstanceOf(Vector::class, $actualVector); + $this->assertSame(1 - $this->x, $actualVector->x()); + $this->assertSame(2 - $this->y, $actualVector->y()); + $this->assertSame(3 - $this->z, $actualVector->z()); + } + + + public function testCreatesANewPointBySubtractingAPoint(): void + { + $vector = new Vector(1.0, 2.0, 3.0); + $point = new Point($this->x, $this->y, $this->z); + + $actualPoint = $vector->minusPoint($point); + + $this->assertInstanceOf(Point::class, $actualPoint); + $this->assertSame(1 - $this->x, $actualPoint->x()); + $this->assertSame(2 - $this->y, $actualPoint->y()); + $this->assertSame(3 - $this->z, $actualPoint->z()); + } + + + /** + * @dataProvider parametersForMagnitudes + * + * @param Vector $vector + * @param float $expectedMagnitude + */ + public function testHasAMagnitude(Vector $vector, float $expectedMagnitude): void + { + $this->assertSame($expectedMagnitude, $vector->magnitude()); + } + + + /** + * @return array + */ + public function parametersForMagnitudes(): array + { + return [ + [new Vector(1.0, 0.0, 0.0), 1.0], + [new Vector(0.0, 1.0, 0.0), 1.0], + [new Vector(0.0, 0.0, 1.0), 1.0], + [new Vector(1.0, 2.0, 3.0), sqrt(14)], + [new Vector(-1.0, -2.0, -3.0), sqrt(14)], + ]; + } + + + /** + * @dataProvider parametersForNormalization + * + * @param Vector $vector + * @param Vector $expectedVector + */ + public function testCanBeNormalized(Vector $vector, Vector $expectedVector): void + { + $normalizedVector = $vector->normalize(); + + $this->assertSame($expectedVector->x(), $normalizedVector->x()); + $this->assertSame($expectedVector->y(), $normalizedVector->y()); + $this->assertSame($expectedVector->z(), $normalizedVector->z()); + $this->assertSame(1.0, $normalizedVector->magnitude()); + } + + + /** + * @return array + */ + public function parametersForNormalization(): array + { + return [ + [ + new Vector(4.0, 0.0, 0.0), + new Vector(1.0, 0.0, 0.0), + ], + [ + new Vector(1.0, 2.0, 3.0), + new Vector(1 / sqrt(14), 2 / sqrt(14), 3 / sqrt(14)), + ], + ]; + } + + + public function testCanBuildDotProduct(): void + { + $vector1 = new Vector(1.0, 2.0, 3.0); + $vector2 = new Vector(2.0, 3.0, 4.0); + + $this->assertSame(20.0, $vector1->dot($vector2)); + } + + + /** + * @dataProvider parametersForVectorCrossProduct + * + * @param Vector $vector1 + * @param Vector $vector2 + * @param Vector $expectedVector + */ + public function testCanBuildCrossProduct(Vector $vector1, Vector $vector2, Vector $expectedVector): void + { + $actualTuple = $vector1->cross($vector2); + + $this->assertSame($expectedVector->x(), $actualTuple->x()); + $this->assertSame($expectedVector->y(), $actualTuple->y()); + $this->assertSame($expectedVector->z(), $actualTuple->z()); + } + + + /** + * @return array + */ + public function parametersForVectorCrossProduct(): array + { + return [ + [ + new Vector(1.0, 2.0, 3.0), + new Vector(2.0, 3.0, 4.0), + new Vector(-1.0, 2.0, -1.0), + ], + [ + new Vector(2.0, 3.0, 4.0), + new Vector(1.0, 2.0, 3.0), + new Vector(1.0, -2.0, 1.0), + ], + ]; + } +}