Compare commits

..

No commits in common. "master" and "chapter-1" have entirely different histories.

12 changed files with 9 additions and 43659 deletions

View file

@ -11,8 +11,7 @@
],
"minimum-stability": "stable",
"require": {
"php": "^7.3",
"league/flysystem": "^1.0"
"php": "^7.3"
},
"require-dev": {
"phpunit/phpunit": "^8.3"

89
composer.lock generated
View file

@ -4,93 +4,8 @@
"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": "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"
}
],
"content-hash": "0f508f2fcdf2e9cc795371e86bf7a436",
"packages": [],
"packages-dev": [
{
"name": "doctrine/instantiator",

File diff suppressed because it is too large Load diff

View file

@ -1,85 +0,0 @@
<?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;
}
}

View file

@ -1,99 +0,0 @@
<?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;
}
}

View file

@ -1,147 +0,0 @@
<?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());
}
}

View file

@ -4,13 +4,8 @@ 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
@ -44,6 +39,7 @@ class Environment
public $wind;
}
function tick(Environment $environment, Projectile $projectile): Projectile
{
$projectile->position = $projectile->position->plusVector($projectile->velocity);
@ -58,17 +54,9 @@ $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.8, 0.0))->normalize()->multiplyWith(11.25);
$canvas = new Canvas(900, 550);
$color = new Color(0.0, 1.0, 0.0);
$projectile->velocity = (new Vector(1.0, 1.0, 0.0))->normalize();
while ($projectile->position->y() > 0) {
$canvas->addPixel((int)$projectile->position->x(), 550 - (int)$projectile->position->y(), $color);
echo $projectile->position->x() . ' | ' . $projectile->position->y() . PHP_EOL;
tick($environment, $projectile);
}
$adapter = new Local(__DIR__ . '/../images');
$filesystem = new Filesystem($adapter);
$canvasWriter = new CanvasPpmFileWriter($filesystem);
$canvasWriter->createFile($canvas, 'virtual_cannon.ppm');

View file

@ -1,122 +0,0 @@
<?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;
});
}
}

View file

@ -1,58 +0,0 @@
<?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));
}
}

View file

@ -1,138 +0,0 @@
<?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());
}
}

View file

@ -21,7 +21,7 @@ class PointTest extends TestCase
private $y;
/**
* @var float
* @var float|int float
*/
private $z;

View file

@ -21,7 +21,7 @@ class VectorTest extends TestCase
private $y;
/**
* @var float
* @var float|int float
*/
private $z;