val delta = Math.sqrt (Real.nextAfter(1.0, 2.0) - 1.0) val infinity = Real.posInf type vec = real * real * real infix 7 *| fun s *| (x, y, z) : vec = (s*x, s*y, s*z) infix 6 +| fun (x1, y1, z1) +| (x2, y2, z2) : vec = (x1+x2, y1+y2, z1+z2) infix 6 -| fun (x1, y1, z1) -| (x2, y2, z2) : vec = (x1-x2, y1-y2, z1-z2) fun dot((x1, y1, z1), (x2, y2, z2)) : real = x1*x2 + y1*y2 + z1*z2 fun unitise r = (1.0 / Real.Math.sqrt (dot(r, r))) *| r datatype scene = Sphere of vec * real | Group of vec * real * scene list fun length r = Math.sqrt(dot(r, r)) fun ray_sphere(orig, dir, center, radius) = let val v = center -| orig val b = dot(v, dir) val disc = b * b - dot(v, v) + radius * radius in if disc < 0.0 then infinity else let val disc = Real.Math.sqrt disc val t2 = b + disc in if t2 < 0.0 then infinity else let val t1 = b - disc in if t1 > 0.0 then t1 else t2 end end end fun intersect(orig, dir, scene) = let fun aux(Sphere (center, radius), first as (l, _)) = let val l' = ray_sphere(orig, dir, center, radius) in if l' >= l then first else (l', unitise (orig +| l' *| dir -| center)) end | aux(Group (center, radius, scenes), first as (l, _)) = if ray_sphere(orig, dir, center, radius) >= l then first else foldl aux first scenes in aux(scene, (infinity, (0.0, 0.0, 0.0))) end val neg_light = unitise (1.0, 3.0, ~2.0) fun ray_trace(dir, scene) = let val (lambda, n) = intersect((0.0, 0.0, 0.0), dir, scene) in if lambda >= infinity then 0.0 else let val g = dot(n, neg_light) in if g <= 0.0 then 0.0 else let val p = lambda *| dir +| delta *| n val (l, _) = intersect(p, neg_light, scene) in if l >= infinity then g else 0.0 end end end fun max (a : real) b = if a print ("P5\n"^s^" "^s^"\n255\n")) (Int.toString n); loop((pixel(scene, real n, ss)), (), (0, 0, n)); OS.Process.success end fun main (_, [level, n]) = aux (getOpt(Int.fromString level, 9), getOpt(Int.fromString n, 512)) | main (_, _) = aux (9, 512) val _ = main ("", CommandLine.arguments ())