{-# LANGUAGE BangPatterns #-} {-| Single-threaded benchmark for CSG operations. -} import Criterion.Main import Data.Vector.Unboxed as V hiding ((++)) import qualified Data.Strict.Maybe as S import Data.CSG hiding (distance) -- | Pixels in meter at unit distance. resolution :: Double resolution = 50.0 -- | Build cartesian axes from yaw and pitch with 0 roll. Angles are -- in radians. buildCartesian :: Double -> Double -> (Vec3, Vec3, Vec3) buildCartesian yaw pitch = (u, v, w) where u = fromXYZ (cos yaw * cos pitch, sin yaw * cos pitch, sin pitch) v = fromXYZ (- (sin yaw), cos yaw, 0) w = u >< v {-# INLINE buildCartesian #-} -- | Generate initial points of rays within a square region on a plane -- parallel to YZ. generateRayPoints :: Int -- ^ Total ray count. -> Double -- ^ Distance of a plane from the origin. -> V.Vector Vec3 generateRayPoints rayCount distance = let !(n, sX, sY) = buildCartesian 0 0 !p = n .^ (-distance) -- Find a dimension of a square viewport used to generate -- rays. The dimension will most closely fit the required -- particle count number without exceeding it dim :: Int !dim = floor (sqrt $ fromIntegral rayCount :: Double) !halfDim = dim `div` 2 -- This differs from Raycaster module by a (fromIntegral dim) -- term because range for ray index values are integers (as -- opposed to (-1,1) used by gloss) !scale = fromIntegral halfDim * distance / (resolution * fromIntegral dim) -- Initial point of I-th ray on a plane, row-major. 0 ray -- starts at (-halfDim, -halfDim). ithRay i = p <+> (sX .^ (scale * rx)) <+> (sY .^ (scale * ry)) where (y, x) = i `divMod` dim rx = fromIntegral $ x - halfDim ry = fromIntegral $ y - halfDim {-# INLINE ithRay #-} in V.generate (dim * dim) ithRay -- | Test raycasting performance for a solid. test :: V.Vector Vec3 -- ^ Initial points of test rays. Test rays are directed along -- the Ox axis. -> Solid -> V.Vector Bool test rayPoints solid = let !(n, _, _) = buildCartesian 0 0 posToTrace pos = -- Only head of trace gets evaluated case cast (Ray (pos, n)) solid of S.Nothing -> False _ -> True in V.map posToTrace rayPoints rays :: Int rays = 1000 * 1000 dist :: Double dist = 100 solid1 :: Solid solid1 = plane origin (fromXYZ (0, -0.5, 1)) `intersect` sphere origin 2.5 solid2 :: Solid solid2 = cylinder origin (fromXYZ (1, 0, 0)) 4 `intersect` cylinder origin (fromXYZ (0, 1, 0)) 4 `intersect` cylinder origin (fromXYZ (0, 0, 1)) 4 main :: IO () main = defaultMain [ bgroup "Misc" [ bench ("Test rays generation (" ++ show rays ++ ")" ) $ whnf (uncurry generateRayPoints) (rays, dist) ] , bgroup "Raycasting" [ bench "solid1" $ whnf (uncurry test) (rs, solid1) , bench "solid2" $ whnf (uncurry test) (rs, solid2) ] ] where !rs = generateRayPoints rays dist