\section{Ray Tracing Support} This section implements functions that could form the basis of a ray tracer. \begin{code}
module RSAGL.RayTrace.RayTrace
    (Geometry(..),
     Plane, 
     plane,
     plane3,
     UnitSphere(..), Sphere,
     sphere,
     testRay1st,
     shadowDeform)
    where

import RSAGL.Scene.CoordinateSystems
import RSAGL.Math.Affine
import RSAGL.Math.WrappedAffine
import RSAGL.Math.Vector
import RSAGL.Math.Ray
import Data.Ord
import Data.List
import Data.Maybe
import RSAGL.Types
\end{code} \subsection{Geometry} \texttt{Geometry} supports testing ray-object intersections. \texttt{traceRay} takes an incomming ray of unit length and the \texttt{Geometry} and yields both a \texttt{SurfaceVertex3D} for the point of intersection and the distance between the point of origin and all points of intersection. A negative distance is valid and optional if the point of intersection is behind the ray. \begin{code}
class Geometry g where
    testRay :: Ray3D -> g -> [(RSdouble,SurfaceVertex3D)]

instance (Geometry g) => Geometry [g] where
    testRay ray gs = concatMap (testRay ray) gs
\end{code} \subsection{Planes} \begin{code}
data Plane = Plane Point3D Vector3D

instance Geometry Plane where
    testRay (ray@(Ray3D r r')) (Plane p n) = case t of
            _ | t > 0.0 -> [(t,SurfaceVertex3D (projectRay t ray) n)]
            _           -> []
        where k = dotProduct n $ vectorToFrom p r
              a = dotProduct n r'
              t = k/a

instance AffineTransformable Plane where
    transform m (Plane p v) = Plane (transform m p) (transform m v)

plane :: Point3D -> Vector3D -> Plane
plane p v = Plane p $ vectorNormalize v

plane3 :: Point3D -> Point3D -> Point3D -> Plane
plane3 p1 p2 p3 = plane p1 $ fromMaybe (error $ "plane3: " ++ show (p1,p2,p3) ++ " don't uniquely define a plane.") $ newell [p1,p2,p3]
\end{code} \subsection{Spheres} \begin{code}
data UnitSphere = UnitSphere
type Sphere = WrappedAffine UnitSphere

instance Geometry UnitSphere where
    testRay (ray@(Ray3D (Point3D kx ky kz) (Vector3D vx vy vz))) UnitSphere = 
        let a = vx^2 + vy^2 + vz^2
	    b = 2 * (vx*kx + vy*ky + vz*kz)
	    c = kx^2  + ky^2  + kz^2 - 1
	    p2s (Point3D x y z) = SurfaceVertex3D (Point3D x y z) (Vector3D x y z)
	    in case highSchoolAlgebra a b c of
		Just (Right (x,y)) -> [(x,p2s $ projectRay x ray),(y,p2s $ projectRay y ray)]
		_ -> []

highSchoolAlgebra :: RSdouble -> RSdouble -> RSdouble -> Maybe (Either RSdouble (RSdouble,RSdouble))
highSchoolAlgebra a b c =
    let d = b*b - 4*a*c
        sqrtd = sqrt d
        ta = 2*a
        in case () of
	    () | d == 0 -> Just $ Left $ negate b / ta
	    () | d > 0 -> Just $ Right ((negate b + sqrtd) / ta,(negate b - sqrtd) / ta)
	    () -> Nothing

sphere :: Point3D -> RSdouble -> Sphere
sphere p r = translateToFrom p origin_point_3d $ scale' r $ wrapAffine UnitSphere
\end{code} \subsection{Instances} \begin{code}
instance Geometry g => Geometry (WrappedAffine g) where
    testRay r (WrappedAffine m g) = map (\(_,sv3d) -> let SurfaceVertex3D p v = migrateToFrom m root_coordinate_system sv3d
            in (distanceAlong r p,SurfaceVertex3D p v)) $
        testRay (normalizeRay $ migrateToFrom root_coordinate_system m r) g
\end{code} \subsection{Algorithms} \texttt{testRay1st} is the special case of testRay that returns only the nearest point of intersection, if any. \begin{code}
testRay1st :: (Geometry g) => Ray3D -> g -> Maybe (RSdouble,SurfaceVertex3D)
testRay1st r g = listToMaybe $ sortBy (comparing fst) $ filter ((>0) . fst) $ testRay r g
\end{code} \texttt{shadowDeform} constructs a deformation function using a geometry. An existing surface is mapped to the surface of the geometry by casting the surface along parallel rays, as happens when a shadow is cast by rays of sunlight. \begin{code}
shadowDeform :: (Geometry g) => Vector3D -> g -> SurfaceVertex3D -> SurfaceVertex3D
shadowDeform sunlight g (sv3d) = maybe sv3d snd $ testRay1st r g
    where r = Ray3D (sv3d_position sv3d) sunlight
\end{code}