import Cardinal
import Comparisons ((~=), (~~=))
import qualified Face (Face(Face, v0, v1, v2, v3))
-import FunctionValues
+import FunctionValues (FunctionValues, eval, rotate)
import Misc (all_equal, disjoint)
-import Point
+import Point (Point(..), dot)
import Tetrahedron (Tetrahedron(..), c, volume)
import ThreeDimensional
-data Cube = Cube { h :: Double,
- i :: Int,
- j :: Int,
- k :: Int,
- fv :: FunctionValues,
- tetrahedra_volume :: Double }
+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))
-- | The left-side boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
xmin :: Cube -> Double
-xmin cube = (2*i' - 1)*delta / 2
+xmin cube = (i' - 1/2)*delta
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 = (2*i' + 1)*delta / 2
+xmax cube = (i' + 1/2)*delta
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 = (2*j' - 1)*delta / 2
+ymin cube = (j' - 1/2)*delta
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 = (2*j' + 1)*delta / 2
+ymax cube = (j' + 1/2)*delta
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 = (2*k' - 1)*delta / 2
+zmin cube = (k' - 1/2)*delta
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 = (2*k' + 1)*delta / 2
+zmax cube = (k' + 1/2)*delta
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 = (x, y, z)
+ center cube = Point x y z
where
delta = h cube
i' = fromIntegral (i cube) :: Double
-- | 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)
+ contains_point cube (Point x y z)
| x < (xmin cube) = False
| x > (xmax cube) = False
| y < (ymin cube) = False
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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = 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' = center lf
+ v2' = Face.v3 lf
+ v3' = Face.v0 lf
fv' = rotate cwz $ rotate cwy
$ fv cube
vol = tetrahedra_volume cube
(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)
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."
+ -- 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
shortest_distance = V.minimum distances
lucky_idx = V.findIndex
-- | 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