SlideShare a Scribd company logo
Environmental
effects
A ray tracing
exercise
Functional Scala
London - 12 Dec 2019
ZIO-101
ZIO[-R, +E, +A]
⬇
R => IO[Either[E, A]]
⬇
R => Either[E, A]
Ray tracing
Ray tracing
→ World (spheres), light source,
camera
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
→ Reflected rays
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
→ Reflected rays
→ Discarded rays
→ Canvas
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
→ Reflected rays
→ Discarded rays
→ Canvas
→ Colored pixels
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
→ Reflected rays
→ Discarded rays
→ Canvas
→ Colored pixels
Ray tracing
Options:
Ray tracing
Options:
1. Compute all the rays (and
discard most of them)
Ray tracing
Options:
1. Compute all the rays (and
discard most of them)
2. Compute only the rays outgoing
from the camera through the
canvas, and determine how
they behave on the surfaces
Ray
case class Ray(origin: Pt, direction: Vec) {
def positionAt(t: Double): Pt =
origin + (direction * t)
}
Transformations Module
trait AT
/* Module */
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor */
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
}
Transformations Module
trait AT
/* Module */
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor */
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
}
Transformations Module
trait AT
/* Module */
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor */
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
}
Transformations Module
trait AT
/* Module */
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor */
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
}
Transformations Module
import zio.macros.annotation.accessible
trait AT
/* Module */
@accessible(">")
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor is generated
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
*/
}
Transformations Module
trait AT
/* Module */
@accessible(">")
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
}
Transformations Module
val rotatedPt =
for {
rotateX <- ATModule.>.rotateX(math.Pi / 2)
_ <- Log.>.info("rotated of π/2")
res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1))
} yield res
Transformations Module
val rotatedPt: ZIO[ATModule with Log, ATError, Pt] =
for {
rotateX <- ATModule.>.rotateX(math.Pi / 2)
_ <- Log.>.info("rotated of π/2")
res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1))
} yield res
Transformations Module - Live
val rotated: ZIO[ATModule, ATError, Vec] =
for {
rotateX <- ATModule.>.rotateX(math.Pi/2)
res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1))
} yield res
Transformations Module - Live
val rotated: ZIO[ATModule, ATError, Vec] =
for {
rotateX <- ATModule.>.rotateX(math.Pi/2)
res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1))
} yield res
→ Vec(x, y, z)
Transformations Module - Live
val rotated: ZIO[ATModule, ATError, Vec] =
for {
rotateX <- ATModule.>.rotateX(math.Pi/2)
res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1))
} yield res
→ Vec(x, y, z)
→ Pt(x, y, z)⠀
Transformations Module - Live
val rotated: ZIO[ATModule, ATError, Vec] =
for {
rotateX <- ATModule.>.rotateX(math.Pi/2)
res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1))
} yield res
→ Vec(x, y, z)
→ Pt(x, y, z)⠀
→ ⠀
Layer 1: Transformations
Camera
object Camera {
def make(
viewFrom: Pt,
viewTo: Pt,
upDirection: Vec,
visualAngleRad: Double,
hRes: Int,
vRes: Int):
ZIO[ATModule, AlgebraicError, Camera] =
worldTransformation(viewFrom, viewTo, upDirection).map {
worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf)
}
Camera
object Camera {
def make(
viewFrom: Pt,
viewTo: Pt,
upDirection: Vec,
visualAngleRad: Double,
hRes: Int,
vRes: Int):
ZIO[ATModule, AlgebraicError, Camera] =
worldTransformation(viewFrom, viewTo, upDirection).map {
worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf)
}
World
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
→ Everything requires ATModule
World Rendering - Top Down
Rastering - Generate a stream of colored pixels
@accessible(">")
trait RasteringModule {
val rasteringModule: RasteringModule.Service[Any]
}
object RasteringModule {
trait Service[R] {
def raster(world: World, camera: Camera):
ZStream[R, RayTracerError, ColoredPixel]
}
World Rendering - Top Down
Rastering - Generate a stream of colored pixels
@accessible(">")
trait RasteringModule {
val rasteringModule: RasteringModule.Service[Any]
}
object RasteringModule {
trait Service[R] {
def raster(world: World, camera: Camera):
ZStream[R, RayTracerError, ColoredPixel]
}
World Rendering - Top Down
Rastering - Generate a stream of colored pixels
@accessible(">")
trait RasteringModule {
val rasteringModule: RasteringModule.Service[Any]
}
object RasteringModule {
trait Service[R] {
def raster(world: World, camera: Camera):
ZStream[R, RayTracerError, ColoredPixel]
}
World Rendering - Top Down
Rastering - Live
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
→ World module - Color per ray
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
→ World module - Color per ray
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
→ World module - Color per ray
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering Live - Module Dependency
trait LiveRasteringModule extends RasteringModule {
val cameraModule: CameraModule.Service[Any]
val worldModule: WorldModule.Service[Any]
val rasteringModule: Service[Any] = new Service[Any] {
def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = {
val pixels: Stream[Nothing, (Int, Int)] = ???
pixels.mapM{
case (px, py) =>
for {
ray <- cameraModule.rayForPixel(camera, px, py)
color <- worldModule.colorForRay(world, ray)
} yield data.ColoredPixel(Pixel(px, py), color)
}
}
}
}
World Rendering - Top Down
Rastering Live - Module Dependency
trait LiveRasteringModule extends RasteringModule {
val cameraModule: CameraModule.Service[Any]
val worldModule: WorldModule.Service[Any]
val rasteringModule: Service[Any] = new Service[Any] {
def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = {
val pixels: Stream[Nothing, (Int, Int)] = ???
pixels.mapM{
case (px, py) =>
for {
ray <- cameraModule.rayForPixel(camera, px, py)
color <- worldModule.colorForRay(world, ray)
} yield data.ColoredPixel(Pixel(px, py), color)
}
}
}
}
World Rendering - Top Down
Rastering Live - Module Dependency
trait LiveRasteringModule extends RasteringModule {
val cameraModule: CameraModule.Service[Any]
val worldModule: WorldModule.Service[Any]
val rasteringModule: Service[Any] = new Service[Any] {
def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = {
val pixels: Stream[Nothing, (Int, Int)] = ???
pixels.mapM{
case (px, py) =>
for {
ray <- cameraModule.rayForPixel(camera, px, py)
color <- worldModule.colorForRay(world, ray)
} yield data.ColoredPixel(Pixel(px, py), color)
}
}
}
}
Test LiveRasteringModule
1 - Define the method under test
val world = /* prepare a world */
val camera = /* prepare a camera */
val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] =
RasteringModule.>.raster(world, camera).runCollect
Test LiveRasteringModule
2 - Annotate the modules as mockable
import zio.macros.annotation.mockable
@mockable
trait CameraModule { ... }
@mockable
trait WorldModule { ... }
Test LiveRasteringModule
3 - Build the expectations
val rayForPixelExp: Expectation[CameraModule, Nothing, Ray] =
(CameraModule.rayForPixel(equalTo((camera, 0, 0))) returns value(r1)) *>
(CameraModule.rayForPixel(equalTo((camera, 0, 1))) returns value(r2))
val colorForRayExp: Expectation[WorldModule, Nothing, Color] =
(WorldModule.colorForRay(equalTo((world, r1, 5))) returns value(Color.red)) *>
(WorldModule.colorForRay(equalTo((world, r2, 5))) returns value(Color.green))
Test LiveRasteringModule
4 - Build the environment for the code under test
val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] =
RasteringModule.>.raster(world, camera).runCollect
appUnderTest.provideManaged(
worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) =>
new LiveRasteringModule {
val cameraModule: CameraModule.Service[Any] = cm.cameraModule
val worldModule: WorldModule.Service[Any] = wm.worldModule
}
}
)
Test LiveRasteringModule
4 - Build the environment for the code under test
val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] =
RasteringModule.>.raster(world, camera).runCollect
appUnderTest.provideManaged(
worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) =>
new LiveRasteringModule {
val cameraModule: CameraModule.Service[Any] = cm.cameraModule
val worldModule: WorldModule.Service[Any] = wm.worldModule
}
}
)
Test LiveRasteringModule
5 - Assert on the results
assert(res, equalTo(List(
ColoredPixel(Pixel(0, 0), Color.red),
ColoredPixel(Pixel(0, 1), Color.green),
ColoredPixel(Pixel(1, 0), Color.blue),
ColoredPixel(Pixel(1, 1), Color.white),
))
)
Test LiveRasteringModule
suite("LiveRasteringModule") {
testM("raster should rely on cameraModule and world module") {
for {
(worldModuleExp, cameraModuleExp) <- RasteringModuleMocks.mockExpectations(world, camera)
res <- appUnderTest.provideManaged(
worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) =>
new LiveRasteringModule {
val cameraModule: CameraModule.Service[Any] = cm.cameraModule
val worldModule: WorldModule.Service[Any] = wm.worldModule
}
}
)
} yield assert(res, equalTo(List(
ColoredPixel(Pixel(0, 0), Color.red),
ColoredPixel(Pixel(0, 1), Color.green)
)))
}
}
Test
Takeaway: Implement and test every layer only in terms of the
immediately underlying layer
Live CameraModule
trait Live extends CameraModule {
val aTModule: ATModule.Service[Any]
/* implementation */
}
Live WorldModule
WorldTopologyModule
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldModule: Service[Any] = new Service[Any] {
def colorForRay(
world: World, ray: Ray, remaining: Ref[Int]
): ZIO[Any, RayTracerError, Color] = {
/* use other modules */
}
}
}
Live WorldModule
WorldTopologyModule
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldModule: Service[Any] = new Service[Any] {
def colorForRay(
world: World, ray: Ray, remaining: Ref[Int]
): ZIO[Any, RayTracerError, Color] = {
/* use other modules */
}
}
}
Live WorldModule
WorldHitCompsModule
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val worldModule: Service[Any] = new Service[Any] {
def colorForRay(
world: World, ray: Ray, remaining: Ref[Int]
): ZIO[Any, RayTracerError, Color] = {
/* use other modules */
}
}
}
Live WorldModule
WorldHitCompsModule
case class HitComps(
shape: Shape, hitPt: Pt, normalV: Vec, eyeV: Vec, rayReflectV: Vec
)
trait Service[R] {
def hitComps(
ray: Ray, hit: Intersection, intersections: List[Intersection]
): ZIO[R, Nothing, HitComps]
}
Live WorldModule
PhongReflectionModule
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldModule: Service[Any] = new Service[Any] {
def colorForRay(
world: World, ray: Ray, remaining: Ref[Int]
): ZIO[Any, RayTracerError, Color] = {
/* use other modules */
}
}
}
Live WorldModule
PhongReflectionModule
case class PhongComponents(
ambient: Color, diffuse: Color, reflective: Color
) {
def toColor: Color = ambient + diffuse + reflective
}
trait BlackWhite extends PhongReflectionModule {
val phongReflectionModule: Service[Any] =
new Service[Any] {
def lighting(
pointLight: PointLight, hitComps: HitComps, inShadow: Boolean
): UIO[PhongComponents] = {
if (inShadow) UIO(PhongComponents.allBlack)
else UIO(PhongComponents.allWhite)
}
}
}
Live WorldModule
PhongReflectionModule
case class PhongComponents(
ambient: Color, diffuse: Color, reflective: Color
) {
def toColor: Color = ambient + diffuse + reflective
}
trait BlackWhite extends PhongReflectionModule {
val phongReflectionModule: Service[Any] =
new Service[Any] {
def lighting(
pointLight: PointLight, hitComps: HitComps, inShadow: Boolean
): UIO[PhongComponents] = {
if (inShadow) UIO(PhongComponents.allBlack)
else UIO(PhongComponents.allWhite)
}
}
}
Display the first canvas / 1
def drawOnCanvasWithCamera(world: World, camera: Camera, canvas: Canvas) =
for {
coloredPointsStream <- RasteringModule.>.raster(world, camera)
_ <- coloredPointsStream.mapM(cp => canvas.update(cp)).run(Sink.drain)
} yield ()
def program(viewFrom: Pt) =
for {
camera <- cameraFor(viewFrom: Pt)
w <- world
canvas <- Canvas.create()
_ <- drawOnCanvasWithCamera(w, camera, canvas)
_ <- CanvasSerializer.>.serialize(canvas, 255)
} yield ()
Display the first canvas / 2
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(
new CanvasSerializer.PNGCanvasSerializer
with RasteringModule.ChunkRasteringModule
with ATModule.Live
)
// Members declared in zio.blocking.Blocking
// [error] val blocking: zio.blocking.Blocking.Service[Any] = ???
// [error]
// [error] // Members declared in modules.RasteringModule.ChunkRasteringModule
// [error] val cameraModule: modules.CameraModule.Service[Any] = ???
// [error] val worldModule: modules.WorldModule.Service[Any] = ???
// [error]
// [error] // Members declared in geometry.affine.ATModule.Live
// [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
Display the first canvas / 2
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(
new CanvasSerializer.PNGCanvasSerializer
with RasteringModule.ChunkRasteringModule
with ATModule.Live
)
// Members declared in zio.blocking.Blocking
// [error] val blocking: zio.blocking.Blocking.Service[Any] = ???
// [error]
// [error] // Members declared in modules.RasteringModule.ChunkRasteringModule
// [error] val cameraModule: modules.CameraModule.Service[Any] = ???
// [error] val worldModule: modules.WorldModule.Service[Any] = ???
// [error]
// [error] // Members declared in geometry.affine.ATModule.Live
// [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
Display the first canvas / 2
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(
new CanvasSerializer.PNGCanvasSerializer
with RasteringModule.ChunkRasteringModule
with ATModule.Live
)
// Members declared in zio.blocking.Blocking
// [error] val blocking: zio.blocking.Blocking.Service[Any] = ???
// [error]
// [error] // Members declared in modules.RasteringModule.ChunkRasteringModule
// [error] val cameraModule: modules.CameraModule.Service[Any] = ???
// [error] val worldModule: modules.WorldModule.Service[Any] = ???
// [error]
// [error] // Members declared in geometry.affine.ATModule.Live
// [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
Display the first canvas / 3
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(
new CanvasSerializer.PNGCanvasSerializer
with RasteringModule.ChunkRasteringModule
with ATModule.Live
with CameraModule.Live
with MatrixModule.BreezeLive
with WorldModule.Live
)
)
// [error] // Members declared in io.tuliplogic.raytracer.ops.model.modules.WorldModule.Live
// [error] val phongReflectionModule: io.tuliplogic.raytracer.ops.model.modules.PhongReflectionModule.Service[Any] = ???
// [error] val worldHitCompsModule: io.tuliplogic.raytracer.ops.model.modules.WorldHitCompsModule.Service[Any] = ???
// [error] val worldReflectionModule: io.tuliplogic.raytracer.ops.model.modules.WorldReflectionModule.Service[Any] = ???
// [error] val worldRefractionModule: io.tuliplogic.raytracer.ops.model.modules.WorldRefractionModule.Service[Any] = ???
// [error] val worldTopologyModule: io.tuliplogic.raytracer.ops.model.modules.WorldTopologyModule.Service[Any] = ???
Display the first canvas - /4
Group modules in trait
type BasicModules =
NormalReflectModule.Live
with RayModule.Live
with ATModule.Live
with MatrixModule.BreezeLive
with WorldModule.Live
with WorldTopologyModule.Live
with WorldHitCompsModule.Live
with CameraModule.Live
with RasteringModule.Live
with Blocking.Live
Display the first canvas - /4
Group modules in trait
type BasicModules =
NormalReflectModule.Live
with RayModule.Live
with ATModule.Live
with MatrixModule.BreezeLive
with WorldModule.Live
with WorldTopologyModule.Live
with WorldHitCompsModule.Live
with CameraModule.Live
with RasteringModule.Live
with Blocking.Live
Display the first canvas - /5
Group modules
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(new BasicModules with PhongReflectionModule.BlackWhite)
Display the first canvas - /6
Group modules
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
def run(args: List[String]): ZIO[ZEnv, Nothing, Int] =
ZIO.traverse(-18 to -6)(z => program(Pt(2, 2, z))
.provide(
new BasicModules with PhongReflectionModule.BlackWhite
)
).foldM(err =>
console.putStrLn(s"Execution failed with: $err").as(1),
_ => UIO.succeed(0)
)
Live PhongReflectionModule
With LightDiffusion
trait Live extends PhongReflectionModule {
val aTModule: ATModule.Service[Any]
val normalReflectModule: NormalReflectModule.Service[Any]
val lightDiffusionModule: LightDiffusionModule.Service[Any]
}
Live PhongReflectionModule
With LightDiffusion
program(Pt(2, 2, -10))
.provide(
new BasicModules
with PhongReflectionModule.Live
// with PhongReflectionModule.BlackWhite
)
Live PhongReflectionModule
With LightDiffusion
program(Pt(2, 2, -10))
.provide(
new BasicModules
with PhongReflectionModule.Live
// with PhongReflectionModule.BlackWhite
)
Live PhongReflectionModule
With LightDiffusion
program(Pt(2, 2, -10))
.provide(
new BasicModules
with PhongReflectionModule.Live
// with PhongReflectionModule.BlackWhite
)
Live PhongReflectionModule
With LightDiffusion
program(Pt(2, 2, -10))
.provide(
new BasicModules
with PhongReflectionModule.Live
// with PhongReflectionModule.BlackWhite
)
Reflect the ligtht source
Describe material properties
case class Material(
color: Color, // the basic color
ambient: Double, // ∈ [0, 1]
diffuse: Double, // ∈ [0, 1]
specular: Double, // ∈ [0, 1]
shininess: Double, // ∈ [10, 200]
)
Live PhongReflectionModule
With LightDiffusion and LightReflection
trait Live extends PhongReflectionModule {
val aTModule: ATModule.Service[Any]
val normalReflectModule: NormalReflectModule.Service[Any]
val lightDiffusionModule: LightDiffusionModule.Service[Any]
val lightReflectionModule: LightReflectionModule.Service[Any]
}
Spheres reflect light source
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with PhongReflectionModule.Live
with LightReflectionModule.Live
}
Reflective surfaces
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
Reflective surfaces
Use WorldReflection
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
val worldModule: Service[Any] =
new Service[Any] {
def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] =
{
/* ... */
for {
color <- /* standard computation of color */
reflectedColor <- worldReflectionModule.reflectedColor(world, hc)
} yield color + reflectedColor
}
}
}
Reflective surfaces
Use WorldReflection
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
val worldModule: Service[Any] =
new Service[Any] {
def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] =
{
/* ... */
for {
color <- /* standard computation of color */
reflectedColor <- worldReflectionModule.reflectedColor(world, hc)
} yield color + reflectedColor
}
}
}
Reflective surfaces
Use WorldReflection
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
val worldModule: Service[Any] =
new Service[Any] {
def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] =
{
/* ... */
for {
color <- /* standard computation of color */
reflectedColor <- worldReflectionModule.reflectedColor(world, hc)
} yield color + reflectedColor
}
}
}
Reflective surfaces
Use WorldReflection
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
val worldModule: Service[Any] =
new Service[Any] {
def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] =
{
/* ... */
for {
color <- /* standard computation of color */
reflectedColor <- worldReflectionModule.reflectedColor(world, hc)
} yield color + reflectedColor
}
}
}
Implement WorldReflection
Live
trait Live extends WorldReflectionModule {
val worldModule: WorldModule.Service[Any]
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(world: World, hitComps: HitComps, remaining: Int):
ZIO[Any, RayTracerError, Color] =
if (hitComps.shape.material.reflective == 0) {
UIO(Color.black)
} else {
val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV)
worldModule.colorForRay(world, reflRay, remaining).map(c =>
c * hitComps.shape.material.reflective
)
}
}
}
Implement WorldReflection
Live
trait Live extends WorldReflectionModule {
val worldModule: WorldModule.Service[Any]
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(world: World, hitComps: HitComps, remaining: Int):
ZIO[Any, RayTracerError, Color] =
if (hitComps.shape.material.reflective == 0) {
UIO(Color.black)
} else {
val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV)
worldModule.colorForRay(world, reflRay, remaining).map(c =>
c * hitComps.shape.material.reflective
)
}
}
}
Implement WorldReflection
Live
trait Live extends WorldReflectionModule {
val worldModule: WorldModule.Service[Any]
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(world: World, hitComps: HitComps, remaining: Int):
ZIO[Any, RayTracerError, Color] =
if (hitComps.shape.material.reflective == 0) {
UIO(Color.black)
} else {
val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV)
worldModule.colorForRay(world, reflRay, remaining).map(c =>
c * hitComps.shape.material.reflective
)
}
}
}
Implement WorldReflection
Live
trait Live extends WorldReflectionModule {
val worldModule: WorldModule.Service[Any]
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(world: World, hitComps: HitComps, remaining: Int):
ZIO[Any, RayTracerError, Color] =
if (hitComps.shape.material.reflective == 0) {
UIO(Color.black)
} else {
val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV)
worldModule.colorForRay(world, reflRay, remaining).map(c =>
c * hitComps.shape.material.reflective
)
}
}
}
Reflective surfaces
Circular dependency
Implement WorldReflection Dummy
trait NoReflection extends WorldReflectionModule {
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(
world: World,
hitComps: HitComps,
remaining: Int
): ZIO[Any, RayTracerError, Color] = UIO.succeed(Color.black)
}
}
Spheres
→ Red: reflective = 0.9
→ Green/white: reflective = 0.6
→ Blue: reflective = 0.9, transparency: 1
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with PhongReflectionModule.Live
with WorldReflectionModule.NoReflection
}
⠀
⠀
⠀
⠀
⠀
⠀⠀
Spheres
→ Red: reflective = 0.9
→ Green/white: reflective = 0.6
→ Blue: reflective = 0.9, transparency: 1
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with PhongReflectionModule.Live
with WorldReflectionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
}
⠀
⠀
⠀
⠀
⠀
⠀⠀
Spheres
→ Red: reflective = 0.9
→ Green/white: reflective = 0.6
→ Blue: reflective = 0.9,
transparency: 1
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with PhongReflectionModule.Live
with WorldReflectionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
with WorldRefractionModule.NoRefraction⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
}
Spheres
→ Red: reflective = 0.9
→ Green/white: reflective = 0.6
→ Blue: reflective = 0.9,
transparency: 1
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with PhongReflectionModule.Live
with WorldReflectionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
with WorldRefractionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
}
⠀
⠀
⠀
⠀
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
→ Do not abuse typeclasses
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
→ Do not abuse typeclasses
→ Can group capabilities
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
→ Do not abuse typeclasses
→ Can group capabilities
→ Can provide capabilities one at a time - provideSome
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
→ Do not abuse typeclasses
→ Can group capabilities
→ Can provide capabilities one at a time - provideSome
→ Are not dependent on implicits (survive refactoring)
Conclusion - Environmental Effects
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
→ Macros help with boilerplate
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
→ Macros help with boilerplate
→ Compiler is your friend
#
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
→ Macros help with boilerplate
→ Compiler is your friend
#
→ Handle circular dependencies
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
→ Macros help with boilerplate
→ Compiler is your friend
#
→ Handle circular dependencies
→ Try it out!
%
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
→ Macros help with boilerplate
→ Compiler is your friend
#
→ Handle circular dependencies
→ Try it out!
%
→ Join ZIO Discord channel
Thank you!
@pierangelocecc
https://github.com/pierangeloc/ray-tracer-zio

More Related Content

What's hot

How to extend map? Or why we need collections redesign? - Scalar 2017
How to extend map? Or why we need collections redesign? - Scalar 2017How to extend map? Or why we need collections redesign? - Scalar 2017
How to extend map? Or why we need collections redesign? - Scalar 2017
Szymon Matejczyk
 
A tour of Python
A tour of PythonA tour of Python
A tour of Python
Aleksandar Veselinovic
 
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
John De Goes
 
The Ring programming language version 1.3 book - Part 25 of 88
The Ring programming language version 1.3 book - Part 25 of 88The Ring programming language version 1.3 book - Part 25 of 88
The Ring programming language version 1.3 book - Part 25 of 88
Mahmoud Samir Fayed
 
SPL 6.1 | Advanced problems on Operators and Math.h function in C
SPL 6.1 | Advanced problems on Operators and Math.h function in CSPL 6.1 | Advanced problems on Operators and Math.h function in C
SPL 6.1 | Advanced problems on Operators and Math.h function in C
Mohammad Imam Hossain
 
ECMAScript 5: Новое в JavaScript
ECMAScript 5: Новое в JavaScriptECMAScript 5: Новое в JavaScript
Advance java
Advance javaAdvance java
Advance java
Vivek Kumar Sinha
 
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
Wanbok Choi
 
미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정
SeungChul Kang
 
The Ring programming language version 1.10 book - Part 33 of 212
The Ring programming language version 1.10 book - Part 33 of 212The Ring programming language version 1.10 book - Part 33 of 212
The Ring programming language version 1.10 book - Part 33 of 212
Mahmoud Samir Fayed
 
The Uncertain Enterprise
The Uncertain EnterpriseThe Uncertain Enterprise
The Uncertain Enterprise
ClarkTony
 
Concurrent Application Development using Scala
Concurrent Application Development using ScalaConcurrent Application Development using Scala
Concurrent Application Development using Scala
Siarhiej Siemianchuk
 
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftHow to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftGiordano Scalzo
 
ADA FILE
ADA FILEADA FILE
ADA FILE
Gaurav Singh
 
Streams for (Co)Free!
Streams for (Co)Free!Streams for (Co)Free!
Streams for (Co)Free!
John De Goes
 
Docopt
DocoptDocopt
Docopt
René Ribaud
 
Solutionsfor co2 C Programs for data structures
Solutionsfor co2 C Programs for data structuresSolutionsfor co2 C Programs for data structures
Solutionsfor co2 C Programs for data structures
Lakshmi Sarvani Videla
 

What's hot (20)

How to extend map? Or why we need collections redesign? - Scalar 2017
How to extend map? Or why we need collections redesign? - Scalar 2017How to extend map? Or why we need collections redesign? - Scalar 2017
How to extend map? Or why we need collections redesign? - Scalar 2017
 
A tour of Python
A tour of PythonA tour of Python
A tour of Python
 
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
 
The Ring programming language version 1.3 book - Part 25 of 88
The Ring programming language version 1.3 book - Part 25 of 88The Ring programming language version 1.3 book - Part 25 of 88
The Ring programming language version 1.3 book - Part 25 of 88
 
SPL 6.1 | Advanced problems on Operators and Math.h function in C
SPL 6.1 | Advanced problems on Operators and Math.h function in CSPL 6.1 | Advanced problems on Operators and Math.h function in C
SPL 6.1 | Advanced problems on Operators and Math.h function in C
 
ECMAScript 5: Новое в JavaScript
ECMAScript 5: Новое в JavaScriptECMAScript 5: Новое в JavaScript
ECMAScript 5: Новое в JavaScript
 
Advance java
Advance javaAdvance java
Advance java
 
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
 
미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정
 
The Ring programming language version 1.10 book - Part 33 of 212
The Ring programming language version 1.10 book - Part 33 of 212The Ring programming language version 1.10 book - Part 33 of 212
The Ring programming language version 1.10 book - Part 33 of 212
 
The Uncertain Enterprise
The Uncertain EnterpriseThe Uncertain Enterprise
The Uncertain Enterprise
 
Concurrent Application Development using Scala
Concurrent Application Development using ScalaConcurrent Application Development using Scala
Concurrent Application Development using Scala
 
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftHow to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
 
ProgrammingwithGOLang
ProgrammingwithGOLangProgrammingwithGOLang
ProgrammingwithGOLang
 
Struct examples
Struct examplesStruct examples
Struct examples
 
ADA FILE
ADA FILEADA FILE
ADA FILE
 
Streams for (Co)Free!
Streams for (Co)Free!Streams for (Co)Free!
Streams for (Co)Free!
 
Docopt
DocoptDocopt
Docopt
 
Solutionsfor co2 C Programs for data structures
Solutionsfor co2 C Programs for data structuresSolutionsfor co2 C Programs for data structures
Solutionsfor co2 C Programs for data structures
 
C programs
C programsC programs
C programs
 

Similar to Ray Tracing with ZIO

Functional programming in C++ LambdaNsk
Functional programming in C++ LambdaNskFunctional programming in C++ LambdaNsk
Functional programming in C++ LambdaNsk
Alexander Granin
 
Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
Hang Zhao
 
Lambdaconf2019 talk
Lambdaconf2019 talkLambdaconf2019 talk
Lambdaconf2019 talk
Dominic Egger
 
Building Enigma with State Monad & Lens
Building Enigma with State Monad & LensBuilding Enigma with State Monad & Lens
Building Enigma with State Monad & Lens
Timothy Perrett
 
Functional "Life": parallel cellular automata and comonads
Functional "Life": parallel cellular automata and comonadsFunctional "Life": parallel cellular automata and comonads
Functional "Life": parallel cellular automata and comonads
Alexander Granin
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator Pattern
Eric Torreborre
 
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)
Eric Torreborre
 
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Sergey Platonov
 
Monads - Dublin Scala meetup
Monads - Dublin Scala meetupMonads - Dublin Scala meetup
Monads - Dublin Scala meetup
Mikhail Girkin
 
Fourier project presentation
Fourier project  presentationFourier project  presentation
Fourier project presentation
志璿 楊
 
C++ extension methods
C++ extension methodsC++ extension methods
C++ extension methods
phil_nash
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is coming
Kirill Rozov
 
The STL
The STLThe STL
The STL
adil raja
 
Monads and friends demystified
Monads and friends demystifiedMonads and friends demystified
Monads and friends demystified
Alessandro Lacava
 
Egor Bogatov - .NET Core intrinsics and other micro-optimizations
Egor Bogatov - .NET Core intrinsics and other micro-optimizationsEgor Bogatov - .NET Core intrinsics and other micro-optimizations
Egor Bogatov - .NET Core intrinsics and other micro-optimizations
Egor Bogatov
 
Modular Module Systems
Modular Module SystemsModular Module Systems
Modular Module Systemsleague
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Codemotion
 
Declare Your Language: Transformation by Strategic Term Rewriting
Declare Your Language: Transformation by Strategic Term RewritingDeclare Your Language: Transformation by Strategic Term Rewriting
Declare Your Language: Transformation by Strategic Term Rewriting
Eelco Visser
 

Similar to Ray Tracing with ZIO (20)

Functional programming in C++ LambdaNsk
Functional programming in C++ LambdaNskFunctional programming in C++ LambdaNsk
Functional programming in C++ LambdaNsk
 
Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
 
Lambdaconf2019 talk
Lambdaconf2019 talkLambdaconf2019 talk
Lambdaconf2019 talk
 
Building Enigma with State Monad & Lens
Building Enigma with State Monad & LensBuilding Enigma with State Monad & Lens
Building Enigma with State Monad & Lens
 
Functional "Life": parallel cellular automata and comonads
Functional "Life": parallel cellular automata and comonadsFunctional "Life": parallel cellular automata and comonads
Functional "Life": parallel cellular automata and comonads
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator Pattern
 
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)
 
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
 
Monads - Dublin Scala meetup
Monads - Dublin Scala meetupMonads - Dublin Scala meetup
Monads - Dublin Scala meetup
 
Fourier project presentation
Fourier project  presentationFourier project  presentation
Fourier project presentation
 
C++ extension methods
C++ extension methodsC++ extension methods
C++ extension methods
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is coming
 
The STL
The STLThe STL
The STL
 
Monads and friends demystified
Monads and friends demystifiedMonads and friends demystified
Monads and friends demystified
 
Egor Bogatov - .NET Core intrinsics and other micro-optimizations
Egor Bogatov - .NET Core intrinsics and other micro-optimizationsEgor Bogatov - .NET Core intrinsics and other micro-optimizations
Egor Bogatov - .NET Core intrinsics and other micro-optimizations
 
Modular Module Systems
Modular Module SystemsModular Module Systems
Modular Module Systems
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
 
Declare Your Language: Transformation by Strategic Term Rewriting
Declare Your Language: Transformation by Strategic Term RewritingDeclare Your Language: Transformation by Strategic Term Rewriting
Declare Your Language: Transformation by Strategic Term Rewriting
 
Oop objects_classes
Oop objects_classesOop objects_classes
Oop objects_classes
 
Fusion_Class
Fusion_ClassFusion_Class
Fusion_Class
 

Recently uploaded

PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
ControlCase
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
Elena Simperl
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
Product School
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
OnBoard
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Tobias Schneck
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Frank van Harmelen
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
Safe Software
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Ramesh Iyer
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Product School
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
Product School
 

Recently uploaded (20)

PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
 

Ray Tracing with ZIO

  • 2. ZIO-101 ZIO[-R, +E, +A] ⬇ R => IO[Either[E, A]] ⬇ R => Either[E, A]
  • 4. Ray tracing → World (spheres), light source, camera
  • 5. Ray tracing → World (spheres), light source, camera → Incident rays
  • 6. Ray tracing → World (spheres), light source, camera → Incident rays → Reflected rays
  • 7. Ray tracing → World (spheres), light source, camera → Incident rays → Reflected rays → Discarded rays → Canvas
  • 8. Ray tracing → World (spheres), light source, camera → Incident rays → Reflected rays → Discarded rays → Canvas → Colored pixels
  • 9. Ray tracing → World (spheres), light source, camera → Incident rays → Reflected rays → Discarded rays → Canvas → Colored pixels
  • 11. Ray tracing Options: 1. Compute all the rays (and discard most of them)
  • 12. Ray tracing Options: 1. Compute all the rays (and discard most of them) 2. Compute only the rays outgoing from the camera through the canvas, and determine how they behave on the surfaces
  • 13. Ray case class Ray(origin: Pt, direction: Vec) { def positionAt(t: Double): Pt = origin + (direction * t) }
  • 14. Transformations Module trait AT /* Module */ trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor */ object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } }
  • 15. Transformations Module trait AT /* Module */ trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor */ object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } }
  • 16. Transformations Module trait AT /* Module */ trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor */ object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } }
  • 17. Transformations Module trait AT /* Module */ trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor */ object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } }
  • 18. Transformations Module import zio.macros.annotation.accessible trait AT /* Module */ @accessible(">") trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor is generated object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } */ }
  • 19. Transformations Module trait AT /* Module */ @accessible(">") trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } }
  • 20. Transformations Module val rotatedPt = for { rotateX <- ATModule.>.rotateX(math.Pi / 2) _ <- Log.>.info("rotated of π/2") res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1)) } yield res
  • 21. Transformations Module val rotatedPt: ZIO[ATModule with Log, ATError, Pt] = for { rotateX <- ATModule.>.rotateX(math.Pi / 2) _ <- Log.>.info("rotated of π/2") res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1)) } yield res
  • 22. Transformations Module - Live val rotated: ZIO[ATModule, ATError, Vec] = for { rotateX <- ATModule.>.rotateX(math.Pi/2) res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1)) } yield res
  • 23. Transformations Module - Live val rotated: ZIO[ATModule, ATError, Vec] = for { rotateX <- ATModule.>.rotateX(math.Pi/2) res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1)) } yield res → Vec(x, y, z)
  • 24. Transformations Module - Live val rotated: ZIO[ATModule, ATError, Vec] = for { rotateX <- ATModule.>.rotateX(math.Pi/2) res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1)) } yield res → Vec(x, y, z) → Pt(x, y, z)⠀
  • 25. Transformations Module - Live val rotated: ZIO[ATModule, ATError, Vec] = for { rotateX <- ATModule.>.rotateX(math.Pi/2) res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1)) } yield res → Vec(x, y, z) → Pt(x, y, z)⠀ → ⠀
  • 27. Camera object Camera { def make( viewFrom: Pt, viewTo: Pt, upDirection: Vec, visualAngleRad: Double, hRes: Int, vRes: Int): ZIO[ATModule, AlgebraicError, Camera] = worldTransformation(viewFrom, viewTo, upDirection).map { worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf) }
  • 28. Camera object Camera { def make( viewFrom: Pt, viewTo: Pt, upDirection: Vec, visualAngleRad: Double, hRes: Int, vRes: Int): ZIO[ATModule, AlgebraicError, Camera] = worldTransformation(viewFrom, viewTo, upDirection).map { worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf) }
  • 29. World sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 30. World → Sphere.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 31. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 32. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 33. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 34. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 35. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 36. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 37. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 38. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 39. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 40. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 41. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape]) → Everything requires ATModule
  • 42. World Rendering - Top Down Rastering - Generate a stream of colored pixels @accessible(">") trait RasteringModule { val rasteringModule: RasteringModule.Service[Any] } object RasteringModule { trait Service[R] { def raster(world: World, camera: Camera): ZStream[R, RayTracerError, ColoredPixel] }
  • 43. World Rendering - Top Down Rastering - Generate a stream of colored pixels @accessible(">") trait RasteringModule { val rasteringModule: RasteringModule.Service[Any] } object RasteringModule { trait Service[R] { def raster(world: World, camera: Camera): ZStream[R, RayTracerError, ColoredPixel] }
  • 44. World Rendering - Top Down Rastering - Generate a stream of colored pixels @accessible(">") trait RasteringModule { val rasteringModule: RasteringModule.Service[Any] } object RasteringModule { trait Service[R] { def raster(world: World, camera: Camera): ZStream[R, RayTracerError, ColoredPixel] }
  • 45. World Rendering - Top Down Rastering - Live object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 46. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 47. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 48. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 49. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 50. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } → World module - Color per ray object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 51. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } → World module - Color per ray object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 52. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } → World module - Color per ray object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 53. World Rendering - Top Down Rastering Live - Module Dependency trait LiveRasteringModule extends RasteringModule { val cameraModule: CameraModule.Service[Any] val worldModule: WorldModule.Service[Any] val rasteringModule: Service[Any] = new Service[Any] { def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = { val pixels: Stream[Nothing, (Int, Int)] = ??? pixels.mapM{ case (px, py) => for { ray <- cameraModule.rayForPixel(camera, px, py) color <- worldModule.colorForRay(world, ray) } yield data.ColoredPixel(Pixel(px, py), color) } } } }
  • 54. World Rendering - Top Down Rastering Live - Module Dependency trait LiveRasteringModule extends RasteringModule { val cameraModule: CameraModule.Service[Any] val worldModule: WorldModule.Service[Any] val rasteringModule: Service[Any] = new Service[Any] { def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = { val pixels: Stream[Nothing, (Int, Int)] = ??? pixels.mapM{ case (px, py) => for { ray <- cameraModule.rayForPixel(camera, px, py) color <- worldModule.colorForRay(world, ray) } yield data.ColoredPixel(Pixel(px, py), color) } } } }
  • 55. World Rendering - Top Down Rastering Live - Module Dependency trait LiveRasteringModule extends RasteringModule { val cameraModule: CameraModule.Service[Any] val worldModule: WorldModule.Service[Any] val rasteringModule: Service[Any] = new Service[Any] { def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = { val pixels: Stream[Nothing, (Int, Int)] = ??? pixels.mapM{ case (px, py) => for { ray <- cameraModule.rayForPixel(camera, px, py) color <- worldModule.colorForRay(world, ray) } yield data.ColoredPixel(Pixel(px, py), color) } } } }
  • 56. Test LiveRasteringModule 1 - Define the method under test val world = /* prepare a world */ val camera = /* prepare a camera */ val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] = RasteringModule.>.raster(world, camera).runCollect
  • 57. Test LiveRasteringModule 2 - Annotate the modules as mockable import zio.macros.annotation.mockable @mockable trait CameraModule { ... } @mockable trait WorldModule { ... }
  • 58. Test LiveRasteringModule 3 - Build the expectations val rayForPixelExp: Expectation[CameraModule, Nothing, Ray] = (CameraModule.rayForPixel(equalTo((camera, 0, 0))) returns value(r1)) *> (CameraModule.rayForPixel(equalTo((camera, 0, 1))) returns value(r2)) val colorForRayExp: Expectation[WorldModule, Nothing, Color] = (WorldModule.colorForRay(equalTo((world, r1, 5))) returns value(Color.red)) *> (WorldModule.colorForRay(equalTo((world, r2, 5))) returns value(Color.green))
  • 59. Test LiveRasteringModule 4 - Build the environment for the code under test val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] = RasteringModule.>.raster(world, camera).runCollect appUnderTest.provideManaged( worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) => new LiveRasteringModule { val cameraModule: CameraModule.Service[Any] = cm.cameraModule val worldModule: WorldModule.Service[Any] = wm.worldModule } } )
  • 60. Test LiveRasteringModule 4 - Build the environment for the code under test val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] = RasteringModule.>.raster(world, camera).runCollect appUnderTest.provideManaged( worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) => new LiveRasteringModule { val cameraModule: CameraModule.Service[Any] = cm.cameraModule val worldModule: WorldModule.Service[Any] = wm.worldModule } } )
  • 61. Test LiveRasteringModule 5 - Assert on the results assert(res, equalTo(List( ColoredPixel(Pixel(0, 0), Color.red), ColoredPixel(Pixel(0, 1), Color.green), ColoredPixel(Pixel(1, 0), Color.blue), ColoredPixel(Pixel(1, 1), Color.white), )) )
  • 62. Test LiveRasteringModule suite("LiveRasteringModule") { testM("raster should rely on cameraModule and world module") { for { (worldModuleExp, cameraModuleExp) <- RasteringModuleMocks.mockExpectations(world, camera) res <- appUnderTest.provideManaged( worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) => new LiveRasteringModule { val cameraModule: CameraModule.Service[Any] = cm.cameraModule val worldModule: WorldModule.Service[Any] = wm.worldModule } } ) } yield assert(res, equalTo(List( ColoredPixel(Pixel(0, 0), Color.red), ColoredPixel(Pixel(0, 1), Color.green) ))) } }
  • 63. Test Takeaway: Implement and test every layer only in terms of the immediately underlying layer
  • 64.
  • 65. Live CameraModule trait Live extends CameraModule { val aTModule: ATModule.Service[Any] /* implementation */ }
  • 66. Live WorldModule WorldTopologyModule trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay( world: World, ray: Ray, remaining: Ref[Int] ): ZIO[Any, RayTracerError, Color] = { /* use other modules */ } } }
  • 67. Live WorldModule WorldTopologyModule trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay( world: World, ray: Ray, remaining: Ref[Int] ): ZIO[Any, RayTracerError, Color] = { /* use other modules */ } } }
  • 68. Live WorldModule WorldHitCompsModule trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay( world: World, ray: Ray, remaining: Ref[Int] ): ZIO[Any, RayTracerError, Color] = { /* use other modules */ } } }
  • 69. Live WorldModule WorldHitCompsModule case class HitComps( shape: Shape, hitPt: Pt, normalV: Vec, eyeV: Vec, rayReflectV: Vec ) trait Service[R] { def hitComps( ray: Ray, hit: Intersection, intersections: List[Intersection] ): ZIO[R, Nothing, HitComps] }
  • 70. Live WorldModule PhongReflectionModule trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay( world: World, ray: Ray, remaining: Ref[Int] ): ZIO[Any, RayTracerError, Color] = { /* use other modules */ } } }
  • 71. Live WorldModule PhongReflectionModule case class PhongComponents( ambient: Color, diffuse: Color, reflective: Color ) { def toColor: Color = ambient + diffuse + reflective } trait BlackWhite extends PhongReflectionModule { val phongReflectionModule: Service[Any] = new Service[Any] { def lighting( pointLight: PointLight, hitComps: HitComps, inShadow: Boolean ): UIO[PhongComponents] = { if (inShadow) UIO(PhongComponents.allBlack) else UIO(PhongComponents.allWhite) } } }
  • 72. Live WorldModule PhongReflectionModule case class PhongComponents( ambient: Color, diffuse: Color, reflective: Color ) { def toColor: Color = ambient + diffuse + reflective } trait BlackWhite extends PhongReflectionModule { val phongReflectionModule: Service[Any] = new Service[Any] { def lighting( pointLight: PointLight, hitComps: HitComps, inShadow: Boolean ): UIO[PhongComponents] = { if (inShadow) UIO(PhongComponents.allBlack) else UIO(PhongComponents.allWhite) } } }
  • 73. Display the first canvas / 1 def drawOnCanvasWithCamera(world: World, camera: Camera, canvas: Canvas) = for { coloredPointsStream <- RasteringModule.>.raster(world, camera) _ <- coloredPointsStream.mapM(cp => canvas.update(cp)).run(Sink.drain) } yield () def program(viewFrom: Pt) = for { camera <- cameraFor(viewFrom: Pt) w <- world canvas <- Canvas.create() _ <- drawOnCanvasWithCamera(w, camera, canvas) _ <- CanvasSerializer.>.serialize(canvas, 255) } yield ()
  • 74. Display the first canvas / 2 def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide( new CanvasSerializer.PNGCanvasSerializer with RasteringModule.ChunkRasteringModule with ATModule.Live ) // Members declared in zio.blocking.Blocking // [error] val blocking: zio.blocking.Blocking.Service[Any] = ??? // [error] // [error] // Members declared in modules.RasteringModule.ChunkRasteringModule // [error] val cameraModule: modules.CameraModule.Service[Any] = ??? // [error] val worldModule: modules.WorldModule.Service[Any] = ??? // [error] // [error] // Members declared in geometry.affine.ATModule.Live // [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
  • 75. Display the first canvas / 2 def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide( new CanvasSerializer.PNGCanvasSerializer with RasteringModule.ChunkRasteringModule with ATModule.Live ) // Members declared in zio.blocking.Blocking // [error] val blocking: zio.blocking.Blocking.Service[Any] = ??? // [error] // [error] // Members declared in modules.RasteringModule.ChunkRasteringModule // [error] val cameraModule: modules.CameraModule.Service[Any] = ??? // [error] val worldModule: modules.WorldModule.Service[Any] = ??? // [error] // [error] // Members declared in geometry.affine.ATModule.Live // [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
  • 76. Display the first canvas / 2 def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide( new CanvasSerializer.PNGCanvasSerializer with RasteringModule.ChunkRasteringModule with ATModule.Live ) // Members declared in zio.blocking.Blocking // [error] val blocking: zio.blocking.Blocking.Service[Any] = ??? // [error] // [error] // Members declared in modules.RasteringModule.ChunkRasteringModule // [error] val cameraModule: modules.CameraModule.Service[Any] = ??? // [error] val worldModule: modules.WorldModule.Service[Any] = ??? // [error] // [error] // Members declared in geometry.affine.ATModule.Live // [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
  • 77. Display the first canvas / 3 def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide( new CanvasSerializer.PNGCanvasSerializer with RasteringModule.ChunkRasteringModule with ATModule.Live with CameraModule.Live with MatrixModule.BreezeLive with WorldModule.Live ) ) // [error] // Members declared in io.tuliplogic.raytracer.ops.model.modules.WorldModule.Live // [error] val phongReflectionModule: io.tuliplogic.raytracer.ops.model.modules.PhongReflectionModule.Service[Any] = ??? // [error] val worldHitCompsModule: io.tuliplogic.raytracer.ops.model.modules.WorldHitCompsModule.Service[Any] = ??? // [error] val worldReflectionModule: io.tuliplogic.raytracer.ops.model.modules.WorldReflectionModule.Service[Any] = ??? // [error] val worldRefractionModule: io.tuliplogic.raytracer.ops.model.modules.WorldRefractionModule.Service[Any] = ??? // [error] val worldTopologyModule: io.tuliplogic.raytracer.ops.model.modules.WorldTopologyModule.Service[Any] = ???
  • 78. Display the first canvas - /4 Group modules in trait type BasicModules = NormalReflectModule.Live with RayModule.Live with ATModule.Live with MatrixModule.BreezeLive with WorldModule.Live with WorldTopologyModule.Live with WorldHitCompsModule.Live with CameraModule.Live with RasteringModule.Live with Blocking.Live
  • 79. Display the first canvas - /4 Group modules in trait type BasicModules = NormalReflectModule.Live with RayModule.Live with ATModule.Live with MatrixModule.BreezeLive with WorldModule.Live with WorldTopologyModule.Live with WorldHitCompsModule.Live with CameraModule.Live with RasteringModule.Live with Blocking.Live
  • 80. Display the first canvas - /5 Group modules def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide(new BasicModules with PhongReflectionModule.BlackWhite)
  • 81. Display the first canvas - /6 Group modules def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] def run(args: List[String]): ZIO[ZEnv, Nothing, Int] = ZIO.traverse(-18 to -6)(z => program(Pt(2, 2, z)) .provide( new BasicModules with PhongReflectionModule.BlackWhite ) ).foldM(err => console.putStrLn(s"Execution failed with: $err").as(1), _ => UIO.succeed(0) )
  • 82. Live PhongReflectionModule With LightDiffusion trait Live extends PhongReflectionModule { val aTModule: ATModule.Service[Any] val normalReflectModule: NormalReflectModule.Service[Any] val lightDiffusionModule: LightDiffusionModule.Service[Any] }
  • 83. Live PhongReflectionModule With LightDiffusion program(Pt(2, 2, -10)) .provide( new BasicModules with PhongReflectionModule.Live // with PhongReflectionModule.BlackWhite )
  • 84. Live PhongReflectionModule With LightDiffusion program(Pt(2, 2, -10)) .provide( new BasicModules with PhongReflectionModule.Live // with PhongReflectionModule.BlackWhite )
  • 85. Live PhongReflectionModule With LightDiffusion program(Pt(2, 2, -10)) .provide( new BasicModules with PhongReflectionModule.Live // with PhongReflectionModule.BlackWhite )
  • 86. Live PhongReflectionModule With LightDiffusion program(Pt(2, 2, -10)) .provide( new BasicModules with PhongReflectionModule.Live // with PhongReflectionModule.BlackWhite )
  • 87. Reflect the ligtht source Describe material properties case class Material( color: Color, // the basic color ambient: Double, // ∈ [0, 1] diffuse: Double, // ∈ [0, 1] specular: Double, // ∈ [0, 1] shininess: Double, // ∈ [10, 200] )
  • 88. Live PhongReflectionModule With LightDiffusion and LightReflection trait Live extends PhongReflectionModule { val aTModule: ATModule.Service[Any] val normalReflectModule: NormalReflectModule.Service[Any] val lightDiffusionModule: LightDiffusionModule.Service[Any] val lightReflectionModule: LightReflectionModule.Service[Any] }
  • 89. Spheres reflect light source program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with PhongReflectionModule.Live with LightReflectionModule.Live }
  • 90. Reflective surfaces trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any]
  • 91. Reflective surfaces Use WorldReflection trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] = { /* ... */ for { color <- /* standard computation of color */ reflectedColor <- worldReflectionModule.reflectedColor(world, hc) } yield color + reflectedColor } } }
  • 92. Reflective surfaces Use WorldReflection trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] = { /* ... */ for { color <- /* standard computation of color */ reflectedColor <- worldReflectionModule.reflectedColor(world, hc) } yield color + reflectedColor } } }
  • 93. Reflective surfaces Use WorldReflection trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] = { /* ... */ for { color <- /* standard computation of color */ reflectedColor <- worldReflectionModule.reflectedColor(world, hc) } yield color + reflectedColor } } }
  • 94. Reflective surfaces Use WorldReflection trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] = { /* ... */ for { color <- /* standard computation of color */ reflectedColor <- worldReflectionModule.reflectedColor(world, hc) } yield color + reflectedColor } } }
  • 95. Implement WorldReflection Live trait Live extends WorldReflectionModule { val worldModule: WorldModule.Service[Any] val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor(world: World, hitComps: HitComps, remaining: Int): ZIO[Any, RayTracerError, Color] = if (hitComps.shape.material.reflective == 0) { UIO(Color.black) } else { val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV) worldModule.colorForRay(world, reflRay, remaining).map(c => c * hitComps.shape.material.reflective ) } } }
  • 96. Implement WorldReflection Live trait Live extends WorldReflectionModule { val worldModule: WorldModule.Service[Any] val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor(world: World, hitComps: HitComps, remaining: Int): ZIO[Any, RayTracerError, Color] = if (hitComps.shape.material.reflective == 0) { UIO(Color.black) } else { val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV) worldModule.colorForRay(world, reflRay, remaining).map(c => c * hitComps.shape.material.reflective ) } } }
  • 97. Implement WorldReflection Live trait Live extends WorldReflectionModule { val worldModule: WorldModule.Service[Any] val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor(world: World, hitComps: HitComps, remaining: Int): ZIO[Any, RayTracerError, Color] = if (hitComps.shape.material.reflective == 0) { UIO(Color.black) } else { val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV) worldModule.colorForRay(world, reflRay, remaining).map(c => c * hitComps.shape.material.reflective ) } } }
  • 98. Implement WorldReflection Live trait Live extends WorldReflectionModule { val worldModule: WorldModule.Service[Any] val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor(world: World, hitComps: HitComps, remaining: Int): ZIO[Any, RayTracerError, Color] = if (hitComps.shape.material.reflective == 0) { UIO(Color.black) } else { val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV) worldModule.colorForRay(world, reflRay, remaining).map(c => c * hitComps.shape.material.reflective ) } } }
  • 100. Implement WorldReflection Dummy trait NoReflection extends WorldReflectionModule { val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor( world: World, hitComps: HitComps, remaining: Int ): ZIO[Any, RayTracerError, Color] = UIO.succeed(Color.black) } }
  • 101. Spheres → Red: reflective = 0.9 → Green/white: reflective = 0.6 → Blue: reflective = 0.9, transparency: 1 program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with PhongReflectionModule.Live with WorldReflectionModule.NoReflection } ⠀ ⠀ ⠀ ⠀ ⠀ ⠀⠀
  • 102. Spheres → Red: reflective = 0.9 → Green/white: reflective = 0.6 → Blue: reflective = 0.9, transparency: 1 program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with PhongReflectionModule.Live with WorldReflectionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ } ⠀ ⠀ ⠀ ⠀ ⠀ ⠀⠀
  • 103. Spheres → Red: reflective = 0.9 → Green/white: reflective = 0.6 → Blue: reflective = 0.9, transparency: 1 program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with PhongReflectionModule.Live with WorldReflectionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ with WorldRefractionModule.NoRefraction⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ }
  • 104. Spheres → Red: reflective = 0.9 → Green/white: reflective = 0.6 → Blue: reflective = 0.9, transparency: 1 program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with PhongReflectionModule.Live with WorldReflectionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ with WorldRefractionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ } ⠀ ⠀ ⠀ ⠀
  • 105. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications
  • 106. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc
  • 107. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc → Do not abuse typeclasses
  • 108. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc → Do not abuse typeclasses → Can group capabilities
  • 109. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc → Do not abuse typeclasses → Can group capabilities → Can provide capabilities one at a time - provideSome
  • 110. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc → Do not abuse typeclasses → Can group capabilities → Can provide capabilities one at a time - provideSome → Are not dependent on implicits (survive refactoring)
  • 112. Conclusion - Environmental Effects → Low entry barrier, very mechanical
  • 113. Conclusion - Environmental Effects → Low entry barrier, very mechanical → Macros help with boilerplate
  • 114. Conclusion - Environmental Effects → Low entry barrier, very mechanical → Macros help with boilerplate → Compiler is your friend #
  • 115. Conclusion - Environmental Effects → Low entry barrier, very mechanical → Macros help with boilerplate → Compiler is your friend # → Handle circular dependencies
  • 116. Conclusion - Environmental Effects → Low entry barrier, very mechanical → Macros help with boilerplate → Compiler is your friend # → Handle circular dependencies → Try it out! %
  • 117. Conclusion - Environmental Effects → Low entry barrier, very mechanical → Macros help with boilerplate → Compiler is your friend # → Handle circular dependencies → Try it out! % → Join ZIO Discord channel