lhc-0.10: tests/bugs/RayT.hs
import System
infinity = 1/0
delta = sqrt e where e = encodeFloat (floatRadix e) (-floatDigits e)
infixl 7 .*, *|
data Vector = V !Double !Double !Double deriving (Show, Eq)
s *| V x y z = V (s * x) (s * y) (s * z)
instance Num Vector where
V x y z + V x' y' z' = V (x + x') (y + y') (z + z')
V x y z - V x' y' z' = V (x - x') (y - y') (z - z')
fromInteger i = V x x x where x = fromInteger i
V x y z .* V x' y' z' = x * x' + y * y' + z * z'
vlength r = sqrt (r .* r)
unitise r = 1 / vlength r *| r
data Scene
= Sphere !Vector !Double
| Group !Vector !Double Scene Scene Scene Scene Scene
deriving (Show)
ray_sphere (V dx dy dz) (V vx vy vz) r =
let disc = vx * vx + vy * vy + vz * vz - r * r
in if disc < 0 then infinity else
let b = vx * dx + vy * dy + vz * dz
b2 = b * b
in if b2 < disc then infinity else
let disk = sqrt(b2 - disc)
t1 = b - disk
in if t1 > 0 then t1 else b + disk
ray_sphere' (V ox oy oz) (V dx dy dz) (V cx cy cz) r =
let vx = cx - ox; vy = cy - oy; vz = cz - oz
vv = vx * vx + vy * vy + vz * vz
b = vx * dx + vy * dy + vz * dz
disc = b * b - vv + r * r
in disc >= 0 && b + sqrt disc >= 0
data Hit = H {l :: !Double, nv :: Vector }
intersect dir@(V dx dy dz) hit s = case s of
Sphere center@(V cx cy cz) radius ->
let l' = ray_sphere dir center radius in
if l' >= l hit then hit else
let x = l' * dx - cx
y = l' * dy - cy
z = l' * dz - cz
il = 1 / sqrt(x * x + y * y + z * z)
in H {l = l', nv = V (il * x) (il * y) (il * z) }
Group center radius a b c d e ->
let l' = ray_sphere dir center radius in
if l' >= l hit then hit else
let f h s = intersect dir h s in
f (f (f (f (f hit a) b) c) d) e
intersect' orig dir s = case s of
Sphere center radius -> ray_sphere' orig dir center radius
Group center radius a b c d e ->
let f s = intersect' orig dir s in
ray_sphere' orig dir center radius && (f a || f b || f c || f d || f e)
neg_light = unitise (V 1 3 (-2))
ray_trace dir scene =
let hit = intersect dir (H infinity 0) scene in
if l hit == infinity then 0 else
let n = nv hit in
let g = n .* neg_light in
if g < 0 then 0 else
if intersect' (l hit *| dir + delta *| n) neg_light scene then 0 else g
fold5 f x a b c d e = f (f (f (f (f x a) b) c) d) e
create level c r =
let obj = Sphere c r in
if level == 1 then obj else
let a = 3 * r / sqrt 12 in
let bound (c, r) s = case s of
Sphere c' r' -> (c, max r (vlength (c - c') + r'))
Group _ _ v w x y z -> fold5 bound (c, r) v w x y z in
let aux x' z' = create (level - 1 :: Int) (c + V x' a z') (0.5 * r) in
let w = aux (-a) (-a); x = aux a (-a) in
let y = aux (-a) a; z = aux a a in
let (c1, r1) = fold5 bound (c + V 0 r 0, 0) obj w x y z in
Group c1 r1 obj w x y z
ss = 4
pixel_vals n scene y x = sum
[ let f a da = a - n / 2 + da / ss; d = unitise (V (f x dx) (f y dy) n)
in ray_trace d scene | dx <- [0..ss-1], dy <- [0..ss-1] ]
main = do
[level,ni] <- fmap (map read) getArgs
let n = fromIntegral ni
scene = create level (V 0 (-1) 4) 1
scale x = 0.5 + 255 * x / (ss*ss)
picture = [ toEnum $ truncate $ scale $ pixel_vals n scene y x | y <- [n-1,n-2..0], x <- [0..n-1]]
putStrLn $ "P5\n" ++ show ni ++ " " ++ show ni ++ "\n255\n" ++ picture