refactored Tuple and created class for Point and Vector
This commit is contained in:
parent
b243d2b9e6
commit
fc0d744bc5
7 changed files with 508 additions and 537 deletions
60
src/Primitives/Point.php
Normal file
60
src/Primitives/Point.php
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ItsAMirko\RayTracer\Primitives;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Point
|
||||||
|
*
|
||||||
|
* @package ItsAMirko\RayTracer\Primitives
|
||||||
|
*/
|
||||||
|
class Point extends Tuple
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Point constructor.
|
||||||
|
*
|
||||||
|
* @param float $x
|
||||||
|
* @param float $y
|
||||||
|
* @param float $z
|
||||||
|
*/
|
||||||
|
public function __construct(float $x, float $y, float $z)
|
||||||
|
{
|
||||||
|
parent::__construct($x, $y, $z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Vector $vector
|
||||||
|
*
|
||||||
|
* @return Point
|
||||||
|
*/
|
||||||
|
public function plusVector(Vector $vector): Point
|
||||||
|
{
|
||||||
|
return new Point($this->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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -5,9 +5,6 @@ declare(strict_types=1);
|
||||||
namespace ItsAMirko\RayTracer\Primitives;
|
namespace ItsAMirko\RayTracer\Primitives;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
|
||||||
use function pow;
|
|
||||||
use function sqrt;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Tuple
|
* Class Tuple
|
||||||
|
|
@ -31,11 +28,6 @@ class Tuple
|
||||||
*/
|
*/
|
||||||
protected $z;
|
protected $z;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var float
|
|
||||||
*/
|
|
||||||
protected $w;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tuple constructor.
|
* Tuple constructor.
|
||||||
|
|
@ -43,40 +35,12 @@ class Tuple
|
||||||
* @param float $x
|
* @param float $x
|
||||||
* @param float $y
|
* @param float $y
|
||||||
* @param float $z
|
* @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->x = $x;
|
||||||
$this->y = $y;
|
$this->y = $y;
|
||||||
$this->z = $z;
|
$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
|
* @return Tuple
|
||||||
*/
|
*/
|
||||||
public function negate(): 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
|
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');
|
throw new InvalidArgumentException('Can not divided with zero');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new self($this->x / $divider, $this->y / $divider, $this->z / $divider, $this->w / $divider);
|
return new static($this->x / $divider, $this->y / $divider, $this->z / $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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
111
src/Primitives/Vector.php
Normal file
111
src/Primitives/Vector.php
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ItsAMirko\RayTracer\Primitives;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Vector
|
||||||
|
*
|
||||||
|
* @package ItsAMirko\RayTracer\Primitives
|
||||||
|
*/
|
||||||
|
class Vector extends Tuple
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Vector constructor.
|
||||||
|
*
|
||||||
|
* @param float $x
|
||||||
|
* @param float $y
|
||||||
|
* @param float $z
|
||||||
|
*/
|
||||||
|
public function __construct(float $x, float $y, float $z)
|
||||||
|
{
|
||||||
|
parent::__construct($x, $y, $z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Vector $vector
|
||||||
|
*
|
||||||
|
* @return Vector
|
||||||
|
*/
|
||||||
|
public function plusVector(Vector $vector): Vector
|
||||||
|
{
|
||||||
|
return new Vector($this->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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
include __DIR__ . '/../vendor/autoload.php';
|
include __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
use ItsAMirko\RayTracer\Primitives\Tuple;
|
use ItsAMirko\RayTracer\Primitives\Point;
|
||||||
|
use ItsAMirko\RayTracer\Primitives\Vector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Projectile
|
* Class Projectile
|
||||||
|
|
@ -12,12 +13,12 @@ use ItsAMirko\RayTracer\Primitives\Tuple;
|
||||||
class Projectile
|
class Projectile
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Tuple
|
* @var Point
|
||||||
*/
|
*/
|
||||||
public $position;
|
public $position;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Tuple
|
* @var Vector
|
||||||
*/
|
*/
|
||||||
public $velocity;
|
public $velocity;
|
||||||
}
|
}
|
||||||
|
|
@ -28,12 +29,12 @@ class Projectile
|
||||||
class Environment
|
class Environment
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Tuple
|
* @var Vector
|
||||||
*/
|
*/
|
||||||
public $gravity;
|
public $gravity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Tuple
|
* @var Vector
|
||||||
*/
|
*/
|
||||||
public $wind;
|
public $wind;
|
||||||
}
|
}
|
||||||
|
|
@ -41,19 +42,19 @@ class Environment
|
||||||
|
|
||||||
function tick(Environment $environment, Projectile $projectile): Projectile
|
function tick(Environment $environment, Projectile $projectile): Projectile
|
||||||
{
|
{
|
||||||
$projectile->position = $projectile->position->plus($projectile->velocity);
|
$projectile->position = $projectile->position->plusVector($projectile->velocity);
|
||||||
$projectile->velocity = $projectile->velocity->plus($environment->gravity)->plus($environment->wind);
|
$projectile->velocity = $projectile->velocity->plusVector($environment->gravity)->plusVector($environment->wind);
|
||||||
|
|
||||||
return $projectile;
|
return $projectile;
|
||||||
}
|
}
|
||||||
|
|
||||||
$environment = new Environment();
|
$environment = new Environment();
|
||||||
$environment->gravity = Tuple::createVector(0.0, -0.1, 0.0);
|
$environment->gravity = new Vector(0.0, -0.1, 0.0);
|
||||||
$environment->wind = Tuple::createVector(-0.01, 0.0, 0.0);
|
$environment->wind = new Vector(-0.01, 0.0, 0.0);
|
||||||
|
|
||||||
$projectile = new Projectile();
|
$projectile = new Projectile();
|
||||||
$projectile->position = Tuple::createPoint(0.0, 1.0, 0.0);
|
$projectile->position = new Point(0.0, 1.0, 0.0);
|
||||||
$projectile->velocity = Tuple::createVector(1.0, 1.0, 0.0)->normalize();
|
$projectile->velocity = (new Vector(1.0, 1.0, 0.0))->normalize();
|
||||||
|
|
||||||
while ($projectile->position->y() > 0) {
|
while ($projectile->position->y() > 0) {
|
||||||
echo $projectile->position->x() . ' | ' . $projectile->position->y() . PHP_EOL;
|
echo $projectile->position->x() . ' | ' . $projectile->position->y() . PHP_EOL;
|
||||||
|
|
|
||||||
93
tests/Primitives/PointTest.php
Normal file
93
tests/Primitives/PointTest.php
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ItsAMirko\RayTracer\Primitives;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function random_int;
|
||||||
|
|
||||||
|
class PointTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var float
|
||||||
|
*/
|
||||||
|
private $x;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var float
|
||||||
|
*/
|
||||||
|
private $y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var float|int float
|
||||||
|
*/
|
||||||
|
private $z;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,252 +4,33 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace ItsAMirko\RayTracer\Primitives;
|
namespace ItsAMirko\RayTracer\Primitives;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use RuntimeException;
|
|
||||||
use function sqrt;
|
|
||||||
|
|
||||||
class TupleTest extends TestCase
|
class TupleTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
public function testProvidedCoordinates(): void
|
||||||
* @testdox A tuple with w=1.0 is a point
|
|
||||||
*/
|
|
||||||
public function testCanBeAPoint(): 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.3, $tuple->x());
|
||||||
$this->assertSame(-4.2, $tuple->y());
|
$this->assertSame(-4.2, $tuple->y());
|
||||||
$this->assertSame(3.1, $tuple->z());
|
$this->assertSame(3.1, $tuple->z());
|
||||||
$this->assertSame(1.0, $tuple->w());
|
|
||||||
$this->assertTrue($tuple->isPoint());
|
|
||||||
$this->assertFalse($tuple->isVector());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
public function testCanBeNegated(): void
|
||||||
* @testdox A tuple with w=0.0 is a vector
|
|
||||||
*/
|
|
||||||
public function testCanBeAVector(): 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());
|
$negatedTuple = $tuple->negate();
|
||||||
$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);
|
|
||||||
|
|
||||||
$this->assertInstanceOf(Tuple::class, $point);
|
$this->assertSame(-3.0, $negatedTuple->x());
|
||||||
$this->assertSame(4.0, $point->x());
|
$this->assertSame(2.0, $negatedTuple->y());
|
||||||
$this->assertSame(-4.0, $point->y());
|
$this->assertSame(-5.0, $negatedTuple->z());
|
||||||
$this->assertSame(3.0, $point->z());
|
|
||||||
$this->assertSame(1.0, $point->w());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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
|
* @dataProvider parametersForTupleMultiplication
|
||||||
*
|
*
|
||||||
* @param Tuple $tuple1
|
* @param Tuple $tuple1
|
||||||
|
|
@ -263,7 +44,6 @@ class TupleTest extends TestCase
|
||||||
$this->assertSame($expectedTuple->x(), $actualTuple->x());
|
$this->assertSame($expectedTuple->x(), $actualTuple->x());
|
||||||
$this->assertSame($expectedTuple->y(), $actualTuple->y());
|
$this->assertSame($expectedTuple->y(), $actualTuple->y());
|
||||||
$this->assertSame($expectedTuple->z(), $actualTuple->z());
|
$this->assertSame($expectedTuple->z(), $actualTuple->z());
|
||||||
$this->assertSame($expectedTuple->w(), $actualTuple->w());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -273,27 +53,14 @@ class TupleTest extends TestCase
|
||||||
public function parametersForTupleMultiplication(): array
|
public function parametersForTupleMultiplication(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[
|
[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, -4.0),
|
[new Tuple(1.0, -2.0, 3.0), 0.5, new Tuple(0.5, -1.0, 1.5)],
|
||||||
3.5,
|
[new Tuple(1.0, -2.0, 3.0), -1, new Tuple(-1.0, 2.0, -3.0)],
|
||||||
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),
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @testdox Dividing a tuple
|
|
||||||
* @dataProvider parametersForTupleDivision
|
* @dataProvider parametersForTupleDivision
|
||||||
*
|
*
|
||||||
* @param Tuple $tuple1
|
* @param Tuple $tuple1
|
||||||
|
|
@ -307,7 +74,6 @@ class TupleTest extends TestCase
|
||||||
$this->assertSame($expectedTuple->x(), $actualTuple->x());
|
$this->assertSame($expectedTuple->x(), $actualTuple->x());
|
||||||
$this->assertSame($expectedTuple->y(), $actualTuple->y());
|
$this->assertSame($expectedTuple->y(), $actualTuple->y());
|
||||||
$this->assertSame($expectedTuple->z(), $actualTuple->z());
|
$this->assertSame($expectedTuple->z(), $actualTuple->z());
|
||||||
$this->assertSame($expectedTuple->w(), $actualTuple->w());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -317,138 +83,9 @@ class TupleTest extends TestCase
|
||||||
public function parametersForTupleDivision(): array
|
public function parametersForTupleDivision(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[
|
[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, -4.0),
|
[new Tuple(1.0, -2.0, 3.0), 0.5, new Tuple(2.0, -4.0, 6.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),
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
215
tests/Primitives/VectorTest.php
Normal file
215
tests/Primitives/VectorTest.php
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ItsAMirko\RayTracer\Primitives;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function random_int;
|
||||||
|
|
||||||
|
class VectorTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var float
|
||||||
|
*/
|
||||||
|
private $x;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var float
|
||||||
|
*/
|
||||||
|
private $y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var float|int float
|
||||||
|
*/
|
||||||
|
private $z;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->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),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue