Compare commits
6 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b736be2b89 | ||
|
|
1a3e0d31db | ||
|
|
dda4ba31e0 | ||
|
|
bcb839fb49 | ||
|
|
4e58fd02a7 | ||
|
|
bc3090325a |
12 changed files with 43659 additions and 9 deletions
|
|
@ -11,7 +11,8 @@
|
|||
],
|
||||
"minimum-stability": "stable",
|
||||
"require": {
|
||||
"php": "^7.3"
|
||||
"php": "^7.3",
|
||||
"league/flysystem": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.3"
|
||||
|
|
|
|||
89
composer.lock
generated
89
composer.lock
generated
|
|
@ -4,8 +4,93 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0f508f2fcdf2e9cc795371e86bf7a436",
|
||||
"packages": [],
|
||||
"content-hash": "cc2e4b7259002d24a30a44257c9aa474",
|
||||
"packages": [
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "1.0.55",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem.git",
|
||||
"reference": "33c91155537c6dc899eacdc54a13ac6303f156e6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/33c91155537c6dc899eacdc54a13ac6303f156e6",
|
||||
"reference": "33c91155537c6dc899eacdc54a13ac6303f156e6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-fileinfo": "*",
|
||||
"php": ">=5.5.9"
|
||||
},
|
||||
"conflict": {
|
||||
"league/flysystem-sftp": "<1.0.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "^3.4",
|
||||
"phpunit/phpunit": "^5.7.10"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-fileinfo": "Required for MimeType",
|
||||
"ext-ftp": "Allows you to use FTP server storage",
|
||||
"ext-openssl": "Allows you to use FTPS server storage",
|
||||
"league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
|
||||
"league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
|
||||
"league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
|
||||
"league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
|
||||
"league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
|
||||
"league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
|
||||
"league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
|
||||
"league/flysystem-webdav": "Allows you to use WebDAV storage",
|
||||
"league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
|
||||
"spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
|
||||
"srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Flysystem\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frank de Jonge",
|
||||
"email": "info@frenky.net"
|
||||
}
|
||||
],
|
||||
"description": "Filesystem abstraction: Many filesystems, one API.",
|
||||
"keywords": [
|
||||
"Cloud Files",
|
||||
"WebDAV",
|
||||
"abstraction",
|
||||
"aws",
|
||||
"cloud",
|
||||
"copy.com",
|
||||
"dropbox",
|
||||
"file systems",
|
||||
"files",
|
||||
"filesystem",
|
||||
"filesystems",
|
||||
"ftp",
|
||||
"rackspace",
|
||||
"remote",
|
||||
"s3",
|
||||
"sftp",
|
||||
"storage"
|
||||
],
|
||||
"time": "2019-08-24T11:17:19+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
|
|
|
|||
42903
images/virtual_cannon.ppm
Normal file
42903
images/virtual_cannon.ppm
Normal file
File diff suppressed because it is too large
Load diff
85
src/Canvas/Canvas.php
Normal file
85
src/Canvas/Canvas.php
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ItsAMirko\RayTracer\Canvas;
|
||||
|
||||
use ItsAMirko\RayTracer\Primitives\Color;
|
||||
use OutOfBoundsException;
|
||||
|
||||
class Canvas
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $width;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $height;
|
||||
|
||||
/**
|
||||
* @var Color[][]
|
||||
*/
|
||||
private $pixels;
|
||||
|
||||
|
||||
/**
|
||||
* Canvas constructor.
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
*/
|
||||
public function __construct(int $width, int $height)
|
||||
{
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function width(): int
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function height(): int
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Color[][]
|
||||
*/
|
||||
public function pixels(): array
|
||||
{
|
||||
return $this->pixels;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param Color $color
|
||||
*/
|
||||
public function addPixel(int $x, int $y, Color $color): void
|
||||
{
|
||||
if ($x < 0 || $x >= $this->width) {
|
||||
throw new OutOfBoundsException('Invalid position for x provided. Expected integer between 0 and '
|
||||
. ($this->width - 1) . ', but got ' . $x);
|
||||
} elseif ($y < 0 || $y >= $this->height) {
|
||||
throw new OutOfBoundsException('Invalid position for y provided. Expected integer between 0 and '
|
||||
. ($this->height - 1) . ', but got ' . $y);
|
||||
}
|
||||
|
||||
$this->pixels[$x][$y] = $color;
|
||||
}
|
||||
}
|
||||
99
src/Canvas/CanvasPpmFileWriter.php
Normal file
99
src/Canvas/CanvasPpmFileWriter.php
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ItsAMirko\RayTracer\Canvas;
|
||||
|
||||
use League\Flysystem\FilesystemInterface;
|
||||
use function strlen;
|
||||
use const PHP_EOL;
|
||||
|
||||
/**
|
||||
* Class CanvasPpmFileWriter
|
||||
*
|
||||
* @package ItsAMirko\RayTracer\Canvas
|
||||
*/
|
||||
class CanvasPpmFileWriter
|
||||
{
|
||||
/**
|
||||
* Defines the maximal number of chars per line in the file.
|
||||
*/
|
||||
private const MAX_CHARS_PER_LINE = 70;
|
||||
|
||||
/**
|
||||
* @var FilesystemInterface
|
||||
*/
|
||||
private $filesystem;
|
||||
|
||||
|
||||
/**
|
||||
* CanvasFileWriter constructor.
|
||||
*
|
||||
* @param FilesystemInterface $filesystem
|
||||
*/
|
||||
public function __construct(FilesystemInterface $filesystem)
|
||||
{
|
||||
$this->filesystem = $filesystem;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Canvas $canvas
|
||||
* @param string $filename
|
||||
*/
|
||||
public function createFile(Canvas $canvas, string $filename): void
|
||||
{
|
||||
$content = $this->getFileHeader($canvas);
|
||||
$content .= $this->getImageContent($canvas);
|
||||
|
||||
$this->filesystem->put($filename, $content);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Canvas $canvas
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getFileHeader(Canvas $canvas): string
|
||||
{
|
||||
return 'P3' . PHP_EOL . $canvas->width() . ' ' . $canvas->height() . PHP_EOL . '255' . PHP_EOL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Canvas $canvas
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getImageContent(Canvas $canvas): string
|
||||
{
|
||||
$content = '';
|
||||
$pixels = $canvas->pixels();
|
||||
|
||||
for ($y = 0; $y < $canvas->height(); $y++) {
|
||||
$rowContent = '';
|
||||
for ($x = 0; $x < $canvas->width(); $x++) {
|
||||
foreach (['red', 'green', 'blue'] as $color) {
|
||||
$colorHex = '0';
|
||||
if (isset($pixels[$x][$y]) && $color === 'red') {
|
||||
$colorHex = $pixels[$x][$y]->redAsHex();
|
||||
} elseif (isset($pixels[$x][$y]) && $color === 'green') {
|
||||
$colorHex = $pixels[$x][$y]->greenAsHex();
|
||||
} elseif (isset($pixels[$x][$y]) && $color === 'blue') {
|
||||
$colorHex = $pixels[$x][$y]->blueAsHex();
|
||||
}
|
||||
|
||||
if (strlen($rowContent . $colorHex) > self::MAX_CHARS_PER_LINE) {
|
||||
$content .= trim($rowContent) . PHP_EOL;
|
||||
$rowContent = '';
|
||||
}
|
||||
$rowContent .= $colorHex . ' ';
|
||||
}
|
||||
}
|
||||
$content .= trim($rowContent) . PHP_EOL;
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
147
src/Primitives/Color.php
Normal file
147
src/Primitives/Color.php
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ItsAMirko\RayTracer\Primitives;
|
||||
|
||||
/**
|
||||
* Class Color
|
||||
*
|
||||
* @package ItsAMirko\RayTracer\Primitives
|
||||
*/
|
||||
class Color
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $red;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $green;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $blue;
|
||||
|
||||
|
||||
/**
|
||||
* Color constructor.
|
||||
*
|
||||
* @param float $red
|
||||
* @param float $green
|
||||
* @param float $blue
|
||||
*/
|
||||
public function __construct(float $red, float $green, float $blue)
|
||||
{
|
||||
$this->red = $red;
|
||||
$this->green = $green;
|
||||
$this->blue = $blue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function red(): float
|
||||
{
|
||||
return $this->red;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function green(): float
|
||||
{
|
||||
return $this->green;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function blue(): float
|
||||
{
|
||||
return $this->blue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function redAsHex(): int
|
||||
{
|
||||
$percentage = ($this->red < 0) ? 0 : (($this->red > 1) ? 1 : $this->red);
|
||||
|
||||
return (int)round(255 * $percentage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function greenAsHex(): int
|
||||
{
|
||||
$percentage = ($this->green < 0) ? 0 : (($this->green > 1) ? 1 : $this->green);
|
||||
|
||||
return (int)round(255 * $percentage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function blueAsHex(): int
|
||||
{
|
||||
$percentage = ($this->blue < 0) ? 0 : (($this->blue > 1) ? 1 : $this->blue);
|
||||
|
||||
return (int)round(255 * $percentage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Color $color
|
||||
*
|
||||
* @return Color
|
||||
*/
|
||||
public function plusColor(Color $color): Color
|
||||
{
|
||||
return new Color($this->red + $color->red(), $this->green + $color->green(), $this->blue + $color->blue());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Color $color
|
||||
*
|
||||
* @return Color
|
||||
*/
|
||||
public function minusColor(Color $color): Color
|
||||
{
|
||||
return new Color($this->red - $color->red(), $this->green - $color->green(), $this->blue - $color->blue());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param float $multiplier
|
||||
*
|
||||
* @return Color
|
||||
*/
|
||||
public function multiplyWithScalar(float $multiplier): Color
|
||||
{
|
||||
return new Color($this->red * $multiplier, $this->green * $multiplier, $this->blue * $multiplier);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Color $color
|
||||
*
|
||||
* @return Color
|
||||
*/
|
||||
public function multiplyWithColor(Color $color): Color
|
||||
{
|
||||
return new Color($this->red * $color->red(), $this->green * $color->green(), $this->blue * $color->blue());
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,13 @@ declare(strict_types=1);
|
|||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use ItsAMirko\RayTracer\Canvas\Canvas;
|
||||
use ItsAMirko\RayTracer\Canvas\CanvasPpmFileWriter;
|
||||
use ItsAMirko\RayTracer\Primitives\Color;
|
||||
use ItsAMirko\RayTracer\Primitives\Point;
|
||||
use ItsAMirko\RayTracer\Primitives\Vector;
|
||||
use League\Flysystem\Adapter\Local;
|
||||
use League\Flysystem\Filesystem;
|
||||
|
||||
/**
|
||||
* Class Projectile
|
||||
|
|
@ -39,7 +44,6 @@ class Environment
|
|||
public $wind;
|
||||
}
|
||||
|
||||
|
||||
function tick(Environment $environment, Projectile $projectile): Projectile
|
||||
{
|
||||
$projectile->position = $projectile->position->plusVector($projectile->velocity);
|
||||
|
|
@ -54,9 +58,17 @@ $environment->wind = new Vector(-0.01, 0.0, 0.0);
|
|||
|
||||
$projectile = new Projectile();
|
||||
$projectile->position = new Point(0.0, 1.0, 0.0);
|
||||
$projectile->velocity = (new Vector(1.0, 1.0, 0.0))->normalize();
|
||||
$projectile->velocity = (new Vector(1.0, 1.8, 0.0))->normalize()->multiplyWith(11.25);
|
||||
|
||||
$canvas = new Canvas(900, 550);
|
||||
$color = new Color(0.0, 1.0, 0.0);
|
||||
|
||||
while ($projectile->position->y() > 0) {
|
||||
echo $projectile->position->x() . ' | ' . $projectile->position->y() . PHP_EOL;
|
||||
$canvas->addPixel((int)$projectile->position->x(), 550 - (int)$projectile->position->y(), $color);
|
||||
tick($environment, $projectile);
|
||||
}
|
||||
}
|
||||
|
||||
$adapter = new Local(__DIR__ . '/../images');
|
||||
$filesystem = new Filesystem($adapter);
|
||||
$canvasWriter = new CanvasPpmFileWriter($filesystem);
|
||||
$canvasWriter->createFile($canvas, 'virtual_cannon.ppm');
|
||||
122
tests/Canvas/CanvasPpmFileWriterTest.php
Normal file
122
tests/Canvas/CanvasPpmFileWriterTest.php
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ItsAMirko\RayTracer\Canvas;
|
||||
|
||||
use ItsAMirko\RayTracer\Primitives\Color;
|
||||
use League\Flysystem\FilesystemInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function explode;
|
||||
use const PHP_EOL;
|
||||
|
||||
class CanvasPpmFileWriterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var FilesystemInterface
|
||||
*/
|
||||
private $filesystem;
|
||||
|
||||
/**
|
||||
* @var CanvasPpmFileWriter
|
||||
*/
|
||||
private $fileWriter;
|
||||
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->filesystem = $this->createMock(FilesystemInterface::class);
|
||||
|
||||
$this->fileWriter = new CanvasPpmFileWriter($this->filesystem);
|
||||
}
|
||||
|
||||
|
||||
public function testWritesPPMHeader(): void
|
||||
{
|
||||
$expectedFilename = 'my_canvas.ppm';
|
||||
$expectedContentLines = [
|
||||
0 => 'P3',
|
||||
1 => '5 3',
|
||||
2 => '255',
|
||||
];
|
||||
|
||||
$this->filesystem->expects($this->once())
|
||||
->method('put')
|
||||
->with($expectedFilename, $this->checkFileContentCallback($expectedContentLines));
|
||||
|
||||
$canvas = new Canvas(5, 3);
|
||||
$this->fileWriter->createFile($canvas, $expectedFilename);
|
||||
}
|
||||
|
||||
|
||||
public function testWritesPixels(): void
|
||||
{
|
||||
$expectedFilename = 'my_canvas.ppm';
|
||||
$expectedContentLines = [
|
||||
3 => '255 0 0 0 0 0 0 0 0 0 0 0 0 0 0',
|
||||
4 => '0 0 0 0 0 0 0 128 0 0 0 0 0 0 0',
|
||||
5 => '0 0 0 0 0 0 0 0 0 0 0 0 0 0 255',
|
||||
];
|
||||
|
||||
$this->filesystem->expects($this->once())
|
||||
->method('put')
|
||||
->with($expectedFilename, $this->checkFileContentCallback($expectedContentLines));
|
||||
|
||||
$canvas = new Canvas(5, 3);
|
||||
$canvas->addPixel(0, 0, new Color(1.5, 0.0, 0.0));
|
||||
$canvas->addPixel(2, 1, new Color(0.0, 0.5, 0.0));
|
||||
$canvas->addPixel(4, 2, new Color(-0.5, 0.0, 1.0));
|
||||
|
||||
$this->fileWriter->createFile($canvas, $expectedFilename);
|
||||
}
|
||||
|
||||
|
||||
public function testSplitsLongLinesOfPixels(): void
|
||||
{
|
||||
$expectedFilename = 'my_canvas.ppm';
|
||||
$expectedContentLines = [
|
||||
3 => '255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204',
|
||||
4 => '153 255 204 153 255 204 153 255 204 153 255 204 153',
|
||||
5 => '255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204',
|
||||
6 => '153 255 204 153 255 204 153 255 204 153 255 204 153',
|
||||
];
|
||||
|
||||
$this->filesystem->expects($this->once())
|
||||
->method('put')
|
||||
->with($expectedFilename, $this->checkFileContentCallback($expectedContentLines));
|
||||
|
||||
$canvas = new Canvas(10, 2);
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$canvas->addPixel($i, 0, new Color(1, 0.8, 0.6));
|
||||
$canvas->addPixel($i, 1, new Color(1, 0.8, 0.6));
|
||||
}
|
||||
|
||||
$this->fileWriter->createFile($canvas, $expectedFilename);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a callback to check the content of the PPM file
|
||||
*
|
||||
* @param array $expectedContentLines
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
private function checkFileContentCallback(array $expectedContentLines)
|
||||
{
|
||||
return $this->callback(function (
|
||||
string $content
|
||||
) use ($expectedContentLines): bool {
|
||||
$actualContentLines = explode(PHP_EOL, $content);
|
||||
foreach ($expectedContentLines as $line => $expectedContent) {
|
||||
if ($expectedContent !== $actualContentLines[$line]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
58
tests/Canvas/CanvasTest.php
Normal file
58
tests/Canvas/CanvasTest.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ItsAMirko\RayTracer\Canvas;
|
||||
|
||||
use Exception;
|
||||
use ItsAMirko\RayTracer\Primitives\Color;
|
||||
use OutOfBoundsException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function random_int;
|
||||
|
||||
class CanvasTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testProvidesDimensions(): void
|
||||
{
|
||||
$width = random_int(1, 100);
|
||||
$height = random_int(1, 100);
|
||||
|
||||
$canvas = new Canvas($width, $height);
|
||||
|
||||
$this->assertSame($width, $canvas->width());
|
||||
$this->assertSame($height, $canvas->height());
|
||||
}
|
||||
|
||||
|
||||
public function testProvidesItsPixels(): void
|
||||
{
|
||||
$canvas = new Canvas(2, 2);
|
||||
$canvas->addPixel(0, 0, new Color(1.0, 0.0, 0.0));
|
||||
$canvas->addPixel(0, 1, new Color(0.0, 1.0, 0.0));
|
||||
$canvas->addPixel(1, 1, new Color(0.0, 0.0, 1.0));
|
||||
|
||||
$expectedPixels = [
|
||||
0 => [
|
||||
0 => new Color(1.0, 0.0, 0.0),
|
||||
1 => new Color(0.0, 1.0, 0.0),
|
||||
],
|
||||
1 => [
|
||||
1 => new Color(0.0, 0.0, 1.0),
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expectedPixels, $canvas->pixels());
|
||||
}
|
||||
|
||||
|
||||
public function testThrowsExceptionIfPixelToAddIsOutOfBounds(): void
|
||||
{
|
||||
$this->expectException(OutOfBoundsException::class);
|
||||
|
||||
$canvas = new Canvas(1, 1);
|
||||
$canvas->addPixel(1, 1, new Color(1.0, 0.0, 0.0));
|
||||
}
|
||||
}
|
||||
138
tests/Primitives/ColorTest.php
Normal file
138
tests/Primitives/ColorTest.php
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ItsAMirko\RayTracer\Primitives;
|
||||
|
||||
use Exception;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function random_int;
|
||||
|
||||
class ColorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $red;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $green;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $blue;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->red = random_int(0, 100) / 100;
|
||||
$this->green = random_int(0, 100) / 100;
|
||||
$this->blue = random_int(0, 100) / 100;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dataProvider parametersForHexValues
|
||||
*
|
||||
* @param int $red
|
||||
* @param int $green
|
||||
* @param int $blue
|
||||
* @param Color $color
|
||||
*/
|
||||
public function testProvidesHexValues(int $red, int $green, int $blue, Color $color): void
|
||||
{
|
||||
$this->assertSame($red, $color->redAsHex());
|
||||
$this->assertSame($green, $color->greenAsHex());
|
||||
$this->assertSame($blue, $color->blueAsHex());
|
||||
}
|
||||
|
||||
|
||||
public function parametersForHexValues()
|
||||
{
|
||||
return [
|
||||
[255, 0, 0, new Color(1.5, 0.0, 0.0)],
|
||||
[0, 128, 0, new Color(0.0, 0.5, 0.0)],
|
||||
[0, 0, 255, new Color(-0.5, 0.0, 1.0)],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function testProvidedColorValues(): void
|
||||
{
|
||||
$color = new Color($this->red, $this->green, $this->blue);
|
||||
|
||||
$this->assertSame($this->red, $color->red());
|
||||
$this->assertSame($this->green, $color->green());
|
||||
$this->assertSame($this->blue, $color->blue());
|
||||
}
|
||||
|
||||
|
||||
public function testCreatesNewColorByAddingAColor(): void
|
||||
{
|
||||
$color1 = new Color(1.0, 2.0, 3.0);
|
||||
$color2 = new Color($this->red, $this->green, $this->blue);
|
||||
|
||||
$actualColor = $color1->plusColor($color2);
|
||||
|
||||
$this->assertInstanceOf(Color::class, $actualColor);
|
||||
$this->assertSame(1 + $this->red, $actualColor->red());
|
||||
$this->assertSame(2 + $this->green, $actualColor->green());
|
||||
$this->assertSame(3 + $this->blue, $actualColor->blue());
|
||||
}
|
||||
|
||||
|
||||
public function testCreatesNewColorBySubtractingAColor(): void
|
||||
{
|
||||
$color1 = new Color(1.0, 2.0, 3.0);
|
||||
$color2 = new Color($this->red, $this->green, $this->blue);
|
||||
|
||||
$actualColor = $color1->minusColor($color2);
|
||||
|
||||
$this->assertInstanceOf(Color::class, $actualColor);
|
||||
$this->assertSame(1 - $this->red, $actualColor->red());
|
||||
$this->assertSame(2 - $this->green, $actualColor->green());
|
||||
$this->assertSame(3 - $this->blue, $actualColor->blue());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testCreatesNewColorByMultiplyingWithAScalar(): void
|
||||
{
|
||||
$color = new Color($this->red, $this->green, $this->blue);
|
||||
$scalar = random_int(0, 100) / 100;
|
||||
|
||||
$actualColor = $color->multiplyWithScalar($scalar);
|
||||
|
||||
$this->assertInstanceOf(Color::class, $actualColor);
|
||||
$this->assertSame($scalar * $this->red, $actualColor->red());
|
||||
$this->assertSame($scalar * $this->green, $actualColor->green());
|
||||
$this->assertSame($scalar * $this->blue, $actualColor->blue());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testCreatesNewColorByMultiplyingWithAColor(): void
|
||||
{
|
||||
$color1 = new Color(1.0, 2.0, 3.0);
|
||||
$color2 = new Color($this->red, $this->green, $this->blue);
|
||||
|
||||
$actualColor = $color1->multiplyWithColor($color2);
|
||||
|
||||
$this->assertInstanceOf(Color::class, $actualColor);
|
||||
$this->assertSame(1.0 * $this->red, $actualColor->red());
|
||||
$this->assertSame(2.0 * $this->green, $actualColor->green());
|
||||
$this->assertSame(3.0 * $this->blue, $actualColor->blue());
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ class PointTest extends TestCase
|
|||
private $y;
|
||||
|
||||
/**
|
||||
* @var float|int float
|
||||
* @var float
|
||||
*/
|
||||
private $z;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class VectorTest extends TestCase
|
|||
private $y;
|
||||
|
||||
/**
|
||||
* @var float|int float
|
||||
* @var float
|
||||
*/
|
||||
private $z;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue