This commit is contained in:
Patrick Stevens
2021-04-03 11:43:46 +01:00
parent 18b7b0a21b
commit 0487ef0e20
12 changed files with 286 additions and 9 deletions

48
RayTracing.App/Program.fs Normal file
View File

@@ -0,0 +1,48 @@
namespace RayTracing.App
open RayTracing
open System.IO.Abstractions
open Spectre.Console
module Program =
type ProgressTask with
member this.Increment (prog : float<progress>) = this.Increment (prog / 1.0<progress>)
let go (ctx : ProgressContext) =
let fs = FileSystem()
let output =
fs.Path.GetTempFileName ()
|> fun s -> fs.Path.ChangeExtension (s, ".ppm")
|> fs.FileInfo.FromFileName
let task = ctx.AddTask "[green]Generating image[/]"
let maxProgress, image = SampleImages.gradient task.Increment
task.MaxValue <- maxProgress / 1.0<progress>
let image = image |> Async.RunSynchronously
let outputTask = ctx.AddTask "[green]Writing image[/]"
let maxProgress, writer = ImageOutput.toPpm outputTask.Increment image output
outputTask.MaxValue <- maxProgress / 1.0<progress>
writer |> Async.RunSynchronously
printfn "%s" output.FullName
[<EntryPoint>]
let main (_ : string []) : int =
let prog =
AnsiConsole.Progress()
.Columns(
TaskDescriptionColumn(),
ProgressBarColumn(),
PercentageColumn(),
RemainingTimeColumn(),
SpinnerColumn()
)
prog.HideCompleted <- false
prog.AutoClear <- false
prog.Start go
0

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RayTracing\RayTracing.fsproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Spectre.Console" Version="0.38.1-preview.0.17" />
</ItemGroup>
</Project>

View File

@@ -1,10 +1,49 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "RayTracing", "RayTracing\RayTracing.fsproj", "{865D12F3-3694-4E1B-A1E2-B5867B48F58C}"
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.6.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "RayTracing", "RayTracing\RayTracing.fsproj", "{AE2CD8DA-FFA5-49BE-B5C9-1A96A3928325}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TestRayTracing", "TestRayTracing\TestRayTracing.fsproj", "{3C554C00-650A-4DEA-B37F-F0831D21CF37}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "RayTracing.App", "RayTracing.App\RayTracing.App.fsproj", "{94792D42-3D22-4FAE-A15B-B89AAF6DB732}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Debug|x64.ActiveCfg = Debug|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Debug|x64.Build.0 = Debug|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Debug|x86.ActiveCfg = Debug|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Debug|x86.Build.0 = Debug|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Release|Any CPU.Build.0 = Release|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Release|x64.ActiveCfg = Release|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Release|x64.Build.0 = Release|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Release|x86.ActiveCfg = Release|Any CPU
{3C554C00-650A-4DEA-B37F-F0831D21CF37}.Release|x86.Build.0 = Release|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Debug|x64.ActiveCfg = Debug|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Debug|x64.Build.0 = Debug|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Debug|x86.ActiveCfg = Debug|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Debug|x86.Build.0 = Debug|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Release|Any CPU.Build.0 = Release|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Release|x64.ActiveCfg = Release|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Release|x64.Build.0 = Release|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Release|x86.ActiveCfg = Release|Any CPU
{94792D42-3D22-4FAE-A15B-B89AAF6DB732}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

24
RayTracing/Domain.fs Normal file
View File

@@ -0,0 +1,24 @@
namespace RayTracing
[<Measure>]
type progress
[<Struct>]
type Pixel =
{
Red : byte
Green : byte
Blue : byte
}
type Image =
| Image of Pixel [] []
[<RequireQualifiedAccess>]
module Image =
let rowCount (Image i) : int =
i.Length
let colCount (Image i) : int =
i.[0].Length

37
RayTracing/ImageOutput.fs Normal file
View File

@@ -0,0 +1,37 @@
namespace RayTracing
open System.IO
open System.IO.Abstractions
[<RequireQualifiedAccess>]
module PixelOutput =
let toPpm (pixel : Pixel) : string =
sprintf "%i %i %i" pixel.Red pixel.Green pixel.Blue
[<RequireQualifiedAccess>]
module ImageOutput =
let toPpm
(progressIncrement : float<progress> -> unit)
(image : Image)
(file : IFileInfo)
: float<progress> * Async<unit>
=
(float (Image.rowCount image)) * 1.0<progress>,
async {
use outputStream = file.OpenWrite ()
use writer = new StreamWriter (outputStream)
writer.Write "P3\n"
writer.Write (sprintf "%i %i\n" (Image.colCount image) (Image.rowCount image))
writer.Write "255\n"
match image with
| Image arr ->
for row in arr do
for pixel in row do
writer.Write (PixelOutput.toPpm pixel)
writer.Write " "
writer.Write "\n"
progressIncrement 1.0<progress>
}

View File

@@ -1,5 +0,0 @@
namespace RayTracing
module Say =
let hello name =
printfn "Hello %s" name

View File

@@ -5,7 +5,13 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="Library.fs"/>
<Compile Include="Domain.fs" />
<Compile Include="ImageOutput.fs" />
<Compile Include="SampleImages.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.IO.Abstractions" Version="13.2.28" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,22 @@
namespace RayTracing
[<RequireQualifiedAccess>]
module SampleImages =
let gradient (progressIncrement : float<progress> -> unit) : float<progress> * Image Async =
let pixelAt i j =
{
Red = (byte i)
Green = (byte j)
Blue = 64uy
}
256.0<progress>,
async {
return Array.init 256 (fun i ->
let output = Array.init 256 (pixelAt i)
progressIncrement 1.0<progress>
output
)
|> Image
}

View File

@@ -0,0 +1,5 @@
P3
3 2
255
255 0 0 0 255 0 0 0 255
255 255 0 255 255 255 0 0 0

View File

@@ -0,0 +1,36 @@
namespace TestRayTracing
open RayTracing
open NUnit.Framework
open FsUnitTyped
open System.IO.Abstractions.TestingHelpers
[<TestFixture>]
module TestRayTracing =
[<Test>]
let ``Wikipedia example of PPM output`` () =
let fs = MockFileSystem ()
let expected = TestUtils.getEmbeddedResource "PpmOutputExample.txt"
let image =
[|
[|
{ Red = 255uy ; Blue = 0uy ; Green = 0uy }
{ Red = 0uy ; Blue = 0uy ; Green = 255uy }
{ Red = 0uy ; Blue = 255uy ; Green = 0uy }
|]
[|
{ Red = 255uy ; Blue = 0uy ; Green = 255uy }
{ Red = 255uy ; Blue = 255uy ; Green = 255uy }
{ Red = 0uy ; Blue = 0uy ; Green = 0uy }
|]
|]
|> Image
let outputFile = fs.Path.GetTempFileName () |> fs.FileInfo.FromFileName
let _, writer = ImageOutput.toPpm ignore image outputFile
writer |> Async.RunSynchronously
fs.File.ReadAllText outputFile.FullName
|> shouldEqual expected

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="TestUtils.fs" />
<Compile Include="TestPpmOutput.fs" />
<EmbeddedResource Include="PpmOutputExample.txt" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FsUnit" Version="4.0.4" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0-beta.1" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="13.2.28" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RayTracing\RayTracing.fsproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,20 @@
namespace TestRayTracing
open System.IO
open System.Reflection
[<RequireQualifiedAccess>]
module TestUtils =
type Dummy = class end
let getEmbeddedResource (filename : string) : string =
let filename =
Assembly.GetAssembly(typeof<Dummy>).GetManifestResourceNames ()
|> Seq.filter (fun s -> s.EndsWith filename)
|> Seq.exactlyOne
use stream =
Assembly.GetAssembly(typeof<Dummy>).GetManifestResourceStream filename
use reader = new StreamReader (stream)
reader.ReadToEnd().Replace("\r\n", "\n")