GlomeVec 0.1.1 → 0.1.2
raw patch · 5 files changed
+867/−867 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Data.GlomeTexture: gamma :: Int -> Int -> Int -> Vec
- Data.GlomeTexture: grad :: Array Int Vec
- Data.GlomeTexture: intGamma :: Int -> Int -> Int
- Data.GlomeTexture: knot :: Int -> Int -> Int -> Vec -> Flt
- Data.GlomeTexture: lump_wave :: Flt -> Flt
- Data.GlomeTexture: noise :: Vec -> Flt
- Data.GlomeTexture: omega :: Flt -> Flt
- Data.GlomeTexture: perlin :: Vec -> Flt
- Data.GlomeTexture: perlin_turb :: Vec -> Int -> Flt
- Data.GlomeTexture: phi :: Array Int Int
- Data.GlomeTexture: sine_wave :: Flt -> Flt
- Data.GlomeTexture: square_wave :: Flt -> Flt
- Data.GlomeTexture: stripe :: Vec -> (Flt -> Flt) -> (Vec -> Flt)
- Data.GlomeTexture: triangle_wave :: Flt -> Flt
- Data.GlomeTexture: turbulence :: Vec -> Int -> Flt
- Data.GlomeVec: Bbox :: !Vec -> !Vec -> Bbox
- Data.GlomeVec: Interval :: !Flt -> !Flt -> Interval
- Data.GlomeVec: Matrix :: !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> Matrix
- Data.GlomeVec: Ray :: !Vec -> !Vec -> Ray
- Data.GlomeVec: Vec :: !Flt -> !Flt -> !Flt -> Vec
- Data.GlomeVec: Xfm :: Matrix -> Matrix -> Xfm
- Data.GlomeVec: about_equal :: Flt -> Flt -> Bool
- Data.GlomeVec: assert_norm :: Vec -> Vec
- Data.GlomeVec: bbclip :: Ray -> Bbox -> Interval
- Data.GlomeVec: bbinside :: Bbox -> Vec -> Bool
- Data.GlomeVec: bbjoin :: Bbox -> Bbox -> Bbox
- Data.GlomeVec: bboverlap :: Bbox -> Bbox -> Bbox
- Data.GlomeVec: bbpts :: [Vec] -> Bbox
- Data.GlomeVec: bbsa :: Bbox -> Flt
- Data.GlomeVec: bbsplit :: Bbox -> Int -> Flt -> (Bbox, Bbox)
- Data.GlomeVec: bbvol :: Bbox -> Flt
- Data.GlomeVec: bisect :: Vec -> Vec -> Vec
- Data.GlomeVec: check_xfm :: Xfm -> Xfm
- Data.GlomeVec: clamp :: Flt -> Flt -> Flt -> Flt
- Data.GlomeVec: compose :: [Xfm] -> Xfm
- Data.GlomeVec: data Bbox
- Data.GlomeVec: data Interval
- Data.GlomeVec: data Matrix
- Data.GlomeVec: data Ray
- Data.GlomeVec: data Vec
- Data.GlomeVec: data Xfm
- Data.GlomeVec: dcos :: Flt -> Flt
- Data.GlomeVec: deg :: Flt -> Flt
- Data.GlomeVec: dir :: Ray -> !Vec
- Data.GlomeVec: empty_bbox :: Bbox
- Data.GlomeVec: everything_bbox :: Bbox
- Data.GlomeVec: fabs :: Flt -> Flt
- Data.GlomeVec: fmax :: Flt -> Flt -> Flt
- Data.GlomeVec: fmax3 :: Flt -> Flt -> Flt -> Flt
- Data.GlomeVec: fmax4 :: Flt -> Flt -> Flt -> Flt -> Flt
- Data.GlomeVec: fmin :: Flt -> Flt -> Flt
- Data.GlomeVec: fmin3 :: Flt -> Flt -> Flt -> Flt
- Data.GlomeVec: fmin4 :: Flt -> Flt -> Flt -> Flt -> Flt
- Data.GlomeVec: iabs :: Int -> Int
- Data.GlomeVec: ident_matrix :: Matrix
- Data.GlomeVec: ident_xfm :: Xfm
- Data.GlomeVec: infinity :: Flt
- Data.GlomeVec: instance Show Bbox
- Data.GlomeVec: instance Show Interval
- Data.GlomeVec: instance Show Matrix
- Data.GlomeVec: instance Show Ray
- Data.GlomeVec: instance Show Vec
- Data.GlomeVec: instance Show Xfm
- Data.GlomeVec: invxfm_norm :: Xfm -> Vec -> Vec
- Data.GlomeVec: invxfm_point :: Xfm -> Vec -> Vec
- Data.GlomeVec: invxfm_ray :: Xfm -> Ray -> Ray
- Data.GlomeVec: invxfm_vec :: Xfm -> Vec -> Vec
- Data.GlomeVec: mat_mult :: Matrix -> Matrix -> Matrix
- Data.GlomeVec: nvx :: Vec
- Data.GlomeVec: nvy :: Vec
- Data.GlomeVec: nvz :: Vec
- Data.GlomeVec: origin :: Ray -> !Vec
- Data.GlomeVec: orth :: Vec -> (Vec, Vec)
- Data.GlomeVec: p1 :: Bbox -> !Vec
- Data.GlomeVec: p2 :: Bbox -> !Vec
- Data.GlomeVec: plane_int :: Ray -> Vec -> Vec -> Vec
- Data.GlomeVec: plane_int_dist :: Ray -> Vec -> Vec -> Flt
- Data.GlomeVec: rad :: Flt -> Flt
- Data.GlomeVec: ray_move :: Ray -> Flt -> Ray
- Data.GlomeVec: reflect :: Vec -> Vec -> Vec
- Data.GlomeVec: rot :: Flt -> Flt
- Data.GlomeVec: rotate :: Vec -> Flt -> Xfm
- Data.GlomeVec: sas2s :: Flt -> Flt -> Flt -> Flt
- Data.GlomeVec: scale :: Vec -> Xfm
- Data.GlomeVec: translate :: Vec -> Xfm
- Data.GlomeVec: type Flt = Double
- Data.GlomeVec: uvw_to_xyz :: Vec -> Vec -> Vec -> Xfm
- Data.GlomeVec: va :: Vec -> Int -> Flt
- Data.GlomeVec: vadd :: Vec -> Vec -> Vec
- Data.GlomeVec: vadd3 :: Vec -> Vec -> Vec -> Vec
- Data.GlomeVec: vcross :: Vec -> Vec -> Vec
- Data.GlomeVec: vdec :: Vec -> Flt -> Vec
- Data.GlomeVec: vdist :: Vec -> Vec -> Flt
- Data.GlomeVec: vdot :: Vec -> Vec -> Flt
- Data.GlomeVec: vec :: Flt -> Flt -> Flt -> Vec
- Data.GlomeVec: veq :: Vec -> Vec -> Bool
- Data.GlomeVec: veqsign :: Vec -> Vec -> Bool
- Data.GlomeVec: vinc :: Vec -> Flt -> Vec
- Data.GlomeVec: vinvert :: Vec -> Vec
- Data.GlomeVec: vlen :: Vec -> Flt
- Data.GlomeVec: vlensqr :: Vec -> Flt
- Data.GlomeVec: vmap :: (Flt -> Flt) -> Vec -> Vec
- Data.GlomeVec: vmap2 :: (Flt -> Flt -> Flt) -> Vec -> Vec -> Vec
- Data.GlomeVec: vmax :: Vec -> Vec -> Vec
- Data.GlomeVec: vmaxaxis :: Vec -> Int
- Data.GlomeVec: vmin :: Vec -> Vec -> Vec
- Data.GlomeVec: vmul :: Vec -> Vec -> Vec
- Data.GlomeVec: vnorm :: Vec -> Vec
- Data.GlomeVec: vnudge :: Vec -> Vec
- Data.GlomeVec: vrcp :: Vec -> Vec
- Data.GlomeVec: vrotate :: Vec -> Ray -> Flt -> Vec
- Data.GlomeVec: vscale :: Vec -> Flt -> Vec
- Data.GlomeVec: vscaleadd :: Vec -> Vec -> Flt -> Vec
- Data.GlomeVec: vset :: Vec -> Int -> Flt -> Vec
- Data.GlomeVec: vsub :: Vec -> Vec -> Vec
- Data.GlomeVec: vunit :: Vec
- Data.GlomeVec: vx :: Vec
- Data.GlomeVec: vy :: Vec
- Data.GlomeVec: vz :: Vec
- Data.GlomeVec: vzero :: Vec
- Data.GlomeVec: x :: Vec -> Flt
- Data.GlomeVec: xfm_mult :: Xfm -> Xfm -> Xfm
- Data.GlomeVec: xfm_point :: Xfm -> Vec -> Vec
- Data.GlomeVec: xfm_ray :: Xfm -> Ray -> Ray
- Data.GlomeVec: xfm_vec :: Xfm -> Vec -> Vec
- Data.GlomeVec: xyz_to_uvw :: Vec -> Vec -> Vec -> Xfm
- Data.GlomeVec: y :: Vec -> Flt
- Data.GlomeVec: z :: Vec -> Flt
+ Data.Glome.Texture: gamma :: Int -> Int -> Int -> Vec
+ Data.Glome.Texture: grad :: Array Int Vec
+ Data.Glome.Texture: intGamma :: Int -> Int -> Int
+ Data.Glome.Texture: knot :: Int -> Int -> Int -> Vec -> Flt
+ Data.Glome.Texture: lump_wave :: Flt -> Flt
+ Data.Glome.Texture: noise :: Vec -> Flt
+ Data.Glome.Texture: omega :: Flt -> Flt
+ Data.Glome.Texture: perlin :: Vec -> Flt
+ Data.Glome.Texture: perlin_turb :: Vec -> Int -> Flt
+ Data.Glome.Texture: phi :: Array Int Int
+ Data.Glome.Texture: sine_wave :: Flt -> Flt
+ Data.Glome.Texture: square_wave :: Flt -> Flt
+ Data.Glome.Texture: stripe :: Vec -> (Flt -> Flt) -> (Vec -> Flt)
+ Data.Glome.Texture: triangle_wave :: Flt -> Flt
+ Data.Glome.Texture: turbulence :: Vec -> Int -> Flt
+ Data.Glome.Vec: Bbox :: !Vec -> !Vec -> Bbox
+ Data.Glome.Vec: Interval :: !Flt -> !Flt -> Interval
+ Data.Glome.Vec: Matrix :: !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> !Flt -> Matrix
+ Data.Glome.Vec: Ray :: !Vec -> !Vec -> Ray
+ Data.Glome.Vec: Vec :: !Flt -> !Flt -> !Flt -> Vec
+ Data.Glome.Vec: Xfm :: Matrix -> Matrix -> Xfm
+ Data.Glome.Vec: about_equal :: Flt -> Flt -> Bool
+ Data.Glome.Vec: assert_norm :: Vec -> Vec
+ Data.Glome.Vec: bbclip :: Ray -> Bbox -> Interval
+ Data.Glome.Vec: bbinside :: Bbox -> Vec -> Bool
+ Data.Glome.Vec: bbjoin :: Bbox -> Bbox -> Bbox
+ Data.Glome.Vec: bboverlap :: Bbox -> Bbox -> Bbox
+ Data.Glome.Vec: bbpts :: [Vec] -> Bbox
+ Data.Glome.Vec: bbsa :: Bbox -> Flt
+ Data.Glome.Vec: bbsplit :: Bbox -> Int -> Flt -> (Bbox, Bbox)
+ Data.Glome.Vec: bbvol :: Bbox -> Flt
+ Data.Glome.Vec: bisect :: Vec -> Vec -> Vec
+ Data.Glome.Vec: check_xfm :: Xfm -> Xfm
+ Data.Glome.Vec: clamp :: Flt -> Flt -> Flt -> Flt
+ Data.Glome.Vec: compose :: [Xfm] -> Xfm
+ Data.Glome.Vec: data Bbox
+ Data.Glome.Vec: data Interval
+ Data.Glome.Vec: data Matrix
+ Data.Glome.Vec: data Ray
+ Data.Glome.Vec: data Vec
+ Data.Glome.Vec: data Xfm
+ Data.Glome.Vec: dcos :: Flt -> Flt
+ Data.Glome.Vec: deg :: Flt -> Flt
+ Data.Glome.Vec: dir :: Ray -> !Vec
+ Data.Glome.Vec: empty_bbox :: Bbox
+ Data.Glome.Vec: everything_bbox :: Bbox
+ Data.Glome.Vec: fabs :: Flt -> Flt
+ Data.Glome.Vec: fmax :: Flt -> Flt -> Flt
+ Data.Glome.Vec: fmax3 :: Flt -> Flt -> Flt -> Flt
+ Data.Glome.Vec: fmax4 :: Flt -> Flt -> Flt -> Flt -> Flt
+ Data.Glome.Vec: fmin :: Flt -> Flt -> Flt
+ Data.Glome.Vec: fmin3 :: Flt -> Flt -> Flt -> Flt
+ Data.Glome.Vec: fmin4 :: Flt -> Flt -> Flt -> Flt -> Flt
+ Data.Glome.Vec: iabs :: Int -> Int
+ Data.Glome.Vec: ident_matrix :: Matrix
+ Data.Glome.Vec: ident_xfm :: Xfm
+ Data.Glome.Vec: infinity :: Flt
+ Data.Glome.Vec: instance Show Bbox
+ Data.Glome.Vec: instance Show Interval
+ Data.Glome.Vec: instance Show Matrix
+ Data.Glome.Vec: instance Show Ray
+ Data.Glome.Vec: instance Show Vec
+ Data.Glome.Vec: instance Show Xfm
+ Data.Glome.Vec: invxfm_norm :: Xfm -> Vec -> Vec
+ Data.Glome.Vec: invxfm_point :: Xfm -> Vec -> Vec
+ Data.Glome.Vec: invxfm_ray :: Xfm -> Ray -> Ray
+ Data.Glome.Vec: invxfm_vec :: Xfm -> Vec -> Vec
+ Data.Glome.Vec: mat_mult :: Matrix -> Matrix -> Matrix
+ Data.Glome.Vec: nvx :: Vec
+ Data.Glome.Vec: nvy :: Vec
+ Data.Glome.Vec: nvz :: Vec
+ Data.Glome.Vec: origin :: Ray -> !Vec
+ Data.Glome.Vec: orth :: Vec -> (Vec, Vec)
+ Data.Glome.Vec: p1 :: Bbox -> !Vec
+ Data.Glome.Vec: p2 :: Bbox -> !Vec
+ Data.Glome.Vec: plane_int :: Ray -> Vec -> Vec -> Vec
+ Data.Glome.Vec: plane_int_dist :: Ray -> Vec -> Vec -> Flt
+ Data.Glome.Vec: rad :: Flt -> Flt
+ Data.Glome.Vec: ray_move :: Ray -> Flt -> Ray
+ Data.Glome.Vec: reflect :: Vec -> Vec -> Vec
+ Data.Glome.Vec: rot :: Flt -> Flt
+ Data.Glome.Vec: rotate :: Vec -> Flt -> Xfm
+ Data.Glome.Vec: sas2s :: Flt -> Flt -> Flt -> Flt
+ Data.Glome.Vec: scale :: Vec -> Xfm
+ Data.Glome.Vec: translate :: Vec -> Xfm
+ Data.Glome.Vec: type Flt = Double
+ Data.Glome.Vec: uvw_to_xyz :: Vec -> Vec -> Vec -> Xfm
+ Data.Glome.Vec: va :: Vec -> Int -> Flt
+ Data.Glome.Vec: vadd :: Vec -> Vec -> Vec
+ Data.Glome.Vec: vadd3 :: Vec -> Vec -> Vec -> Vec
+ Data.Glome.Vec: vcross :: Vec -> Vec -> Vec
+ Data.Glome.Vec: vdec :: Vec -> Flt -> Vec
+ Data.Glome.Vec: vdist :: Vec -> Vec -> Flt
+ Data.Glome.Vec: vdot :: Vec -> Vec -> Flt
+ Data.Glome.Vec: vec :: Flt -> Flt -> Flt -> Vec
+ Data.Glome.Vec: veq :: Vec -> Vec -> Bool
+ Data.Glome.Vec: veqsign :: Vec -> Vec -> Bool
+ Data.Glome.Vec: vinc :: Vec -> Flt -> Vec
+ Data.Glome.Vec: vinvert :: Vec -> Vec
+ Data.Glome.Vec: vlen :: Vec -> Flt
+ Data.Glome.Vec: vlensqr :: Vec -> Flt
+ Data.Glome.Vec: vmap :: (Flt -> Flt) -> Vec -> Vec
+ Data.Glome.Vec: vmap2 :: (Flt -> Flt -> Flt) -> Vec -> Vec -> Vec
+ Data.Glome.Vec: vmax :: Vec -> Vec -> Vec
+ Data.Glome.Vec: vmaxaxis :: Vec -> Int
+ Data.Glome.Vec: vmin :: Vec -> Vec -> Vec
+ Data.Glome.Vec: vmul :: Vec -> Vec -> Vec
+ Data.Glome.Vec: vnorm :: Vec -> Vec
+ Data.Glome.Vec: vnudge :: Vec -> Vec
+ Data.Glome.Vec: vrcp :: Vec -> Vec
+ Data.Glome.Vec: vrotate :: Vec -> Ray -> Flt -> Vec
+ Data.Glome.Vec: vscale :: Vec -> Flt -> Vec
+ Data.Glome.Vec: vscaleadd :: Vec -> Vec -> Flt -> Vec
+ Data.Glome.Vec: vset :: Vec -> Int -> Flt -> Vec
+ Data.Glome.Vec: vsub :: Vec -> Vec -> Vec
+ Data.Glome.Vec: vunit :: Vec
+ Data.Glome.Vec: vx :: Vec
+ Data.Glome.Vec: vy :: Vec
+ Data.Glome.Vec: vz :: Vec
+ Data.Glome.Vec: vzero :: Vec
+ Data.Glome.Vec: x :: Vec -> Flt
+ Data.Glome.Vec: xfm_mult :: Xfm -> Xfm -> Xfm
+ Data.Glome.Vec: xfm_point :: Xfm -> Vec -> Vec
+ Data.Glome.Vec: xfm_ray :: Xfm -> Ray -> Ray
+ Data.Glome.Vec: xfm_vec :: Xfm -> Vec -> Vec
+ Data.Glome.Vec: xyz_to_uvw :: Vec -> Vec -> Vec -> Xfm
+ Data.Glome.Vec: y :: Vec -> Flt
+ Data.Glome.Vec: z :: Vec -> Flt
Files
- Data/Glome/Texture.hs +126/−0
- Data/Glome/Vec.hs +738/−0
- Data/GlomeTexture.hs +0/−126
- Data/GlomeVec.hs +0/−738
- GlomeVec.cabal +3/−3
+ Data/Glome/Texture.hs view
@@ -0,0 +1,126 @@+{-+module SolidTexture (square_wave, triangle_wave, sine_wave, + stripe, noise, turbulence+ ) where -}++module Data.Glome.Texture where+import Data.Glome.Vec+import Data.Array.IArray++-- INTERPOLATION FUNCTIONS --+square_wave :: Flt -> Flt+square_wave x =+ let offset = x - (fromIntegral (floor x))+ in if offset < 0.5 then 0 else 1++triangle_wave :: Flt -> Flt+triangle_wave x =+ let offset = x - (fromIntegral (floor x))+ in if offset < 0.5 + then (offset*2)+ else (2-(offset*2))++sine_wave :: Flt -> Flt+sine_wave x = (sin (x*2*pi))*0.5 + 0.5+++lump_wave :: Flt -> Flt+lump_wave x = 1 - x*x*x++-- SCALAR TEXTURE FUNCTIONS --++-- These are simple solid texture functions that take a +-- point as argument and return a number 0 < n < 1++stripe :: Vec -> (Flt -> Flt) -> (Vec -> Flt)+stripe axis interp =+ let len = vlen axis + in+ (\pos -> let offset = vdot pos axis + in interp offset)+++-- PERLIN NOISE --++-- (-6 t^5 + 15 t^4 - 10t^3 +1)+-- "realistic ray tracing 2nd edition" inconsistent +-- on whether it should be t^5 or t^6,+-- but t^5 works and t^6 doesn't.+omega :: Flt -> Flt+omega t_ = + let t = fabs t_+ tsqr = t*t+ tcube = tsqr*t+ in (-6)*tcube*tsqr + 15*tcube*t - 10*tcube + 1++-- questionably random+phi :: Array Int Int+phi = listArray (0,11) [3,0,2,7,4,1,5,11,8,10,9,6]++grad :: Array Int Vec+grad = listArray (0,11) + $ filter (\x -> let l = vlen x in l < 1.5 && l > 1.1) + [Vec x y z | x <- [(-1),0,1],+ y <- [(-1),0,1],+ z <- [(-1),0,1]] ++gamma :: Int -> Int -> Int -> Vec+gamma i j k =+ let a = phi!(mod (iabs k) 12)+ b = phi!(mod (iabs (j+a)) 12)+ c = phi!(mod (iabs (i+b)) 12)+ in grad!c++knot :: Int -> Int -> Int -> Vec -> Flt+knot i j k v =+ let Vec x y z = v+ in (omega x) * (omega y) * (omega z) * (vdot (gamma i j k) v)++intGamma :: Int -> Int -> Int+intGamma i j =+ let a = phi!(mod (iabs j) 16)+ b = phi!(mod (iabs (i+a)) 16)+ in b++turbulence :: Vec -> Int -> Flt+turbulence p 1 = fabs(noise(p))+turbulence p n =+ let newp = vscale p 0.5+ t = fabs (noise p)+ in t + (0.5 * (turbulence newp (n-1)))++noise :: Vec -> Flt +noise (Vec x y z) =+ let i = floor x+ j = floor y+ k = floor z+ u = x-(fromIntegral i)+ v = y-(fromIntegral j)+ w = z-(fromIntegral k)+ in knot i j k (Vec u v w) ++ knot (i+1) j k (Vec (u-1) v w) ++ knot i (j+1) k (Vec u (v-1) w) ++ knot i j (k+1) (Vec u v (w-1)) ++ knot (i+1) (j+1) k (Vec (u-1) (v-1) w) ++ knot (i+1) j (k+1) (Vec (u-1) v (w-1)) ++ knot i (j+1) (k+1) (Vec u (v-1) (w-1)) ++ knot (i+1) (j+1) (k+1) (Vec (u-1) (v-1) (w-1))++perlin :: Vec -> Flt+perlin v =+ let p = ((noise v)+1)*0.5+ in if p > 1 + then error $ "perlin noise error, 1 < " ++ (show p)+ else if p < 0 + then error $ "perlin noise error, 0 > " ++ (show p)+ else p++--untested+perlin_turb :: Vec -> Int -> Flt+perlin_turb v l =+ let p = turbulence v l+ in if p > 1 + then error $ "perlin turbulence error, 1 < " ++ (show p)+ else if p < 0 + then error $ "perlin turbulence error, 0 > " ++ (show p)+ else p
+ Data/Glome/Vec.hs view
@@ -0,0 +1,738 @@+{-# OPTIONS_GHC -fexcess-precision #-}+{-# OPTIONS_GHC -funbox-strict-fields #-}+{-# LANGUAGE BangPatterns #-}++module Data.Glome.Vec where++-- | Performance is pretty similar with Floats or Doubles.+-- Todo: make separate Float and Double instances of this library.+type Flt = Double++-- maybe this is defined somewhere?+infinity :: Flt+--infinity = 1.0 / 0.0+infinity = 1000000.0++-- | Convert from degrees to native angle format (radians).+deg :: Flt -> Flt+deg !x = (x*3.1415926535897)/180++-- | Convert from radians to native format (noop).+rad :: Flt -> Flt+rad !x = x++-- | Convert from rotations to native format. (rot 1 == deg 360)+rot :: Flt -> Flt+rot !x = x*3.1415926535897*2++-- | Trig with degrees instead of radians.+dcos :: Flt -> Flt+dcos d = cos $ deg d++-- | Force a value to be within a range. Usage: clamp min x max+clamp :: Flt -> Flt -> Flt -> Flt+clamp !min !x !max+ | x < min = min+ | x > max = max+ | otherwise = x++-- | Tuning parameter.+delta = 0.0001 :: Flt++-- | Non-polymorphic fmin; this speeds+-- things up in ocaml, not sure about haskell.+fmin :: Flt -> Flt -> Flt+fmin !a !b = if a > b then b else a++-- | Non-polymorphic fmax.+fmax :: Flt -> Flt -> Flt+fmax !a !b = if a > b then a else b++-- | Non-polymorphic min of 3 values.+fmin3 :: Flt -> Flt -> Flt -> Flt+fmin3 !a !b !c = if a > b + then if b > c + then c+ else b+ else if a > c+ then c+ else a++-- | Non-polymorphic max of 3 values.+fmax3 :: Flt -> Flt -> Flt -> Flt+fmax3 !a !b !c = if a > b+ then if a > c+ then a+ else c+ else if b > c+ then b+ else c++-- | Min of 4 values.+fmin4 :: Flt -> Flt -> Flt -> Flt -> Flt+fmin4 !a !b !c !d = fmin (fmin a b) (fmin c d)++-- | Max of 4 values.+fmax4 :: Flt -> Flt -> Flt -> Flt -> Flt+fmax4 !a !b !c !d = fmax (fmax a b) (fmax c d)++-- | Non-polymorphic absolute value.+fabs :: Flt -> Flt+fabs !a = + if a < 0 then (-a) else a++-- | Non-polymorphic integer absolute value.+iabs :: Int -> Int+iabs !a =+ if a < 0 then (-a) else a++-- | Force user to use fabs or iabs, for performance reasons. Not sure if+-- this really helps, though.+abs a = error "use non-polymorphic version, fabs"++-- | Approximate equality for Flt. True if a and b are "almost" equal.+-- The (abs $ a-b) test doesn't work if+-- a and b are large.+about_equal :: Flt -> Flt -> Bool+about_equal !a !b =+ if a > 1 + then+ fabs (1 - (a/b)) < (delta*10) + else+ (fabs $ a - b) < (delta*10)++-- | 3d type represented as a record of unboxed floats.+data Vec = Vec !Flt !Flt !Flt deriving Show++-- | A Ray is made up of an origin and direction Vec.+data Ray = Ray {origin, dir :: !Vec} deriving Show+--data Plane = Plane {norm :: !Vec, offset :: !Flt} deriving Show++-- | Vec constructor.+vec :: Flt -> Flt -> Flt -> Vec+vec !x !y !z = (Vec x y z)++-- | Zero Vec.+vzero :: Vec+vzero = Vec 0.0 0.0 0.0++-- | For when we need a unit vector, but we +-- don't care where it points.+vunit :: Vec+vunit = vx++-- | Unit X vector.+vx :: Vec+vx = Vec 1 0 0++-- | Unit y vector.+vy :: Vec+vy = Vec 0 1 0++-- | Unit z vector.+vz :: Vec+vz = Vec 0 0 1++-- | Negative x vector.+nvx :: Vec+nvx = Vec (-1) 0 0++-- | Negative y vector.+nvy :: Vec+nvy = Vec 0 (-1) 0++-- | Negative z vector.+nvz :: Vec+nvz = Vec 0 0 (-1)++-- Extract x coordinate.+x :: Vec -> Flt+x (Vec x_ _ _) = x_++-- Extract y coordinate.+y :: Vec -> Flt+y (Vec _ y_ _) = y_++-- Extract z coordinate.+z :: Vec -> Flt+z (Vec _ _ z_) = z_++-- | Access the Vec as if it were an array indexed from 0..2.+-- Note: this actually accounts for a noticeable amount of cpu +-- time in the Glome ray tracer.+va :: Vec -> Int -> Flt+va !(Vec x y z) !n = + case n of+ 0 -> x+ 1 -> y+ 2 -> z++-- | Create a new Vec with the Nth field overwritten by new value.+-- I could have used record update syntax.+vset :: Vec -> Int -> Flt -> Vec+vset !(Vec x y z) !i !f =+ case i of+ 0 -> Vec f y z+ 1 -> Vec x f z+ 2 -> Vec x y f++-- | Dot product of 2 vectors. We use this all the time. Dot product of 2+-- normal vectors is the cosine of the angle between them.+vdot :: Vec -> Vec -> Flt+vdot !(Vec x1 y1 z1) !(Vec x2 y2 z2) =+ (x1*x2)+(y1*y2)+(z1*z2)++-- | Cross product of 2 vectors. Produces a vector perpendicular +-- to the given vectors. We use this for things like making the forward,+-- up, and right camera vectors orthogonal. If the input vectors are+-- normalized, the output vector will be as well.+vcross :: Vec -> Vec -> Vec+vcross !(Vec x1 y1 z1) !(Vec x2 y2 z2) =+ Vec + ((y1 * z2) - (z1 * y2))+ ((z1 * x2) - (x1 * z2))+ ((x1 * y2) - (y1 * x2))++-- | Apply a unary Flt operator to each field of the Vec.+vmap :: (Flt -> Flt) -> Vec -> Vec+vmap f !v1 = + Vec (f (x v1)) (f (y v1)) (f (z v1))++-- | Apply a binary Flt operator to pairs of fields from 2 Vecs.+vmap2 :: (Flt -> Flt -> Flt) -> Vec -> Vec -> Vec+vmap2 f !v1 !v2 =+ Vec (f (x v1) (x v2)) + (f (y v1) (y v2)) + (f (z v1) (z v2))++-- | Reverse the direction of a Vec.+vinvert :: Vec -> Vec+vinvert !(Vec x1 y1 z1) =+ Vec (-x1) (-y1) (-z1)++-- | Get the length of a Vec squared. We use this to avoid a slow sqrt. +vlensqr :: Vec -> Flt+vlensqr !v1 = vdot v1 v1++-- | Get the length of a Vec. This is expensive because sqrt is slow.+vlen :: Vec -> Flt+vlen !v1 = sqrt (vdot v1 v1)++-- | Add 2 vectors.+vadd :: Vec -> Vec -> Vec+vadd !(Vec x1 y1 z1) !(Vec x2 y2 z2) =+ Vec (x1 + x2)+ (y1 + y2)+ (z1 + z2)++-- | Add 3 vectors.+vadd3 :: Vec -> Vec -> Vec -> Vec+vadd3 !(Vec x1 y1 z1) !(Vec x2 y2 z2) !(Vec x3 y3 z3) =+ Vec (x1 + x2 + x3)+ (y1 + y2 + y3)+ (z1 + z2 + z3)++-- | Subtract vectors. "vsub b a" is the vector from a to b.+vsub :: Vec -> Vec -> Vec+vsub !(Vec x1 y1 z1) !(Vec x2 y2 z2) =+ Vec (x1 - x2)+ (y1 - y2)+ (z1 - z2)++-- | Multiply corresponding fields. Rarely useful.+vmul :: Vec -> Vec -> Vec+vmul !(Vec x1 y1 z1) !(Vec x2 y2 z2) =+ Vec (x1 * x2)+ (y1 * y2)+ (z1 * z2)++-- | Add a value to all the fields of a Vec. Useful, for instance, to get+-- one corner of the bounding box around a sphere.+vinc :: Vec -> Flt -> Vec+vinc !(Vec x y z) !n =+ Vec (x + n)+ (y + n)+ (z + n)++-- | Subtract a value from all fields of a Vec.+vdec :: Vec -> Flt -> Vec+vdec !(Vec x y z) !n =+ Vec (x - n)+ (y - n)+ (z - n)++-- | Get the maximum of all corresponding fields between 2 Vecs.+vmax :: Vec -> Vec -> Vec+vmax !(Vec x1 y1 z1) !(Vec x2 y2 z2) =+ Vec (fmax x1 x2)+ (fmax y1 y2)+ (fmax z1 z2)++-- | Get the minimum of all corresponding fields between 2 Vecs.+vmin :: Vec -> Vec -> Vec+vmin !(Vec x1 y1 z1) !(Vec x2 y2 z2) =+ Vec (fmin x1 x2)+ (fmin y1 y2)+ (fmin z1 z2)++-- | Return the largest axis. Often used with "va".+vmaxaxis :: Vec -> Int+vmaxaxis !(Vec x y z) =+ if (x > y) + then if (x > z) + then 0+ else 2+ else if (y > z) + then 1+ else 2++-- | Scale a Vec by some value.+vscale :: Vec -> Flt -> Vec+vscale !(Vec x y z) !fac =+ Vec (x * fac)+ (y * fac)+ (z * fac)++-- | Take the first Vec, and add to it the second Vec scaled by some amount.+-- This is used quite a lot in Glome.+vscaleadd :: Vec -> Vec -> Flt -> Vec+vscaleadd !(Vec x1 y1 z1) !(Vec x2 y2 z2) fac =+ Vec (x1 + (x2 * fac))+ (y1 + (y2 * fac))+ (z1 + (z2 * fac))+ +-- | Make the length of a Vec just a little shorter.+vnudge :: Vec -> Vec+vnudge x = vscale x (1-delta)++-- | Normalize a vector. Division is expensive, so we compute the reciprocol +-- of the length and multiply by that. The sqrt is also expensive.+vnorm :: Vec -> Vec+vnorm !(Vec x1 y1 z1) = + let !invlen = 1.0 / (sqrt ((x1*x1)+(y1*y1)+(z1*z1))) in+ Vec (x1*invlen) (y1*invlen) (z1*invlen)++-- | Throw an exception if a vector hasn't been normalized.+assert_norm :: Vec -> Vec+assert_norm v =+ let l = vdot v v+ in if l > (1+delta) + then error $ "vector too long" ++ (show v)+ else if l < (1-delta)+ then error $ "vector too short: " ++ (show v)+ else v++-- | Get the victor bisecting two other vectors (which ought to be the same+-- length).+bisect :: Vec -> Vec -> Vec+bisect !v1 !v2 = vnorm (vadd v1 v2)++-- | Distance between 2 vectors.+vdist :: Vec -> Vec -> Flt+vdist v1 v2 = + let d = vsub v2 v1 in vlen d++-- | Reflect a vector "v" off of a surface with normal "norm".+reflect :: Vec -> Vec -> Vec+reflect !v !norm =+ -- vadd v $ vscale norm $ (-2) * (vdot v norm)+ vscaleadd v norm $ (-2) * (vdot v norm)++-- | Reciprocol of all fields of a Vec.+vrcp :: Vec -> Vec+vrcp !(Vec x y z) =+ Vec (1/x) (1/y) (1/z)++-- | Test Vecs for approximate equality+veq :: Vec -> Vec -> Bool+veq !(Vec ax ay az) !(Vec bx by bz) =+ (about_equal ax bx) && (about_equal ay by) && (about_equal az bz)++-- | Test Vecs for matching sign on all fields. Returns false if any value is+-- zero. Used by packet tracing.+veqsign :: Vec -> Vec -> Bool+veqsign !(Vec ax ay az) !(Vec bx by bz) =+ ax*bx > 0 && ay*by > 0 && az*bz > 0++-- | Translate a ray's origin in ray's direction by d amount.+ray_move :: Ray -> Flt -> Ray+ray_move !(Ray orig dir) !d =+ (Ray (vscaleadd orig dir d) dir)++-- | Find a pair of orthogonal vectors to the one given.+orth :: Vec -> (Vec,Vec)+orth v1 =+ if about_equal (vdot v1 v1) 1+ then+ let x = (Vec 1 0 0)+ y = (Vec 0 1 0)+ dvx = vdot v1 x+ v2 = if dvx < 0.8 && dvx > (-0.8) -- don't want to cross with a+ then vnorm $ vcross v1 x -- vector that's too similar+ else vnorm $ vcross v1 y+ v3 = vcross v1 v2+ in (v2,v3)+ else error $ "orth: unnormalized vector" ++ (show v1)++-- | Intersect a ray with a plane +-- defined by a point "p" and a normal "norm".+-- (Ray does not need to be normalized.)+plane_int :: Ray -> Vec -> Vec -> Vec+plane_int !(Ray orig dir) !p !norm =+ let newo = vsub orig p+ dist = -(vdot norm newo) / (vdot norm dir)+ in vscaleadd orig dir dist++-- | Find the distance along a ray until it intersects with a plane defined+-- by a point "p" and normal "norm".+plane_int_dist :: Ray -> Vec -> Vec -> Flt+plane_int_dist !(Ray orig dir) !p !norm =+ let newo = vsub orig p+ in -(vdot norm newo) / (vdot norm dir)++-- find intersection with plane+-- from graphics gems -- an efficient ray-polygon intersection+-- it seems that the ray need not be normalized+-- let plane_intersect ray (n,d) =+-- let t = -.((d +. (vdot n ray.origin)) /. (vdot n ray.dir))+-- in vadd ray.origin (vscale ray.dir t)+++-- TRANSFORMATIONS --++-- | 3x4 Transformation matrix. These are described in most graphics texts.+data Matrix = Matrix !Flt !Flt !Flt !Flt + !Flt !Flt !Flt !Flt + !Flt !Flt !Flt !Flt deriving Show++-- | A transformation. Inverting a matrix is expensive, so we keep a forward+-- transformation matrix and a reverse transformation matrix.+-- Note: This can be made a little faster if the matricies are non-strict.+data Xfm = Xfm Matrix Matrix deriving Show++-- | Identity matrix. Transforming a vector by this matrix does nothing.+ident_matrix :: Matrix+ident_matrix = (Matrix 1 0 0 0 0 1 0 0 0 0 1 0)++-- | Identity transformation.+ident_xfm :: Xfm+ident_xfm = Xfm ident_matrix ident_matrix++-- | Multiply two matricies. This is unrolled for efficiency, and it's also+-- a little bit easier (in my opinion) to see what's going on.+mat_mult :: Matrix -> Matrix -> Matrix+mat_mult (Matrix a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23)+ (Matrix b00 b01 b02 b03 b10 b11 b12 b13 b20 b21 b22 b23) =+ Matrix+ (a00*b00 + a01*b10 + a02*b20)+ (a00*b01 + a01*b11 + a02*b21)+ (a00*b02 + a01*b12 + a02*b22)+ (a00*b03 + a01*b13 + a02*b23 + a03)++ (a10*b00 + a11*b10 + a12*b20)+ (a10*b01 + a11*b11 + a12*b21)+ (a10*b02 + a11*b12 + a12*b22)+ (a10*b03 + a11*b13 + a12*b23 + a13)++ (a20*b00 + a21*b10 + a22*b20)+ (a20*b01 + a21*b11 + a22*b21)+ (a20*b02 + a21*b12 + a22*b22)+ (a20*b03 + a21*b13 + a22*b23 + a23)++-- | Multiply two tranformations. This just multiplies the forward and +-- reverse transformations.+xfm_mult :: Xfm -> Xfm -> Xfm+xfm_mult (Xfm a inva) (Xfm b invb) =+ Xfm (mat_mult a b) (mat_mult invb inva)++-- TRANSFORM UTILITY FUNCTIONS --++-- | There is a seemingly-magical property of transformation matricies, that+-- we can combine the effects of any number of transformations into a single+-- transformation just by multiplying them together in reverse order. For +-- instance, we could move a point, then rotate it about the origin by some +-- angle around some vector, then move it again, and this can all be done by +-- a single transformation.+-- This function combines transformations in this way, though it reverses the+-- list first so the transformations take effect in their expected order.+compose :: [Xfm] -> Xfm+compose xfms = check_xfm $ foldr xfm_mult ident_xfm (reverse xfms)++-- | Make sure a transformation is valid. Multipy the forward and reverse+-- matrix and verify that the result is the identity matrix.+check_xfm :: Xfm -> Xfm+check_xfm (Xfm m i) = + let (Matrix m00 m01 m02 m03 + m10 m11 m12 m13 + m20 m21 m22 m23) = mat_mult m i+ ae = about_equal+ in+ if ae m00 1 && ae m01 0 && ae m02 0 && ae m03 0 &&+ ae m10 0 && ae m11 1 && ae m12 0 && ae m13 0 &&+ ae m20 0 && ae m21 0 && ae m22 1 && ae m23 0+ then (Xfm m i)+ else error $ "corrupt matrix " ++ (show (Xfm m i)) ++ "\n" ++ (show (mat_mult m i)) ++-- | Complex transformations: Rotate point (or vector) "pt" about ray by +-- angle c. The angle is in radians,+-- but using the angle conversion routines "deg", "rad" and "rot" is +-- recommended.+vrotate :: Vec -> Ray -> Flt -> Vec+vrotate pt (Ray orig axis_) angle =+ let axis = assert_norm axis_+ transform = compose [ translate (vinvert orig)+ , rotate axis angle+ , translate orig+ ]+ new_pt = xfm_point transform pt+ in if about_equal (vlen (vsub orig pt)) (vlen (vsub orig new_pt))+ then new_pt+ else error $ "something is wrong with vrotate" ++ + (show $ vlen (vsub orig pt)) ++ " " ++ + (show $ vlen (vsub orig new_pt))+++-- TRANSFORM APPLICATION --+-- these need to be fast++-- | Transform a point. The point is treated as (x y z 1).+xfm_point :: Xfm -> Vec -> Vec+xfm_point !(Xfm (Matrix m00 m01 m02 m03 + m10 m11 m12 m13 + m20 m21 m22 m23) inv) + !(Vec x y z) =+ Vec (m00*x + m01*y + m02*z + m03)+ (m10*x + m11*y + m12*z + m13)+ (m20*x + m21*y + m22*z + m23)++-- | Inverse transform a point.+invxfm_point :: Xfm -> Vec -> Vec+invxfm_point !(Xfm fwd (Matrix i00 i01 i02 i03 + i10 i11 i12 i13 + i20 i21 i22 i23)) + !(Vec x y z) =+ Vec (i00*x + i01*y + i02*z + i03)+ (i10*x + i11*y + i12*z + i13)+ (i20*x + i21*y + i22*z + i23)++-- | Transform a vector. The vector is treated as (x y z 0).+xfm_vec :: Xfm -> Vec -> Vec+xfm_vec !(Xfm (Matrix m00 m01 m02 m03 + m10 m11 m12 m13 + m20 m21 m22 m23) inv) + !(Vec x y z) =+ Vec (m00*x + m01*y + m02*z)+ (m10*x + m11*y + m12*z)+ (m20*x + m21*y + m22*z)++-- | Inverse transform a vector.+invxfm_vec :: Xfm -> Vec -> Vec+invxfm_vec !(Xfm fwd (Matrix i00 i01 i02 i03 + i10 i11 i12 i13 + i20 i21 i22 i23)) + !(Vec x y z) =+ Vec (i00*x + i01*y + i02*z)+ (i10*x + i11*y + i12*z)+ (i20*x + i21*y + i22*z)++-- | Inverse transform a normal. This one is tricky: we need to transform +-- by the inverse transpose.+invxfm_norm :: Xfm -> Vec -> Vec+invxfm_norm !(Xfm fwd (Matrix i00 i01 i02 i03 + i10 i11 i12 i13 + i20 i21 i22 i23)) + !(Vec x y z) =+ Vec (i00*x + i10*y + i20*z)+ (i01*x + i11*y + i21*z)+ (i02*x + i12*y + i22*z)++-- | Transform a Ray.+xfm_ray :: Xfm -> Ray -> Ray+xfm_ray !xfm !(Ray orig dir) =+ Ray (xfm_point xfm orig) (vnorm (xfm_vec xfm dir))++-- | Inverse transform a Ray.+invxfm_ray :: Xfm -> Ray -> Ray+invxfm_ray !xfm !(Ray orig dir) =+ Ray (invxfm_point xfm orig) (vnorm (invxfm_vec xfm dir))++-- BASIC TRANSFORMS --+-- | Basic transforms: move by some displacement vector.+translate :: Vec -> Xfm+translate (Vec x y z) =+ check_xfm $ Xfm (Matrix 1 0 0 x 0 1 0 y 0 0 1 z) + (Matrix 1 0 0 (-x) 0 1 0 (-y) 0 0 1 (-z))++-- | Basic transforms: stretch along the three axes, by the amount+-- in the given vector. (If x==y==z, then it's uniform scaling.)+scale :: Vec -> Xfm+scale (Vec x y z) =+ check_xfm $ Xfm (Matrix x 0 0 0 0 y 0 0 0 0 z 0)+ (Matrix (1/x) 0 0 0 0 (1/y) 0 0 0 0 (1/z) 0)++-- | Basic transforms: rotate about a given axis by some angle.+rotate :: Vec -> Flt -> Xfm+rotate v@(Vec x y z) angle =+ if not $ (vlen v) `about_equal` 1+ then error $ "please use a normalized vector for rotation: " ++ (show (vlen v))+ else + let s = sin angle+ c = cos angle ++ m00 = ((x*x)+((1-(x*x))*c)) + m01 = (((x*y)*(1-c))-(z*s)) + m02 = ((x*z*(1-c))+(y*s))++ m10 = (((x*y)*(1-c))+(z*s))+ m11 = ((y*y)+((1-(y*y))*c))+ m12 = ((y*z*(1-c))-(x*s))++ m20 = ((x*z*(1-c))-(y*s))+ m21 = ((y*z*(1-c))+(x*s))+ m22 = ((z*z)+((1-(z*z))*c))+ in+ check_xfm $ Xfm (Matrix m00 m01 m02 0 m10 m11 m12 0 m20 m21 m22 0)+ (Matrix m00 m10 m20 0 m01 m11 m21 0 m02 m12 m22 0)++-- | Basic transforms: Convert coordinate system from canonical xyz +-- coordinates to uvw coordinates.+xyz_to_uvw :: Vec -> Vec -> Vec -> Xfm+xyz_to_uvw u v w =+ let Vec ux uy uz = u+ Vec vx vy vz = v+ Vec wx wy wz = w+ in if (vdot u u) `about_equal` 1+ then+ if (vdot v v) `about_equal` 1+ then+ if (vdot w w) `about_equal` 1+ then + if ((vdot u v) `about_equal` 0) && + ((vdot u w) `about_equal` 0) && + ((vdot v w) `about_equal` 0)+ then+ check_xfm $ Xfm (Matrix ux vx wx 0 uy vy wy 0 uz vz wz 0)+ (Matrix ux uy uz 0 vx vy vz 0 wx wy wz 0)+ else error "vectors aren't orthogonal"+ else error $ "unnormalized w " ++ (show w)+ else error $ "unnormalized v " ++ (show v)+ else error $ "unnormalized u " ++ (show u)++-- | Basic transforms: Convert from uvw coordinates back to normal xyz +-- coordinates.+uvw_to_xyz :: Vec -> Vec -> Vec -> Xfm+uvw_to_xyz (Vec ux uy uz) (Vec vx vy vz) (Vec wx wy wz) =+ check_xfm $ Xfm (Matrix ux uy uz 0 vx vy vz 0 wx wy wz 0)+ (Matrix ux vx wx 0 uy vy wy 0 uz vz wz 0)++++-- TRIANGLE UTILITY FUNCTIONS --++-- | Given a side, angle, and side of a triangle, produce the length of the +-- opposite side.+sas2s :: Flt -> Flt -> Flt -> Flt+sas2s s1 a s2 =+ sqrt (((s1 * s1) + (s2 * s2)) - ((2 * s1 * s2 * (dcos a))))++++-- BOUNDING BOXES --+-- | Axis-aligned Bounding Box (AABB), defined by opposite corners. P1 is the+-- min values, p2 has the max values.+data Bbox = Bbox {p1 :: !Vec, p2 :: !Vec} deriving Show++-- | A near-far pair of distances. Basically just a tuple.+data Interval = Interval !Flt !Flt deriving Show -- used instead of a tuple++-- | Bounding box that encloses two bounding boxes.+bbjoin :: Bbox -> Bbox -> Bbox+bbjoin (Bbox p1a p2a) (Bbox p1b p2b) =+ (Bbox (vmin p1a p1b) (vmax p2a p2b))++-- | Find the overlap of two bounding boxes.+bboverlap :: Bbox -> Bbox -> Bbox+bboverlap (Bbox p1a p2a) (Bbox p1b p2b) =+ (Bbox (vmax p1a p1b) (vmin p2a p2b))++-- | Test if a Vec is inside the bounding box.+bbinside :: Bbox -> Vec -> Bool+bbinside (Bbox (Vec p1x p1y p1z) (Vec p2x p2y p2z)) (Vec x y z) =+ p1x <= x && x <= p2x && p1y <= y && y <= p2y && p1z <= z && z <= p2z++-- | Split a bounding box into two, given an axis and offset. Throw exception+-- if the offset isn't inside the bounding box.+bbsplit :: Bbox -> Int -> Flt -> (Bbox,Bbox)+bbsplit (Bbox p1 p2) axis offset =+ if (offset < (va p1 axis)) || (offset > (va p2 axis))+ then error "degenerate bounding box split"+ else ((Bbox p1 (vset p2 axis offset)),+ (Bbox (vset p1 axis offset) p2))++-- | Generate a minimum bounding box that encloses a list of points.+bbpts :: [Vec] -> Bbox+bbpts [] = empty_bbox+bbpts ((Vec x y z):[]) =+ Bbox (Vec (x-delta) (y-delta) (z-delta)) + (Vec (x+delta) (y+delta) (z+delta))++bbpts ((Vec x y z):pts) =+ let (Bbox (Vec p1x p1y p1z) (Vec p2x p2y p2z)) = bbpts pts+ minx = fmin (x-delta) p1x+ miny = fmin (y-delta) p1y+ minz = fmin (z-delta) p1z+ maxx = fmax (x+delta) p2x+ maxy = fmax (y+delta) p2y+ maxz = fmax (z+delta) p2z in+ Bbox (Vec minx miny minz) (Vec maxx maxy maxz)++-- | Surface area of a bounding box. Useful for cost heuristics when attempting+-- to build optimal bounding box heirarchies. Undefined for degenerate bounding+-- boxes.+bbsa :: Bbox -> Flt+bbsa (Bbox p1 p2) =+ let Vec dx dy dz = vsub p2 p1 + in dx*dy + dx*dz + dy*dz++-- | Volume of a bounding box. Undefined for degenerate bounding boxes.+bbvol :: Bbox -> Flt+bbvol (Bbox p1 p2) =+ let (Vec dx dy dz) = vsub p2 p1+ in dx*dy*dz++-- | Degenerate bounding box that contains an empty volume.+empty_bbox :: Bbox+empty_bbox = + Bbox (Vec infinity infinity infinity) + (Vec (-infinity) (-infinity) (-infinity))++-- | "Infinite" bounding box.+everything_bbox :: Bbox+everything_bbox =+ Bbox (Vec (-infinity) (-infinity) (-infinity))+ (Vec infinity infinity infinity)++-- | Find a ray's entrance and exit from a bounding +-- box. If last entrance is before the first exit,+-- we hit. Otherwise, we miss. (It's up to the +-- caller to figure that out.)++bbclip :: Ray -> Bbox -> Interval+bbclip (Ray (Vec ox oy oz) (Vec dx dy dz)) + (Bbox (Vec p1x p1y p1z) (Vec p2x p2y p2z)) =+ let dxrcp = 1/dx+ dyrcp = 1/dy+ dzrcp = 1/dz+ Interval inx outx = if dx > 0 + then Interval ((p1x-ox)*dxrcp) ((p2x-ox)*dxrcp)+ else Interval ((p2x-ox)*dxrcp) ((p1x-ox)*dxrcp)+ Interval iny outy = if dy > 0+ then Interval ((p1y-oy)*dyrcp) ((p2y-oy)*dyrcp)+ else Interval ((p2y-oy)*dyrcp) ((p1y-oy)*dyrcp)+ Interval inz outz = if dz > 0+ then Interval ((p1z-oz)*dzrcp) ((p2z-oz)*dzrcp)+ else Interval ((p2z-oz)*dzrcp) ((p1z-oz)*dzrcp)+ in+ Interval (fmax3 inx iny inz) (fmin3 outx outy outz)++
− Data/GlomeTexture.hs
@@ -1,126 +0,0 @@-{--module SolidTexture (square_wave, triangle_wave, sine_wave, - stripe, noise, turbulence- ) where -}--module Data.GlomeTexture where-import Data.GlomeVec-import Data.Array.IArray---- INTERPOLATION FUNCTIONS ---square_wave :: Flt -> Flt-square_wave x =- let offset = x - (fromIntegral (floor x))- in if offset < 0.5 then 0 else 1--triangle_wave :: Flt -> Flt-triangle_wave x =- let offset = x - (fromIntegral (floor x))- in if offset < 0.5 - then (offset*2)- else (2-(offset*2))--sine_wave :: Flt -> Flt-sine_wave x = (sin (x*2*pi))*0.5 + 0.5---lump_wave :: Flt -> Flt-lump_wave x = 1 - x*x*x---- SCALAR TEXTURE FUNCTIONS ------ These are simple solid texture functions that take a --- point as argument and return a number 0 < n < 1--stripe :: Vec -> (Flt -> Flt) -> (Vec -> Flt)-stripe axis interp =- let len = vlen axis - in- (\pos -> let offset = vdot pos axis - in interp offset)----- PERLIN NOISE ------ (-6 t^5 + 15 t^4 - 10t^3 +1)--- "realistic ray tracing 2nd edition" inconsistent --- on whether it should be t^5 or t^6,--- but t^5 works and t^6 doesn't.-omega :: Flt -> Flt-omega t_ = - let t = fabs t_- tsqr = t*t- tcube = tsqr*t- in (-6)*tcube*tsqr + 15*tcube*t - 10*tcube + 1---- questionably random-phi :: Array Int Int-phi = listArray (0,11) [3,0,2,7,4,1,5,11,8,10,9,6]--grad :: Array Int Vec-grad = listArray (0,11) - $ filter (\x -> let l = vlen x in l < 1.5 && l > 1.1) - [Vec x y z | x <- [(-1),0,1],- y <- [(-1),0,1],- z <- [(-1),0,1]] --gamma :: Int -> Int -> Int -> Vec-gamma i j k =- let a = phi!(mod (iabs k) 12)- b = phi!(mod (iabs (j+a)) 12)- c = phi!(mod (iabs (i+b)) 12)- in grad!c--knot :: Int -> Int -> Int -> Vec -> Flt-knot i j k v =- let Vec x y z = v- in (omega x) * (omega y) * (omega z) * (vdot (gamma i j k) v)--intGamma :: Int -> Int -> Int-intGamma i j =- let a = phi!(mod (iabs j) 16)- b = phi!(mod (iabs (i+a)) 16)- in b--turbulence :: Vec -> Int -> Flt-turbulence p 1 = fabs(noise(p))-turbulence p n =- let newp = vscale p 0.5- t = fabs (noise p)- in t + (0.5 * (turbulence newp (n-1)))--noise :: Vec -> Flt -noise (Vec x y z) =- let i = floor x- j = floor y- k = floor z- u = x-(fromIntegral i)- v = y-(fromIntegral j)- w = z-(fromIntegral k)- in knot i j k (Vec u v w) +- knot (i+1) j k (Vec (u-1) v w) +- knot i (j+1) k (Vec u (v-1) w) +- knot i j (k+1) (Vec u v (w-1)) +- knot (i+1) (j+1) k (Vec (u-1) (v-1) w) +- knot (i+1) j (k+1) (Vec (u-1) v (w-1)) +- knot i (j+1) (k+1) (Vec u (v-1) (w-1)) +- knot (i+1) (j+1) (k+1) (Vec (u-1) (v-1) (w-1))--perlin :: Vec -> Flt-perlin v =- let p = ((noise v)+1)*0.5- in if p > 1 - then error $ "perlin noise error, 1 < " ++ (show p)- else if p < 0 - then error $ "perlin noise error, 0 > " ++ (show p)- else p----untested-perlin_turb :: Vec -> Int -> Flt-perlin_turb v l =- let p = turbulence v l- in if p > 1 - then error $ "perlin turbulence error, 1 < " ++ (show p)- else if p < 0 - then error $ "perlin turbulence error, 0 > " ++ (show p)- else p
− Data/GlomeVec.hs
@@ -1,738 +0,0 @@-{-# OPTIONS_GHC -fexcess-precision #-}-{-# OPTIONS_GHC -funbox-strict-fields #-}-{-# LANGUAGE BangPatterns #-}--module Data.GlomeVec where---- | Performance is pretty similar with Floats or Doubles.--- Todo: make separate Float and Double instances of this library.-type Flt = Double---- maybe this is defined somewhere?-infinity :: Flt---infinity = 1.0 / 0.0-infinity = 1000000.0---- | Convert from degrees to native angle format (radians).-deg :: Flt -> Flt-deg !x = (x*3.1415926535897)/180---- | Convert from radians to native format (noop).-rad :: Flt -> Flt-rad !x = x---- | Convert from rotations to native format. (rot 1 == deg 360)-rot :: Flt -> Flt-rot !x = x*3.1415926535897*2---- | Trig with degrees instead of radians.-dcos :: Flt -> Flt-dcos d = cos $ deg d---- | Force a value to be within a range. Usage: clamp min x max-clamp :: Flt -> Flt -> Flt -> Flt-clamp !min !x !max- | x < min = min- | x > max = max- | otherwise = x---- | Tuning parameter.-delta = 0.0001 :: Flt---- | Non-polymorphic fmin; this speeds--- things up in ocaml, not sure about haskell.-fmin :: Flt -> Flt -> Flt-fmin !a !b = if a > b then b else a---- | Non-polymorphic fmax.-fmax :: Flt -> Flt -> Flt-fmax !a !b = if a > b then a else b---- | Non-polymorphic min of 3 values.-fmin3 :: Flt -> Flt -> Flt -> Flt-fmin3 !a !b !c = if a > b - then if b > c - then c- else b- else if a > c- then c- else a---- | Non-polymorphic max of 3 values.-fmax3 :: Flt -> Flt -> Flt -> Flt-fmax3 !a !b !c = if a > b- then if a > c- then a- else c- else if b > c- then b- else c---- | Min of 4 values.-fmin4 :: Flt -> Flt -> Flt -> Flt -> Flt-fmin4 !a !b !c !d = fmin (fmin a b) (fmin c d)---- | Max of 4 values.-fmax4 :: Flt -> Flt -> Flt -> Flt -> Flt-fmax4 !a !b !c !d = fmax (fmax a b) (fmax c d)---- | Non-polymorphic absolute value.-fabs :: Flt -> Flt-fabs !a = - if a < 0 then (-a) else a---- | Non-polymorphic integer absolute value.-iabs :: Int -> Int-iabs !a =- if a < 0 then (-a) else a---- | Force user to use fabs or iabs, for performance reasons. Not sure if--- this really helps, though.-abs a = error "use non-polymorphic version, fabs"---- | Approximate equality for Flt. True if a and b are "almost" equal.--- The (abs $ a-b) test doesn't work if--- a and b are large.-about_equal :: Flt -> Flt -> Bool-about_equal !a !b =- if a > 1 - then- fabs (1 - (a/b)) < (delta*10) - else- (fabs $ a - b) < (delta*10)---- | 3d type represented as a record of unboxed floats.-data Vec = Vec !Flt !Flt !Flt deriving Show---- | A Ray is made up of an origin and direction Vec.-data Ray = Ray {origin, dir :: !Vec} deriving Show---data Plane = Plane {norm :: !Vec, offset :: !Flt} deriving Show---- | Vec constructor.-vec :: Flt -> Flt -> Flt -> Vec-vec !x !y !z = (Vec x y z)---- | Zero Vec.-vzero :: Vec-vzero = Vec 0.0 0.0 0.0---- | For when we need a unit vector, but we --- don't care where it points.-vunit :: Vec-vunit = vx---- | Unit X vector.-vx :: Vec-vx = Vec 1 0 0---- | Unit y vector.-vy :: Vec-vy = Vec 0 1 0---- | Unit z vector.-vz :: Vec-vz = Vec 0 0 1---- | Negative x vector.-nvx :: Vec-nvx = Vec (-1) 0 0---- | Negative y vector.-nvy :: Vec-nvy = Vec 0 (-1) 0---- | Negative z vector.-nvz :: Vec-nvz = Vec 0 0 (-1)---- Extract x coordinate.-x :: Vec -> Flt-x (Vec x_ _ _) = x_---- Extract y coordinate.-y :: Vec -> Flt-y (Vec _ y_ _) = y_---- Extract z coordinate.-z :: Vec -> Flt-z (Vec _ _ z_) = z_---- | Access the Vec as if it were an array indexed from 0..2.--- Note: this actually accounts for a noticeable amount of cpu --- time in the Glome ray tracer.-va :: Vec -> Int -> Flt-va !(Vec x y z) !n = - case n of- 0 -> x- 1 -> y- 2 -> z---- | Create a new Vec with the Nth field overwritten by new value.--- I could have used record update syntax.-vset :: Vec -> Int -> Flt -> Vec-vset !(Vec x y z) !i !f =- case i of- 0 -> Vec f y z- 1 -> Vec x f z- 2 -> Vec x y f---- | Dot product of 2 vectors. We use this all the time. Dot product of 2--- normal vectors is the cosine of the angle between them.-vdot :: Vec -> Vec -> Flt-vdot !(Vec x1 y1 z1) !(Vec x2 y2 z2) =- (x1*x2)+(y1*y2)+(z1*z2)---- | Cross product of 2 vectors. Produces a vector perpendicular --- to the given vectors. We use this for things like making the forward,--- up, and right camera vectors orthogonal. If the input vectors are--- normalized, the output vector will be as well.-vcross :: Vec -> Vec -> Vec-vcross !(Vec x1 y1 z1) !(Vec x2 y2 z2) =- Vec - ((y1 * z2) - (z1 * y2))- ((z1 * x2) - (x1 * z2))- ((x1 * y2) - (y1 * x2))---- | Apply a unary Flt operator to each field of the Vec.-vmap :: (Flt -> Flt) -> Vec -> Vec-vmap f !v1 = - Vec (f (x v1)) (f (y v1)) (f (z v1))---- | Apply a binary Flt operator to pairs of fields from 2 Vecs.-vmap2 :: (Flt -> Flt -> Flt) -> Vec -> Vec -> Vec-vmap2 f !v1 !v2 =- Vec (f (x v1) (x v2)) - (f (y v1) (y v2)) - (f (z v1) (z v2))---- | Reverse the direction of a Vec.-vinvert :: Vec -> Vec-vinvert !(Vec x1 y1 z1) =- Vec (-x1) (-y1) (-z1)---- | Get the length of a Vec squared. We use this to avoid a slow sqrt. -vlensqr :: Vec -> Flt-vlensqr !v1 = vdot v1 v1---- | Get the length of a Vec. This is expensive because sqrt is slow.-vlen :: Vec -> Flt-vlen !v1 = sqrt (vdot v1 v1)---- | Add 2 vectors.-vadd :: Vec -> Vec -> Vec-vadd !(Vec x1 y1 z1) !(Vec x2 y2 z2) =- Vec (x1 + x2)- (y1 + y2)- (z1 + z2)---- | Add 3 vectors.-vadd3 :: Vec -> Vec -> Vec -> Vec-vadd3 !(Vec x1 y1 z1) !(Vec x2 y2 z2) !(Vec x3 y3 z3) =- Vec (x1 + x2 + x3)- (y1 + y2 + y3)- (z1 + z2 + z3)---- | Subtract vectors. "vsub b a" is the vector from a to b.-vsub :: Vec -> Vec -> Vec-vsub !(Vec x1 y1 z1) !(Vec x2 y2 z2) =- Vec (x1 - x2)- (y1 - y2)- (z1 - z2)---- | Multiply corresponding fields. Rarely useful.-vmul :: Vec -> Vec -> Vec-vmul !(Vec x1 y1 z1) !(Vec x2 y2 z2) =- Vec (x1 * x2)- (y1 * y2)- (z1 * z2)---- | Add a value to all the fields of a Vec. Useful, for instance, to get--- one corner of the bounding box around a sphere.-vinc :: Vec -> Flt -> Vec-vinc !(Vec x y z) !n =- Vec (x + n)- (y + n)- (z + n)---- | Subtract a value from all fields of a Vec.-vdec :: Vec -> Flt -> Vec-vdec !(Vec x y z) !n =- Vec (x - n)- (y - n)- (z - n)---- | Get the maximum of all corresponding fields between 2 Vecs.-vmax :: Vec -> Vec -> Vec-vmax !(Vec x1 y1 z1) !(Vec x2 y2 z2) =- Vec (fmax x1 x2)- (fmax y1 y2)- (fmax z1 z2)---- | Get the minimum of all corresponding fields between 2 Vecs.-vmin :: Vec -> Vec -> Vec-vmin !(Vec x1 y1 z1) !(Vec x2 y2 z2) =- Vec (fmin x1 x2)- (fmin y1 y2)- (fmin z1 z2)---- | Return the largest axis. Often used with "va".-vmaxaxis :: Vec -> Int-vmaxaxis !(Vec x y z) =- if (x > y) - then if (x > z) - then 0- else 2- else if (y > z) - then 1- else 2---- | Scale a Vec by some value.-vscale :: Vec -> Flt -> Vec-vscale !(Vec x y z) !fac =- Vec (x * fac)- (y * fac)- (z * fac)---- | Take the first Vec, and add to it the second Vec scaled by some amount.--- This is used quite a lot in Glome.-vscaleadd :: Vec -> Vec -> Flt -> Vec-vscaleadd !(Vec x1 y1 z1) !(Vec x2 y2 z2) fac =- Vec (x1 + (x2 * fac))- (y1 + (y2 * fac))- (z1 + (z2 * fac))- --- | Make the length of a Vec just a little shorter.-vnudge :: Vec -> Vec-vnudge x = vscale x (1-delta)---- | Normalize a vector. Division is expensive, so we compute the reciprocol --- of the length and multiply by that. The sqrt is also expensive.-vnorm :: Vec -> Vec-vnorm !(Vec x1 y1 z1) = - let !invlen = 1.0 / (sqrt ((x1*x1)+(y1*y1)+(z1*z1))) in- Vec (x1*invlen) (y1*invlen) (z1*invlen)---- | Throw an exception if a vector hasn't been normalized.-assert_norm :: Vec -> Vec-assert_norm v =- let l = vdot v v- in if l > (1+delta) - then error $ "vector too long" ++ (show v)- else if l < (1-delta)- then error $ "vector too short: " ++ (show v)- else v---- | Get the victor bisecting two other vectors (which ought to be the same--- length).-bisect :: Vec -> Vec -> Vec-bisect !v1 !v2 = vnorm (vadd v1 v2)---- | Distance between 2 vectors.-vdist :: Vec -> Vec -> Flt-vdist v1 v2 = - let d = vsub v2 v1 in vlen d---- | Reflect a vector "v" off of a surface with normal "norm".-reflect :: Vec -> Vec -> Vec-reflect !v !norm =- -- vadd v $ vscale norm $ (-2) * (vdot v norm)- vscaleadd v norm $ (-2) * (vdot v norm)---- | Reciprocol of all fields of a Vec.-vrcp :: Vec -> Vec-vrcp !(Vec x y z) =- Vec (1/x) (1/y) (1/z)---- | Test Vecs for approximate equality-veq :: Vec -> Vec -> Bool-veq !(Vec ax ay az) !(Vec bx by bz) =- (about_equal ax bx) && (about_equal ay by) && (about_equal az bz)---- | Test Vecs for matching sign on all fields. Returns false if any value is--- zero. Used by packet tracing.-veqsign :: Vec -> Vec -> Bool-veqsign !(Vec ax ay az) !(Vec bx by bz) =- ax*bx > 0 && ay*by > 0 && az*bz > 0---- | Translate a ray's origin in ray's direction by d amount.-ray_move :: Ray -> Flt -> Ray-ray_move !(Ray orig dir) !d =- (Ray (vscaleadd orig dir d) dir)---- | Find a pair of orthogonal vectors to the one given.-orth :: Vec -> (Vec,Vec)-orth v1 =- if about_equal (vdot v1 v1) 1- then- let x = (Vec 1 0 0)- y = (Vec 0 1 0)- dvx = vdot v1 x- v2 = if dvx < 0.8 && dvx > (-0.8) -- don't want to cross with a- then vnorm $ vcross v1 x -- vector that's too similar- else vnorm $ vcross v1 y- v3 = vcross v1 v2- in (v2,v3)- else error $ "orth: unnormalized vector" ++ (show v1)---- | Intersect a ray with a plane --- defined by a point "p" and a normal "norm".--- (Ray does not need to be normalized.)-plane_int :: Ray -> Vec -> Vec -> Vec-plane_int !(Ray orig dir) !p !norm =- let newo = vsub orig p- dist = -(vdot norm newo) / (vdot norm dir)- in vscaleadd orig dir dist---- | Find the distance along a ray until it intersects with a plane defined--- by a point "p" and normal "norm".-plane_int_dist :: Ray -> Vec -> Vec -> Flt-plane_int_dist !(Ray orig dir) !p !norm =- let newo = vsub orig p- in -(vdot norm newo) / (vdot norm dir)---- find intersection with plane--- from graphics gems -- an efficient ray-polygon intersection--- it seems that the ray need not be normalized--- let plane_intersect ray (n,d) =--- let t = -.((d +. (vdot n ray.origin)) /. (vdot n ray.dir))--- in vadd ray.origin (vscale ray.dir t)----- TRANSFORMATIONS ------ | 3x4 Transformation matrix. These are described in most graphics texts.-data Matrix = Matrix !Flt !Flt !Flt !Flt - !Flt !Flt !Flt !Flt - !Flt !Flt !Flt !Flt deriving Show---- | A transformation. Inverting a matrix is expensive, so we keep a forward--- transformation matrix and a reverse transformation matrix.--- Note: This can be made a little faster if the matricies are non-strict.-data Xfm = Xfm Matrix Matrix deriving Show---- | Identity matrix. Transforming a vector by this matrix does nothing.-ident_matrix :: Matrix-ident_matrix = (Matrix 1 0 0 0 0 1 0 0 0 0 1 0)---- | Identity transformation.-ident_xfm :: Xfm-ident_xfm = Xfm ident_matrix ident_matrix---- | Multiply two matricies. This is unrolled for efficiency, and it's also--- a little bit easier (in my opinion) to see what's going on.-mat_mult :: Matrix -> Matrix -> Matrix-mat_mult (Matrix a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23)- (Matrix b00 b01 b02 b03 b10 b11 b12 b13 b20 b21 b22 b23) =- Matrix- (a00*b00 + a01*b10 + a02*b20)- (a00*b01 + a01*b11 + a02*b21)- (a00*b02 + a01*b12 + a02*b22)- (a00*b03 + a01*b13 + a02*b23 + a03)-- (a10*b00 + a11*b10 + a12*b20)- (a10*b01 + a11*b11 + a12*b21)- (a10*b02 + a11*b12 + a12*b22)- (a10*b03 + a11*b13 + a12*b23 + a13)-- (a20*b00 + a21*b10 + a22*b20)- (a20*b01 + a21*b11 + a22*b21)- (a20*b02 + a21*b12 + a22*b22)- (a20*b03 + a21*b13 + a22*b23 + a23)---- | Multiply two tranformations. This just multiplies the forward and --- reverse transformations.-xfm_mult :: Xfm -> Xfm -> Xfm-xfm_mult (Xfm a inva) (Xfm b invb) =- Xfm (mat_mult a b) (mat_mult invb inva)---- TRANSFORM UTILITY FUNCTIONS ------ | There is a seemingly-magical property of transformation matricies, that--- we can combine the effects of any number of transformations into a single--- transformation just by multiplying them together in reverse order. For --- instance, we could move a point, then rotate it about the origin by some --- angle around some vector, then move it again, and this can all be done by --- a single transformation.--- This function combines transformations in this way, though it reverses the--- list first so the transformations take effect in their expected order.-compose :: [Xfm] -> Xfm-compose xfms = check_xfm $ foldr xfm_mult ident_xfm (reverse xfms)---- | Make sure a transformation is valid. Multipy the forward and reverse--- matrix and verify that the result is the identity matrix.-check_xfm :: Xfm -> Xfm-check_xfm (Xfm m i) = - let (Matrix m00 m01 m02 m03 - m10 m11 m12 m13 - m20 m21 m22 m23) = mat_mult m i- ae = about_equal- in- if ae m00 1 && ae m01 0 && ae m02 0 && ae m03 0 &&- ae m10 0 && ae m11 1 && ae m12 0 && ae m13 0 &&- ae m20 0 && ae m21 0 && ae m22 1 && ae m23 0- then (Xfm m i)- else error $ "corrupt matrix " ++ (show (Xfm m i)) ++ "\n" ++ (show (mat_mult m i)) ---- | Complex transformations: Rotate point (or vector) "pt" about ray by --- angle c. The angle is in radians,--- but using the angle conversion routines "deg", "rad" and "rot" is --- recommended.-vrotate :: Vec -> Ray -> Flt -> Vec-vrotate pt (Ray orig axis_) angle =- let axis = assert_norm axis_- transform = compose [ translate (vinvert orig)- , rotate axis angle- , translate orig- ]- new_pt = xfm_point transform pt- in if about_equal (vlen (vsub orig pt)) (vlen (vsub orig new_pt))- then new_pt- else error $ "something is wrong with vrotate" ++ - (show $ vlen (vsub orig pt)) ++ " " ++ - (show $ vlen (vsub orig new_pt))----- TRANSFORM APPLICATION ----- these need to be fast---- | Transform a point. The point is treated as (x y z 1).-xfm_point :: Xfm -> Vec -> Vec-xfm_point !(Xfm (Matrix m00 m01 m02 m03 - m10 m11 m12 m13 - m20 m21 m22 m23) inv) - !(Vec x y z) =- Vec (m00*x + m01*y + m02*z + m03)- (m10*x + m11*y + m12*z + m13)- (m20*x + m21*y + m22*z + m23)---- | Inverse transform a point.-invxfm_point :: Xfm -> Vec -> Vec-invxfm_point !(Xfm fwd (Matrix i00 i01 i02 i03 - i10 i11 i12 i13 - i20 i21 i22 i23)) - !(Vec x y z) =- Vec (i00*x + i01*y + i02*z + i03)- (i10*x + i11*y + i12*z + i13)- (i20*x + i21*y + i22*z + i23)---- | Transform a vector. The vector is treated as (x y z 0).-xfm_vec :: Xfm -> Vec -> Vec-xfm_vec !(Xfm (Matrix m00 m01 m02 m03 - m10 m11 m12 m13 - m20 m21 m22 m23) inv) - !(Vec x y z) =- Vec (m00*x + m01*y + m02*z)- (m10*x + m11*y + m12*z)- (m20*x + m21*y + m22*z)---- | Inverse transform a vector.-invxfm_vec :: Xfm -> Vec -> Vec-invxfm_vec !(Xfm fwd (Matrix i00 i01 i02 i03 - i10 i11 i12 i13 - i20 i21 i22 i23)) - !(Vec x y z) =- Vec (i00*x + i01*y + i02*z)- (i10*x + i11*y + i12*z)- (i20*x + i21*y + i22*z)---- | Inverse transform a normal. This one is tricky: we need to transform --- by the inverse transpose.-invxfm_norm :: Xfm -> Vec -> Vec-invxfm_norm !(Xfm fwd (Matrix i00 i01 i02 i03 - i10 i11 i12 i13 - i20 i21 i22 i23)) - !(Vec x y z) =- Vec (i00*x + i10*y + i20*z)- (i01*x + i11*y + i21*z)- (i02*x + i12*y + i22*z)---- | Transform a Ray.-xfm_ray :: Xfm -> Ray -> Ray-xfm_ray !xfm !(Ray orig dir) =- Ray (xfm_point xfm orig) (vnorm (xfm_vec xfm dir))---- | Inverse transform a Ray.-invxfm_ray :: Xfm -> Ray -> Ray-invxfm_ray !xfm !(Ray orig dir) =- Ray (invxfm_point xfm orig) (vnorm (invxfm_vec xfm dir))---- BASIC TRANSFORMS ----- | Basic transforms: move by some displacement vector.-translate :: Vec -> Xfm-translate (Vec x y z) =- check_xfm $ Xfm (Matrix 1 0 0 x 0 1 0 y 0 0 1 z) - (Matrix 1 0 0 (-x) 0 1 0 (-y) 0 0 1 (-z))---- | Basic transforms: stretch along the three axes, by the amount--- in the given vector. (If x==y==z, then it's uniform scaling.)-scale :: Vec -> Xfm-scale (Vec x y z) =- check_xfm $ Xfm (Matrix x 0 0 0 0 y 0 0 0 0 z 0)- (Matrix (1/x) 0 0 0 0 (1/y) 0 0 0 0 (1/z) 0)---- | Basic transforms: rotate about a given axis by some angle.-rotate :: Vec -> Flt -> Xfm-rotate v@(Vec x y z) angle =- if not $ (vlen v) `about_equal` 1- then error $ "please use a normalized vector for rotation: " ++ (show (vlen v))- else - let s = sin angle- c = cos angle -- m00 = ((x*x)+((1-(x*x))*c)) - m01 = (((x*y)*(1-c))-(z*s)) - m02 = ((x*z*(1-c))+(y*s))-- m10 = (((x*y)*(1-c))+(z*s))- m11 = ((y*y)+((1-(y*y))*c))- m12 = ((y*z*(1-c))-(x*s))-- m20 = ((x*z*(1-c))-(y*s))- m21 = ((y*z*(1-c))+(x*s))- m22 = ((z*z)+((1-(z*z))*c))- in- check_xfm $ Xfm (Matrix m00 m01 m02 0 m10 m11 m12 0 m20 m21 m22 0)- (Matrix m00 m10 m20 0 m01 m11 m21 0 m02 m12 m22 0)---- | Basic transforms: Convert coordinate system from canonical xyz --- coordinates to uvw coordinates.-xyz_to_uvw :: Vec -> Vec -> Vec -> Xfm-xyz_to_uvw u v w =- let Vec ux uy uz = u- Vec vx vy vz = v- Vec wx wy wz = w- in if (vdot u u) `about_equal` 1- then- if (vdot v v) `about_equal` 1- then- if (vdot w w) `about_equal` 1- then - if ((vdot u v) `about_equal` 0) && - ((vdot u w) `about_equal` 0) && - ((vdot v w) `about_equal` 0)- then- check_xfm $ Xfm (Matrix ux vx wx 0 uy vy wy 0 uz vz wz 0)- (Matrix ux uy uz 0 vx vy vz 0 wx wy wz 0)- else error "vectors aren't orthogonal"- else error $ "unnormalized w " ++ (show w)- else error $ "unnormalized v " ++ (show v)- else error $ "unnormalized u " ++ (show u)---- | Basic transforms: Convert from uvw coordinates back to normal xyz --- coordinates.-uvw_to_xyz :: Vec -> Vec -> Vec -> Xfm-uvw_to_xyz (Vec ux uy uz) (Vec vx vy vz) (Vec wx wy wz) =- check_xfm $ Xfm (Matrix ux uy uz 0 vx vy vz 0 wx wy wz 0)- (Matrix ux vx wx 0 uy vy wy 0 uz vz wz 0)------ TRIANGLE UTILITY FUNCTIONS ------ | Given a side, angle, and side of a triangle, produce the length of the --- opposite side.-sas2s :: Flt -> Flt -> Flt -> Flt-sas2s s1 a s2 =- sqrt (((s1 * s1) + (s2 * s2)) - ((2 * s1 * s2 * (dcos a))))------ BOUNDING BOXES ----- | Axis-aligned Bounding Box (AABB), defined by opposite corners. P1 is the--- min values, p2 has the max values.-data Bbox = Bbox {p1 :: !Vec, p2 :: !Vec} deriving Show---- | A near-far pair of distances. Basically just a tuple.-data Interval = Interval !Flt !Flt deriving Show -- used instead of a tuple---- | Bounding box that encloses two bounding boxes.-bbjoin :: Bbox -> Bbox -> Bbox-bbjoin (Bbox p1a p2a) (Bbox p1b p2b) =- (Bbox (vmin p1a p1b) (vmax p2a p2b))---- | Find the overlap of two bounding boxes.-bboverlap :: Bbox -> Bbox -> Bbox-bboverlap (Bbox p1a p2a) (Bbox p1b p2b) =- (Bbox (vmax p1a p1b) (vmin p2a p2b))---- | Test if a Vec is inside the bounding box.-bbinside :: Bbox -> Vec -> Bool-bbinside (Bbox (Vec p1x p1y p1z) (Vec p2x p2y p2z)) (Vec x y z) =- p1x <= x && x <= p2x && p1y <= y && y <= p2y && p1z <= z && z <= p2z---- | Split a bounding box into two, given an axis and offset. Throw exception--- if the offset isn't inside the bounding box.-bbsplit :: Bbox -> Int -> Flt -> (Bbox,Bbox)-bbsplit (Bbox p1 p2) axis offset =- if (offset < (va p1 axis)) || (offset > (va p2 axis))- then error "degenerate bounding box split"- else ((Bbox p1 (vset p2 axis offset)),- (Bbox (vset p1 axis offset) p2))---- | Generate a minimum bounding box that encloses a list of points.-bbpts :: [Vec] -> Bbox-bbpts [] = empty_bbox-bbpts ((Vec x y z):[]) =- Bbox (Vec (x-delta) (y-delta) (z-delta)) - (Vec (x+delta) (y+delta) (z+delta))--bbpts ((Vec x y z):pts) =- let (Bbox (Vec p1x p1y p1z) (Vec p2x p2y p2z)) = bbpts pts- minx = fmin (x-delta) p1x- miny = fmin (y-delta) p1y- minz = fmin (z-delta) p1z- maxx = fmax (x+delta) p2x- maxy = fmax (y+delta) p2y- maxz = fmax (z+delta) p2z in- Bbox (Vec minx miny minz) (Vec maxx maxy maxz)---- | Surface area of a bounding box. Useful for cost heuristics when attempting--- to build optimal bounding box heirarchies. Undefined for degenerate bounding--- boxes.-bbsa :: Bbox -> Flt-bbsa (Bbox p1 p2) =- let Vec dx dy dz = vsub p2 p1 - in dx*dy + dx*dz + dy*dz---- | Volume of a bounding box. Undefined for degenerate bounding boxes.-bbvol :: Bbox -> Flt-bbvol (Bbox p1 p2) =- let (Vec dx dy dz) = vsub p2 p1- in dx*dy*dz---- | Degenerate bounding box that contains an empty volume.-empty_bbox :: Bbox-empty_bbox = - Bbox (Vec infinity infinity infinity) - (Vec (-infinity) (-infinity) (-infinity))---- | "Infinite" bounding box.-everything_bbox :: Bbox-everything_bbox =- Bbox (Vec (-infinity) (-infinity) (-infinity))- (Vec infinity infinity infinity)---- | Find a ray's entrance and exit from a bounding --- box. If last entrance is before the first exit,--- we hit. Otherwise, we miss. (It's up to the --- caller to figure that out.)--bbclip :: Ray -> Bbox -> Interval-bbclip (Ray (Vec ox oy oz) (Vec dx dy dz)) - (Bbox (Vec p1x p1y p1z) (Vec p2x p2y p2z)) =- let dxrcp = 1/dx- dyrcp = 1/dy- dzrcp = 1/dz- Interval inx outx = if dx > 0 - then Interval ((p1x-ox)*dxrcp) ((p2x-ox)*dxrcp)- else Interval ((p2x-ox)*dxrcp) ((p1x-ox)*dxrcp)- Interval iny outy = if dy > 0- then Interval ((p1y-oy)*dyrcp) ((p2y-oy)*dyrcp)- else Interval ((p2y-oy)*dyrcp) ((p1y-oy)*dyrcp)- Interval inz outz = if dz > 0- then Interval ((p1z-oz)*dzrcp) ((p2z-oz)*dzrcp)- else Interval ((p2z-oz)*dzrcp) ((p1z-oz)*dzrcp)- in- Interval (fmax3 inx iny inz) (fmin3 outx outy outz)--
GlomeVec.cabal view
@@ -1,5 +1,5 @@ Name: GlomeVec-Version: 0.1.1+Version: 0.1.2 Synopsis: Simple 3D vector library Description: A simple library for dealing with 3D vectors, suitable for graphics projects. A small texture library with Perlin noise is included as well. License: GPL@@ -14,7 +14,7 @@ Cabal-Version: >= 1.2 extra-source-files: README.txt- library- exposed-modules: Data.GlomeVec, Data.GlomeTexture+ exposed-modules: Data.Glome.Vec+ Data.Glome.Texture Build-Depends: base >= 3 && < 4, array