Files
ray-tracing-fsharp/RayTracing/Point.fs
2021-04-18 23:11:45 +01:00

109 lines
3.5 KiB
Forth

namespace RayTracing
open System.Runtime.CompilerServices
/// An n-dimensional point.
/// We don't let you compare these for equality, because floats are hard.
[<NoEquality ; NoComparison ; Struct ; IsReadOnly>]
type Point =
| Point of struct(float * float * float)
[<NoEquality ; NoComparison ; Struct ; IsReadOnly>]
type Vector =
| Vector of struct(float * float * float)
[<Struct ; IsReadOnly ; NoEquality ; NoComparison>]
type UnitVector = | UnitVector of Vector
[<RequireQualifiedAccess>]
module Vector =
let dot (Vector (x, y, z)) (Vector (a, b, c)) : float =
x * a + y * b + z * c
let sum (Vector (a, b, c)) (Vector (d, e, f)) =
Vector (a + d, b + e, c + f)
let scale (scale : float) (vec : Vector) : Vector =
match vec with
| Vector (a, b, c) ->
Vector (scale * a, scale * b, scale * c)
let difference (Vector (a, b, c)) (Vector (x, y, z)) : Vector =
Vector (a - x, b - y, c - z)
let unitise (vec : Vector) : UnitVector option =
let dot = dot vec vec
if Float.equal dot 0.0 then None else
let factor = 1.0 / sqrt dot
scale factor vec
|> UnitVector
|> Some
let normSquared (vec : Vector) : float =
dot vec vec
let equal (Vector (a, b, c)) (Vector (x, y, z)) : bool =
Float.equal a x && Float.equal b y && Float.equal c z
let make (x : float) (y : float) (z : float) =
Vector (x, y, z)
let cross (Vector (x, y, z)) (Vector (a, b, c)) : Vector =
make (y * c - z * b) (z * a - x * c) (x * b - a * y)
[<RequireQualifiedAccess>]
module UnitVector =
let rec random (floatProducer : FloatProducer) (dimension : int) : UnitVector =
let struct(rand1, rand2, rand3) = floatProducer.GetThree ()
let x = (2.0 * rand1) - 1.0
let y = (2.0 * rand2) - 1.0
let z = (2.0 * rand3) - 1.0
Vector.make x y z
|> Vector.unitise
|> function
| None -> random floatProducer dimension
| Some result -> result
let inline dot (UnitVector a) (UnitVector b) = Vector.dot a b
let inline dot' (UnitVector a) (b : Vector) = Vector.dot a b
let inline difference (UnitVector v1) (UnitVector v2) = Vector.difference v1 v2
let inline difference' (UnitVector v1) (v2 : Vector) = Vector.difference v1 v2
let inline scale (scale : float) (UnitVector vec) = Vector.scale scale vec
let inline flip (UnitVector vec) = UnitVector (Vector.scale -1.0 vec)
let basis (_ : int) : UnitVector [] =
[|
Vector (1.0, 0.0, 0.0) |> UnitVector
Vector (0.0, 1.0, 0.0) |> UnitVector
Vector (0.0, 0.0, 1.0) |> UnitVector
|]
let inline coordinate (i : int) (UnitVector (Vector (a, b, c))) : float =
match i with
| 0 -> a
| 1 -> b
| 2 -> c
| _ -> failwithf "Bad coordinate: %i" i
[<RequireQualifiedAccess>]
module Point =
let inline coordinate (i : int) (Point (x, y, z)) =
match i with
| 0 -> x
| 1 -> y
| 2 -> z
| _ -> failwithf "Bad coordinate: %i" i
let sum (Point (a, b, c)) (Point (x, y, z)) : Point =
Point (a + x, b + y, c + z)
let differenceToThenFrom (Point (a, b, c)) (Point (x, y, z)) : Vector =
Vector (a - x, b - y, c - z)
let equal (Point (a, b, c)) (Point (x, y, z)) : bool =
Float.equal a x && Float.equal b y && Float.equal c z
let make (x : float) (y : float) (z : float) = Point (x, y, z)
let inline dimension _ = 3