SlideShare ist ein Scribd-Unternehmen logo
1 von 118
Downloaden Sie, um offline zu lesen
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

Weitere ähnliche Inhalte

Was ist angesagt?

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 2017Szymon Matejczyk
 
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 88Mahmoud 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 CMohammad Imam Hossain
 
[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 212Mahmoud Samir Fayed
 
The Uncertain Enterprise
The Uncertain EnterpriseThe Uncertain Enterprise
The Uncertain EnterpriseClarkTony
 
Concurrent Application Development using Scala
Concurrent Application Development using ScalaConcurrent Application Development using Scala
Concurrent Application Development using ScalaSiarhiej 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
 
Streams for (Co)Free!
Streams for (Co)Free!Streams for (Co)Free!
Streams for (Co)Free!John De Goes
 
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 structuresLakshmi Sarvani Videla
 

Was ist angesagt? (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
 

Ähnlich wie Ray Tracing with ZIO

Functional programming in C++ LambdaNsk
Functional programming in C++ LambdaNskFunctional programming in C++ LambdaNsk
Functional programming in C++ LambdaNskAlexander 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 2Hang Zhao
 
Building Enigma with State Monad & Lens
Building Enigma with State Monad & LensBuilding Enigma with State Monad & Lens
Building Enigma with State Monad & LensTimothy 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 comonadsAlexander Granin
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator PatternEric 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 meetupMikhail Girkin
 
Fourier project presentation
Fourier project  presentationFourier project  presentation
Fourier project presentation志璿 楊
 
C++ extension methods
C++ extension methodsC++ extension methods
C++ extension methodsphil_nash
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKirill Rozov
 
Monads and friends demystified
Monads and friends demystifiedMonads and friends demystified
Monads and friends demystifiedAlessandro 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-optimizationsEgor 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 RewritingEelco Visser
 

Ähnlich wie 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
 

Kürzlich hochgeladen

Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DaySri Ambati
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 

Kürzlich hochgeladen (20)

Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 

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