cube_properties,
find_containing_tetrahedron,
tetrahedra,
- tetrahedron
- )
+ tetrahedron )
where
-import Data.Maybe (fromJust)
+import Data.Maybe ( fromJust )
import qualified Data.Vector as V (
Vector,
findIndex,
minimum,
singleton,
snoc,
- unsafeIndex
- )
-import Prelude hiding (LT)
-import Test.Framework (Test, testGroup)
-import Test.Framework.Providers.QuickCheck2 (testProperty)
-import Test.QuickCheck (Arbitrary(..), Gen, Positive(..), choose)
-
-import Cardinal
-import Comparisons ((~=), (~~=))
-import qualified Face (Face(Face, v0, v1, v2, v3))
-import FunctionValues (FunctionValues, eval, rotate)
-import Misc (all_equal, disjoint)
-import Point (Point(..), dot)
-import Tetrahedron (Tetrahedron(..), c, volume)
-import ThreeDimensional
-
-data Cube = Cube { h :: !Double,
- i :: !Int,
+ unsafeIndex)
+import Prelude hiding ( LT )
+import Test.Framework ( Test, testGroup )
+import Test.Framework.Providers.QuickCheck2 ( testProperty )
+import Test.QuickCheck ( Arbitrary(..), Gen, Positive(..), choose )
+
+import Cardinal (
+ Cardinal(..),
+ ccwx,
+ ccwy,
+ ccwz,
+ cwx,
+ cwy,
+ cwz )
+import Comparisons ( (~=), (~~=) )
+import qualified Face ( Face(..), center )
+import FunctionValues ( FunctionValues, eval, rotate )
+import Misc ( all_equal, disjoint )
+import Point ( Point(..), dot )
+import Tetrahedron ( Tetrahedron(..), barycenter, c, volume )
+
+data Cube = Cube { i :: !Int,
j :: !Int,
k :: !Int,
fv :: !FunctionValues,
instance Arbitrary Cube where
arbitrary = do
- (Positive h') <- arbitrary :: Gen (Positive Double)
i' <- choose (coordmin, coordmax)
j' <- choose (coordmin, coordmax)
k' <- choose (coordmin, coordmax)
fv' <- arbitrary :: Gen FunctionValues
(Positive tet_vol) <- arbitrary :: Gen (Positive Double)
- return (Cube h' i' j' k' fv' tet_vol)
+ return (Cube i' j' k' fv' tet_vol)
where
-- The idea here is that, when cubed in the volume formula,
-- these numbers don't overflow 64 bits. This number is not
instance Show Cube where
show cube =
"Cube_" ++ subscript ++ "\n" ++
- " h: " ++ (show (h cube)) ++ "\n" ++
" Center: " ++ (show (center cube)) ++ "\n" ++
" xmin: " ++ (show (xmin cube)) ++ "\n" ++
" xmax: " ++ (show (xmax cube)) ++ "\n" ++
-- | The left-side boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
xmin :: Cube -> Double
-xmin cube = (i' - 1/2)*delta
+xmin cube = (i' - 1/2)
where
i' = fromIntegral (i cube) :: Double
- delta = h cube
-- | The right-side boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
xmax :: Cube -> Double
-xmax cube = (i' + 1/2)*delta
+xmax cube = (i' + 1/2)
where
i' = fromIntegral (i cube) :: Double
- delta = h cube
-- | The front boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
ymin :: Cube -> Double
-ymin cube = (j' - 1/2)*delta
+ymin cube = (j' - 1/2)
where
j' = fromIntegral (j cube) :: Double
- delta = h cube
-- | The back boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
ymax :: Cube -> Double
-ymax cube = (j' + 1/2)*delta
+ymax cube = (j' + 1/2)
where
j' = fromIntegral (j cube) :: Double
- delta = h cube
-- | The bottom boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
zmin :: Cube -> Double
-zmin cube = (k' - 1/2)*delta
+zmin cube = (k' - 1/2)
where
k' = fromIntegral (k cube) :: Double
- delta = h cube
-- | The top boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
zmax :: Cube -> Double
-zmax cube = (k' + 1/2)*delta
+zmax cube = (k' + 1/2)
where
k' = fromIntegral (k cube) :: Double
- delta = h cube
-
-instance ThreeDimensional Cube where
- -- | The center of Cube_ijk coincides with v_ijk at
- -- (ih, jh, kh). See Sorokina and Zeilfelder, p. 76.
- center cube = Point x y z
- where
- delta = h cube
- i' = fromIntegral (i cube) :: Double
- j' = fromIntegral (j cube) :: Double
- k' = fromIntegral (k cube) :: Double
- x = delta * i'
- y = delta * j'
- z = delta * k'
-
- -- | It's easy to tell if a point is within a cube; just make sure
- -- that it falls on the proper side of each of the cube's faces.
- contains_point cube (Point x y z)
- | x < (xmin cube) = False
- | x > (xmax cube) = False
- | y < (ymin cube) = False
- | y > (ymax cube) = False
- | z < (zmin cube) = False
- | z > (zmax cube) = False
- | otherwise = True
+-- | The center of Cube_ijk coincides with v_ijk at
+-- (i, j, k). See Sorokina and Zeilfelder, p. 76.
+center :: Cube -> Point
+center cube =
+ Point x y z
+ where
+ x = fromIntegral (i cube) :: Double
+ y = fromIntegral (j cube) :: Double
+ z = fromIntegral (k cube) :: Double
+
-- Face stuff.
top_face :: Cube -> Face.Face
top_face cube = Face.Face v0' v1' v2' v3'
where
- delta = (1/2)*(h cube)
+ delta = 1/2
cc = center cube
v0' = cc + ( Point delta (-delta) delta )
v1' = cc + ( Point delta delta delta )
back_face :: Cube -> Face.Face
back_face cube = Face.Face v0' v1' v2' v3'
where
- delta = (1/2)*(h cube)
+ delta = 1/2
cc = center cube
v0' = cc + ( Point delta (-delta) (-delta) )
v1' = cc + ( Point delta delta (-delta) )
down_face :: Cube -> Face.Face
down_face cube = Face.Face v0' v1' v2' v3'
where
- delta = (1/2)*(h cube)
+ delta = 1/2
cc = center cube
v0' = cc + ( Point (-delta) (-delta) (-delta) )
v1' = cc + ( Point (-delta) delta (-delta) )
front_face :: Cube -> Face.Face
front_face cube = Face.Face v0' v1' v2' v3'
where
- delta = (1/2)*(h cube)
+ delta = 1/2
cc = center cube
v0' = cc + ( Point (-delta) (-delta) delta )
v1' = cc + ( Point (-delta) delta delta )
left_face :: Cube -> Face.Face
left_face cube = Face.Face v0' v1' v2' v3'
where
- delta = (1/2)*(h cube)
+ delta = 1/2
cc = center cube
v0' = cc + ( Point delta (-delta) delta )
v1' = cc + ( Point (-delta) (-delta) delta )
right_face :: Cube -> Face.Face
right_face cube = Face.Face v0' v1' v2' v3'
where
- delta = (1/2)*(h cube)
+ delta = 1/2
cc = center cube
v0' = cc + ( Point (-delta) delta delta)
v1' = cc + ( Point delta delta delta )
where
v0' = center cube
ff = front_face cube
- v1' = center ff
+ v1' = Face.center ff
v2' = Face.v0 ff
v3' = Face.v1 ff
vol = tetrahedra_volume cube
where
v0' = center cube
ff = front_face cube
- v1' = center ff
+ v1' = Face.center ff
v2' = Face.v1 ff
v3' = Face.v2 ff
fv' = rotate ccwx (fv cube)
where
v0' = center cube
ff = front_face cube
- v1' = center ff
+ v1' = Face.center ff
v2' = Face.v2 ff
v3' = Face.v3 ff
fv' = rotate ccwx $ rotate ccwx $ fv cube
where
v0' = center cube
ff = front_face cube
- v1' = center ff
+ v1' = Face.center ff
v2' = Face.v3 ff
v3' = Face.v0 ff
fv' = rotate cwx (fv cube)
where
v0' = center cube
tf = top_face cube
- v1' = center tf
+ v1' = Face.center tf
v2' = Face.v0 tf
v3' = Face.v1 tf
fv' = rotate cwy (fv cube)
where
v0' = center cube
tf = top_face cube
- v1' = center tf
+ v1' = Face.center tf
v2' = Face.v1 tf
v3' = Face.v2 tf
fv' = rotate cwy $ rotate cwz $ fv cube
where
v0' = center cube
tf = top_face cube
- v1' = center tf
+ v1' = Face.center tf
v2' = Face.v2 tf
v3' = Face.v3 tf
fv' = rotate cwy $ rotate cwz
where
v0' = center cube
tf = top_face cube
- v1' = center tf
+ v1' = Face.center tf
v2' = Face.v3 tf
v3' = Face.v0 tf
fv' = rotate cwy $ rotate ccwz $ fv cube
where
v0' = center cube
bf = back_face cube
- v1' = center bf
+ v1' = Face.center bf
v2' = Face.v0 bf
v3' = Face.v1 bf
fv' = rotate cwy $ rotate cwy $ fv cube
where
v0' = center cube
bf = back_face cube
- v1' = center bf
+ v1' = Face.center bf
v2' = Face.v1 bf
v3' = Face.v2 bf
fv' = rotate cwy $ rotate cwy
where
v0' = center cube
bf = back_face cube
- v1' = center bf
+ v1' = Face.center bf
v2' = Face.v2 bf
v3' = Face.v3 bf
fv' = rotate cwy $ rotate cwy
where
v0' = center cube
bf = back_face cube
- v1' = center bf
+ v1' = Face.center bf
v2' = Face.v3 bf
v3' = Face.v0 bf
fv' = rotate cwy $ rotate cwy
where
v0' = center cube
df = down_face cube
- v1' = center df
+ v1' = Face.center df
v2' = Face.v0 df
v3' = Face.v1 df
fv' = rotate ccwy $ fv cube
where
v0' = center cube
df = down_face cube
- v1' = center df
+ v1' = Face.center df
v2' = Face.v1 df
v3' = Face.v2 df
fv' = rotate ccwy $ rotate ccwz $ fv cube
where
v0' = center cube
df = down_face cube
- v1' = center df
+ v1' = Face.center df
v2' = Face.v2 df
v3' = Face.v3 df
fv' = rotate ccwy $ rotate ccwz
where
v0' = center cube
df = down_face cube
- v1' = center df
+ v1' = Face.center df
v2' = Face.v3 df
v3' = Face.v0 df
fv' = rotate ccwy $ rotate cwz $ fv cube
where
v0' = center cube
rf = right_face cube
- v1' = center rf
+ v1' = Face.center rf
v2' = Face.v0 rf
v3' = Face.v1 rf
fv' = rotate ccwz $ fv cube
where
v0' = center cube
rf = right_face cube
- v1' = center rf
+ v1' = Face.center rf
v2' = Face.v1 rf
v3' = Face.v2 rf
fv' = rotate ccwz $ rotate cwy $ fv cube
where
v0' = center cube
rf = right_face cube
- v1' = center rf
+ v1' = Face.center rf
v2' = Face.v2 rf
v3' = Face.v3 rf
fv' = rotate ccwz $ rotate cwy
where
v0' = center cube
rf = right_face cube
- v1' = center rf
+ v1' = Face.center rf
v2' = Face.v3 rf
v3' = Face.v0 rf
fv' = rotate ccwz $ rotate ccwy
where
v0' = center cube
lf = left_face cube
- v1' = center lf
+ v1' = Face.center lf
v2' = Face.v0 lf
v3' = Face.v1 lf
fv' = rotate cwz $ fv cube
where
v0' = center cube
lf = left_face cube
- v1' = center lf
+ v1' = Face.center lf
v2' = Face.v1 lf
v3' = Face.v2 lf
fv' = rotate cwz $ rotate ccwy $ fv cube
where
v0' = center cube
lf = left_face cube
- v1' = center lf
+ v1' = Face.center lf
v2' = Face.v2 lf
v3' = Face.v3 lf
fv' = rotate cwz $ rotate ccwy
where
v0' = center cube
lf = left_face cube
- v1' = center lf
+ v1' = Face.center lf
v2' = Face.v3 lf
v3' = Face.v0 lf
fv' = rotate cwz $ rotate cwy
$ fv cube
vol = tetrahedra_volume cube
--- Feels dirty, but whatever.
-tetrahedron _ _ = error "asked for a nonexistent tetrahedron"
-
-- Only used in tests, so we don't need the added speed
-- of Data.Vector.
-- This can throw an exception, but the use of 'head' might
-- save us some unnecessary computations.
--
+{-# INLINE find_containing_tetrahedron #-}
find_containing_tetrahedron :: Cube -> Point -> Tetrahedron
find_containing_tetrahedron cube p =
candidates `V.unsafeIndex` (fromJust lucky_idx)
top_half = in_top_half cube p
left_half = in_left_half cube p
- candidates =
- if front_half then
-
+ candidates :: V.Vector Tetrahedron
+ candidates
+ | front_half =
if left_half then
if top_half then
front_left_top_tetrahedra cube
else
front_right_down_tetrahedra cube
- else -- bottom half
-
+ | otherwise = -- back half
if left_half then
if top_half then
back_left_top_tetrahedra cube
-- Use the dot product instead of Euclidean distance here to save
-- a sqrt(). So, "distances" below really means "distances
-- squared."
- distances = V.map ((dot p) . center) candidates
+ distances :: V.Vector Double
+ distances = V.map ((dot p) . barycenter) candidates
+
+ shortest_distance :: Double
shortest_distance = V.minimum distances
+
+ -- Compute the index of the tetrahedron with the center closest to
+ -- p. This is a bad algorithm, but don't change it! If you make it
+ -- smarter by finding the index of shortest_distance in distances
+ -- (this should give the same answer and avoids recomputing the
+ -- dot product), the program gets slower. Seriously!
+ lucky_idx :: Maybe Int
lucky_idx = V.findIndex
- (\t -> (center t) `dot` p == shortest_distance)
+ (\t -> (barycenter t) `dot` p == shortest_distance)
candidates
-- | In fact, since all of the tetrahedra are identical, we should
-- already know their volumes. There's 24 tetrahedra to a cube, so
--- we'd expect the volume of each one to be (1/24)*h^3.
+-- we'd expect the volume of each one to be 1/24.
prop_all_volumes_exact :: Cube -> Bool
prop_all_volumes_exact cube =
- and [volume t ~~= (1/24)*(delta^(3::Int)) | t <- tetrahedra cube]
- where
- delta = h cube
+ and [volume t ~~= 1/24 | t <- tetrahedra cube]
--- | All tetrahedron should have their v0 located at the center of the cube.
+-- | All tetrahedron should have their v0 located at the center of the
+-- cube.
prop_v0_all_equal :: Cube -> Bool
prop_v0_all_equal cube = (v0 t0) == (v0 t1)
where
-- This test checks the rotation works as expected.
prop_c_tilde_2100_rotation_correct :: Cube -> Bool
prop_c_tilde_2100_rotation_correct cube =
- expr1 == expr2
+ expr1 ~= expr2
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6
-- even meaningful!
prop_c_tilde_2100_correct :: Cube -> Bool
prop_c_tilde_2100_correct cube =
- c t6 2 1 0 0 == expected
+ c t6 2 1 0 0 ~= expected
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6