V10/cmd/sml/doc/examples/ray.sml
To: research!dbm
Status: R
(* Find the intersection of a ray with a cylinder in three-space. If
there is more than one intersection, return the parameter of
intersection closest to the start of the ray. If there is no
intersection, return NONE.
The ray is specified by its starting point (rayStart) and direction
vector (rayVec). The cylinder is specified by a starting point (cStart)
a length vector (cVec), and a radius (cRad). The cylinder is finite;
it does not extend beyond the direction vector at either end.
*)
local open Real
val EP = 0.0000001
fun newton f x =
let val y = f x
in if abs(y)>EP then newton f (x+EP*y/(y-f(x+EP))) else x
end
infix 7 @ fun (a,b,c) @ (d,e,f) = a*d+b*e+c*f (* dot product *)
infix 7 *! fun (a,b,c) *! t = (a*t,b*t,c*t) (* vector * scalar *)
infix 6 +! fun (a,b,c) +! (d,e,f) = (a+d,b+e,c+f) (* vector addition *)
infix 6 -! fun (a,b,c) -! (d,e,f) = (a-d,b-e,c-f) (* vector subtraction *)
fun along (C,D) t = C +! (D *! t)
in
fun cylhit (ray as (rayStart, rayVec), cyl as (cStart, cVec), cRad) =
let val rayStart = rayStart -! cStart
fun cylpos p = (cVec@p) / (cVec@cVec)
(* parameter of closest point on cVec to the point p *)
fun cyldist p = let val v = p -! (along cyl (cylpos p)) in v@v end
(* distance of p from closest point on cVec *)
fun cyldist' p = p*!(cVec@cVec) -! cVec*!(p@cVec)
(* gradient of the cyldist function *)
val tf = newton(fn t => cyldist' (along ray t) @ rayVec) 0.0
(* parameter along the ray of the closest point to cVec *)
in if cyldist (along ray tf) > cRad*cRad then NONE else
let val t0 = newton(fn t => cyldist(along ray t)-(cRad*cRad)) 0.0
(* one of the two intersection points *)
val t1 = newton(fn t => cyldist(along ray t)-(cRad*cRad)) (tf+tf-t0)
(* the other intersection point *)
fun inRange v = v >= 0.0 andalso v <= 1.0
in case (t0 > 0.0 andalso inRange (cylpos (along ray t0)),
t1 > 0.0 andalso inRange (cylpos (along ray t1)))
of (false,false) => NONE
| (true,false) => SOME t0
| (false,true) => SOME t1
| (true,true) => SOME (if (t0<t1) then t0 else t1)
end
end
end