import Cardinal
import Comparisons ((~=), (~~=))
-import qualified Face (Face(Face, v0, v1, v2, v3))
-import FunctionValues
+import qualified Face (Face(..), center)
+import FunctionValues (FunctionValues, eval, rotate)
import Misc (all_equal, disjoint)
-import Point
-import Tetrahedron (Tetrahedron(..), c, volume)
-import ThreeDimensional
-
-data Cube = Cube { h :: Double,
- i :: Int,
- j :: Int,
- k :: Int,
- fv :: FunctionValues,
- tetrahedra_volume :: Double }
+import Point (Point(..), dot)
+import Tetrahedron (Tetrahedron(..), barycenter, c, volume)
+
+data Cube = Cube { h :: !Double,
+ i :: !Int,
+ j :: !Int,
+ k :: !Int,
+ fv :: !FunctionValues,
+ tetrahedra_volume :: !Double }
deriving (Eq)
fv' <- arbitrary :: Gen FunctionValues
(Positive tet_vol) <- arbitrary :: Gen (Positive Double)
return (Cube h' i' j' k' fv' tet_vol)
- where
- coordmin = -268435456 -- -(2^29 / 2)
- coordmax = 268435456 -- +(2^29 / 2)
+ where
+ -- The idea here is that, when cubed in the volume formula,
+ -- these numbers don't overflow 64 bits. This number is not
+ -- magic in any other sense than that it does not cause test
+ -- failures, while 2^23 does.
+ coordmax = 4194304 -- 2^22
+ coordmin = -coordmax
instance Show Cube where
" ymin: " ++ (show (ymin cube)) ++ "\n" ++
" ymax: " ++ (show (ymax cube)) ++ "\n" ++
" zmin: " ++ (show (zmin cube)) ++ "\n" ++
- " zmax: " ++ (show (zmax cube)) ++ "\n" ++
- " fv: " ++ (show (Cube.fv cube)) ++ "\n"
+ " zmax: " ++ (show (zmax cube)) ++ "\n"
where
subscript =
(show (i cube)) ++ "," ++ (show (j cube)) ++ "," ++ (show (k cube))
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 = (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 (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
+-- (ih, jh, kh). See Sorokina and Zeilfelder, p. 76.
+center :: Cube -> Point
+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'
-- Face stuff.
top_face cube = Face.Face v0' v1' v2' v3'
where
delta = (1/2)*(h cube)
- v0' = (center cube) + (delta, -delta, delta)
- v1' = (center cube) + (delta, delta, delta)
- v2' = (center cube) + (-delta, delta, delta)
- v3' = (center cube) + (-delta, -delta, delta)
+ cc = center cube
+ v0' = cc + ( Point delta (-delta) delta )
+ v1' = cc + ( Point delta delta delta )
+ v2' = cc + ( Point (-delta) delta delta )
+ v3' = cc + ( Point (-delta) (-delta) delta )
back_face cube = Face.Face v0' v1' v2' v3'
where
delta = (1/2)*(h cube)
- v0' = (center cube) + (delta, -delta, -delta)
- v1' = (center cube) + (delta, delta, -delta)
- v2' = (center cube) + (delta, delta, delta)
- v3' = (center cube) + (delta, -delta, delta)
+ cc = center cube
+ v0' = cc + ( Point delta (-delta) (-delta) )
+ v1' = cc + ( Point delta delta (-delta) )
+ v2' = cc + ( Point delta delta delta )
+ v3' = cc + ( Point delta (-delta) delta )
-- The bottom face (in the direction of -z) of the cube.
down_face cube = Face.Face v0' v1' v2' v3'
where
delta = (1/2)*(h cube)
- v0' = (center cube) + (-delta, -delta, -delta)
- v1' = (center cube) + (-delta, delta, -delta)
- v2' = (center cube) + (delta, delta, -delta)
- v3' = (center cube) + (delta, -delta, -delta)
+ cc = center cube
+ v0' = cc + ( Point (-delta) (-delta) (-delta) )
+ v1' = cc + ( Point (-delta) delta (-delta) )
+ v2' = cc + ( Point delta delta (-delta) )
+ v3' = cc + ( Point delta (-delta) (-delta) )
front_face cube = Face.Face v0' v1' v2' v3'
where
delta = (1/2)*(h cube)
- v0' = (center cube) + (-delta, -delta, delta)
- v1' = (center cube) + (-delta, delta, delta)
- v2' = (center cube) + (-delta, delta, -delta)
- v3' = (center cube) + (-delta, -delta, -delta)
+ cc = center cube
+ v0' = cc + ( Point (-delta) (-delta) delta )
+ v1' = cc + ( Point (-delta) delta delta )
+ v2' = cc + ( Point (-delta) delta (-delta) )
+ v3' = cc + ( Point (-delta) (-delta) (-delta) )
-- | The left (in the direction of -y) face of the cube.
left_face :: Cube -> Face.Face
left_face cube = Face.Face v0' v1' v2' v3'
where
delta = (1/2)*(h cube)
- v0' = (center cube) + (delta, -delta, delta)
- v1' = (center cube) + (-delta, -delta, delta)
- v2' = (center cube) + (-delta, -delta, -delta)
- v3' = (center cube) + (delta, -delta, -delta)
+ cc = center cube
+ v0' = cc + ( Point delta (-delta) delta )
+ v1' = cc + ( Point (-delta) (-delta) delta )
+ v2' = cc + ( Point (-delta) (-delta) (-delta) )
+ v3' = cc + ( Point delta (-delta) (-delta) )
-- | The right (in the direction of y) face of the cube.
right_face cube = Face.Face v0' v1' v2' v3'
where
delta = (1/2)*(h cube)
- v0' = (center cube) + (-delta, delta, delta)
- v1' = (center cube) + (delta, delta, delta)
- v2' = (center cube) + (delta, delta, -delta)
- v3' = (center cube) + (-delta, delta, -delta)
+ cc = center cube
+ v0' = cc + ( Point (-delta) delta delta)
+ v1' = cc + ( Point delta delta delta )
+ v2' = cc + ( Point delta delta (-delta) )
+ v3' = cc + ( Point (-delta) delta (-delta) )
tetrahedron :: Cube -> Int -> Tetrahedron
Tetrahedron (fv cube) v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (front_face cube)
- v2' = Face.v0 (front_face cube)
- v3' = Face.v1 (front_face cube)
+ ff = front_face cube
+ v1' = Face.center ff
+ v2' = Face.v0 ff
+ v3' = Face.v1 ff
vol = tetrahedra_volume cube
tetrahedron cube 1 =
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (front_face cube)
- v2' = Face.v1 (front_face cube)
- v3' = Face.v2 (front_face cube)
+ ff = front_face cube
+ v1' = Face.center ff
+ v2' = Face.v1 ff
+ v3' = Face.v2 ff
fv' = rotate ccwx (fv cube)
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (front_face cube)
- v2' = Face.v2 (front_face cube)
- v3' = Face.v3 (front_face cube)
+ ff = front_face cube
+ v1' = Face.center ff
+ v2' = Face.v2 ff
+ v3' = Face.v3 ff
fv' = rotate ccwx $ rotate ccwx $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (front_face cube)
- v2' = Face.v3 (front_face cube)
- v3' = Face.v0 (front_face cube)
+ ff = front_face cube
+ v1' = Face.center ff
+ v2' = Face.v3 ff
+ v3' = Face.v0 ff
fv' = rotate cwx (fv cube)
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (top_face cube)
- v2' = Face.v0 (top_face cube)
- v3' = Face.v1 (top_face cube)
+ tf = top_face cube
+ v1' = Face.center tf
+ v2' = Face.v0 tf
+ v3' = Face.v1 tf
fv' = rotate cwy (fv cube)
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (top_face cube)
- v2' = Face.v1 (top_face cube)
- v3' = Face.v2 (top_face cube)
+ tf = top_face cube
+ v1' = Face.center tf
+ v2' = Face.v1 tf
+ v3' = Face.v2 tf
fv' = rotate cwy $ rotate cwz $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (top_face cube)
- v2' = Face.v2 (top_face cube)
- v3' = Face.v3 (top_face cube)
+ tf = top_face cube
+ v1' = Face.center tf
+ v2' = Face.v2 tf
+ v3' = Face.v3 tf
fv' = rotate cwy $ rotate cwz
$ rotate cwz
$ fv cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (top_face cube)
- v2' = Face.v3 (top_face cube)
- v3' = Face.v0 (top_face cube)
+ tf = top_face cube
+ v1' = Face.center tf
+ v2' = Face.v3 tf
+ v3' = Face.v0 tf
fv' = rotate cwy $ rotate ccwz $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (back_face cube)
- v2' = Face.v0 (back_face cube)
- v3' = Face.v1 (back_face cube)
+ bf = back_face cube
+ v1' = Face.center bf
+ v2' = Face.v0 bf
+ v3' = Face.v1 bf
fv' = rotate cwy $ rotate cwy $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (back_face cube)
- v2' = Face.v1 (back_face cube)
- v3' = Face.v2 (back_face cube)
+ bf = back_face cube
+ v1' = Face.center bf
+ v2' = Face.v1 bf
+ v3' = Face.v2 bf
fv' = rotate cwy $ rotate cwy
$ rotate cwx
$ fv cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (back_face cube)
- v2' = Face.v2 (back_face cube)
- v3' = Face.v3 (back_face cube)
+ bf = back_face cube
+ v1' = Face.center bf
+ v2' = Face.v2 bf
+ v3' = Face.v3 bf
fv' = rotate cwy $ rotate cwy
$ rotate cwx
$ rotate cwx
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (back_face cube)
- v2' = Face.v3 (back_face cube)
- v3' = Face.v0 (back_face cube)
+ bf = back_face cube
+ v1' = Face.center bf
+ v2' = Face.v3 bf
+ v3' = Face.v0 bf
fv' = rotate cwy $ rotate cwy
$ rotate ccwx
$ fv cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (down_face cube)
- v2' = Face.v0 (down_face cube)
- v3' = Face.v1 (down_face cube)
+ df = down_face cube
+ v1' = Face.center df
+ v2' = Face.v0 df
+ v3' = Face.v1 df
fv' = rotate ccwy $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (down_face cube)
- v2' = Face.v1 (down_face cube)
- v3' = Face.v2 (down_face cube)
+ df = down_face cube
+ v1' = Face.center df
+ v2' = Face.v1 df
+ v3' = Face.v2 df
fv' = rotate ccwy $ rotate ccwz $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (down_face cube)
- v2' = Face.v2 (down_face cube)
- v3' = Face.v3 (down_face cube)
+ df = down_face cube
+ v1' = Face.center df
+ v2' = Face.v2 df
+ v3' = Face.v3 df
fv' = rotate ccwy $ rotate ccwz
$ rotate ccwz
$ fv cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (down_face cube)
- v2' = Face.v3 (down_face cube)
- v3' = Face.v0 (down_face cube)
+ df = down_face cube
+ v1' = Face.center df
+ v2' = Face.v3 df
+ v3' = Face.v0 df
fv' = rotate ccwy $ rotate cwz $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (right_face cube)
- v2' = Face.v0 (right_face cube)
- v3' = Face.v1 (right_face cube)
+ rf = right_face cube
+ v1' = Face.center rf
+ v2' = Face.v0 rf
+ v3' = Face.v1 rf
fv' = rotate ccwz $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (right_face cube)
- v2' = Face.v1 (right_face cube)
- v3' = Face.v2 (right_face cube)
+ rf = right_face cube
+ v1' = Face.center rf
+ v2' = Face.v1 rf
+ v3' = Face.v2 rf
fv' = rotate ccwz $ rotate cwy $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (right_face cube)
- v2' = Face.v2 (right_face cube)
- v3' = Face.v3 (right_face cube)
+ rf = right_face cube
+ v1' = Face.center rf
+ v2' = Face.v2 rf
+ v3' = Face.v3 rf
fv' = rotate ccwz $ rotate cwy
$ rotate cwy
$ fv cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (right_face cube)
- v2' = Face.v3 (right_face cube)
- v3' = Face.v0 (right_face cube)
+ rf = right_face cube
+ v1' = Face.center rf
+ v2' = Face.v3 rf
+ v3' = Face.v0 rf
fv' = rotate ccwz $ rotate ccwy
$ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (left_face cube)
- v2' = Face.v0 (left_face cube)
- v3' = Face.v1 (left_face cube)
+ lf = left_face cube
+ v1' = Face.center lf
+ v2' = Face.v0 lf
+ v3' = Face.v1 lf
fv' = rotate cwz $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (left_face cube)
- v2' = Face.v1 (left_face cube)
- v3' = Face.v2 (left_face cube)
+ lf = left_face cube
+ v1' = Face.center lf
+ v2' = Face.v1 lf
+ v3' = Face.v2 lf
fv' = rotate cwz $ rotate ccwy $ fv cube
vol = tetrahedra_volume cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (left_face cube)
- v2' = Face.v2 (left_face cube)
- v3' = Face.v3 (left_face cube)
+ lf = left_face cube
+ v1' = Face.center lf
+ v2' = Face.v2 lf
+ v3' = Face.v3 lf
fv' = rotate cwz $ rotate ccwy
$ rotate ccwy
$ fv cube
Tetrahedron fv' v0' v1' v2' v3' vol
where
v0' = center cube
- v1' = center (left_face cube)
- v2' = Face.v3 (left_face cube)
- v3' = Face.v0 (left_face cube)
+ lf = left_face cube
+ 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.
(tetrahedron cube 18)
in_top_half :: Cube -> Point -> Bool
-in_top_half cube (_,_,z) =
+in_top_half cube (Point _ _ z) =
distance_from_top <= distance_from_bottom
where
distance_from_top = abs $ (zmax cube) - z
distance_from_bottom = abs $ (zmin cube) - z
in_front_half :: Cube -> Point -> Bool
-in_front_half cube (x,_,_) =
+in_front_half cube (Point x _ _) =
distance_from_front <= distance_from_back
where
distance_from_front = abs $ (xmin cube) - x
in_left_half :: Cube -> Point -> Bool
-in_left_half cube (_,y,_) =
+in_left_half cube (Point _ y _) =
distance_from_left <= distance_from_right
where
distance_from_left = abs $ (ymin cube) - y
-- 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 :: V.Vector Tetrahedron
candidates =
if front_half then
else
back_right_down_tetrahedra cube
- -- Use the dot product instead of 'distance' here to save a
- -- sqrt(). So, "distances" below really means "distances squared."
- distances = V.map ((dot p) . center) candidates
+ -- Use the dot product instead of Euclidean distance here to save
+ -- a sqrt(). So, "distances" below really means "distances
+ -- squared."
+ 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
-- | Since the grid size is necessarily positive, all tetrahedra
--- (which comprise cubes of positive volume) must have positive volume
--- as well.
+-- (which comprise cubes of positive volume) must have positive
+-- volume as well.
prop_all_volumes_positive :: Cube -> Bool
prop_all_volumes_positive cube =
- null nonpositive_volumes
+ all (>= 0) volumes
where
ts = tetrahedra cube
volumes = map volume ts
- nonpositive_volumes = filter (<= 0) volumes
+
-- | In fact, since all of the tetrahedra are identical, we should
-- already know their volumes. There's 24 tetrahedra to a cube, so
where
delta = h 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