4 import qualified Data.Vector as V (
9 import Numeric.LinearAlgebra hiding (i, scale)
10 import Prelude hiding (LT)
11 import Test.QuickCheck (Arbitrary(..), Gen)
14 import Comparisons (nearly_ge)
16 import Misc (factorial)
19 import ThreeDimensional
22 Tetrahedron { fv :: FunctionValues,
27 precomputed_volume :: Double
32 instance Arbitrary Tetrahedron where
34 rnd_v0 <- arbitrary :: Gen Point
35 rnd_v1 <- arbitrary :: Gen Point
36 rnd_v2 <- arbitrary :: Gen Point
37 rnd_v3 <- arbitrary :: Gen Point
38 rnd_fv <- arbitrary :: Gen FunctionValues
40 -- We can't assign an incorrect precomputed volume,
41 -- so we have to calculate the correct one here.
42 let t' = Tetrahedron rnd_fv rnd_v0 rnd_v1 rnd_v2 rnd_v3 0
44 return (Tetrahedron rnd_fv rnd_v0 rnd_v1 rnd_v2 rnd_v3 vol)
47 instance Show Tetrahedron where
48 show t = "Tetrahedron:\n" ++
49 " fv: " ++ (show (fv t)) ++ "\n" ++
50 " v0: " ++ (show (v0 t)) ++ "\n" ++
51 " v1: " ++ (show (v1 t)) ++ "\n" ++
52 " v2: " ++ (show (v2 t)) ++ "\n" ++
53 " v3: " ++ (show (v3 t)) ++ "\n"
56 instance ThreeDimensional Tetrahedron where
57 center (Tetrahedron _ v0' v1' v2' v3' _) =
58 (v0' + v1' + v2' + v3') `scale` (1/4)
61 b0_unscaled `nearly_ge` 0 &&
62 b1_unscaled `nearly_ge` 0 &&
63 b2_unscaled `nearly_ge` 0 &&
64 b3_unscaled `nearly_ge` 0
66 -- Drop the useless division and volume calculation that we
67 -- would do if we used the regular b0,..b3 functions.
69 b0_unscaled = volume inner_tetrahedron
70 where inner_tetrahedron = t { v0 = p }
73 b1_unscaled = volume inner_tetrahedron
74 where inner_tetrahedron = t { v1 = p }
77 b2_unscaled = volume inner_tetrahedron
78 where inner_tetrahedron = t { v2 = p }
81 b3_unscaled = volume inner_tetrahedron
82 where inner_tetrahedron = t { v3 = p }
85 polynomial :: Tetrahedron -> (RealFunction Point)
87 V.sum $ V.singleton ((c t 0 0 0 3) `cmult` (beta t 0 0 0 3)) `V.snoc`
88 ((c t 0 0 1 2) `cmult` (beta t 0 0 1 2)) `V.snoc`
89 ((c t 0 0 2 1) `cmult` (beta t 0 0 2 1)) `V.snoc`
90 ((c t 0 0 3 0) `cmult` (beta t 0 0 3 0)) `V.snoc`
91 ((c t 0 1 0 2) `cmult` (beta t 0 1 0 2)) `V.snoc`
92 ((c t 0 1 1 1) `cmult` (beta t 0 1 1 1)) `V.snoc`
93 ((c t 0 1 2 0) `cmult` (beta t 0 1 2 0)) `V.snoc`
94 ((c t 0 2 0 1) `cmult` (beta t 0 2 0 1)) `V.snoc`
95 ((c t 0 2 1 0) `cmult` (beta t 0 2 1 0)) `V.snoc`
96 ((c t 0 3 0 0) `cmult` (beta t 0 3 0 0)) `V.snoc`
97 ((c t 1 0 0 2) `cmult` (beta t 1 0 0 2)) `V.snoc`
98 ((c t 1 0 1 1) `cmult` (beta t 1 0 1 1)) `V.snoc`
99 ((c t 1 0 2 0) `cmult` (beta t 1 0 2 0)) `V.snoc`
100 ((c t 1 1 0 1) `cmult` (beta t 1 1 0 1)) `V.snoc`
101 ((c t 1 1 1 0) `cmult` (beta t 1 1 1 0)) `V.snoc`
102 ((c t 1 2 0 0) `cmult` (beta t 1 2 0 0)) `V.snoc`
103 ((c t 2 0 0 1) `cmult` (beta t 2 0 0 1)) `V.snoc`
104 ((c t 2 0 1 0) `cmult` (beta t 2 0 1 0)) `V.snoc`
105 ((c t 2 1 0 0) `cmult` (beta t 2 1 0 0)) `V.snoc`
106 ((c t 3 0 0 0) `cmult` (beta t 3 0 0 0))
109 -- | Returns the domain point of t with indices i,j,k,l.
110 -- Simply an alias for the domain_point function.
111 xi :: Tetrahedron -> Int -> Int -> Int -> Int -> Point
114 -- | Returns the domain point of t with indices i,j,k,l.
115 domain_point :: Tetrahedron -> Int -> Int -> Int -> Int -> Point
116 domain_point t i j k l
117 | i + j + k + l == 3 = weighted_sum `scale` (1/3)
118 | otherwise = error "domain point index out of bounds"
120 v0' = (v0 t) `scale` (fromIntegral i)
121 v1' = (v1 t) `scale` (fromIntegral j)
122 v2' = (v2 t) `scale` (fromIntegral k)
123 v3' = (v3 t) `scale` (fromIntegral l)
124 weighted_sum = v0' + v1' + v2' + v3'
127 -- | The Bernstein polynomial on t with indices i,j,k,l. Denoted by a
128 -- capital 'B' in the Sorokina/Zeilfelder paper.
129 beta :: Tetrahedron -> Int -> Int -> Int -> Int -> (RealFunction Point)
131 | (i + j + k + l == 3) =
132 coefficient `cmult` (b0_term * b1_term * b2_term * b3_term)
133 | otherwise = error "basis function index out of bounds"
135 denominator = (factorial i)*(factorial j)*(factorial k)*(factorial l)
136 coefficient = 6 / (fromIntegral denominator)
137 b0_term = (b0 t) `fexp` i
138 b1_term = (b1 t) `fexp` j
139 b2_term = (b2 t) `fexp` k
140 b3_term = (b3 t) `fexp` l
143 -- | The coefficient function. c t i j k l returns the coefficient
144 -- c_ijkl with respect to the tetrahedron t. The definition uses
145 -- pattern matching to mimic the definitions given in Sorokina and
146 -- Zeilfelder, pp. 84-86. If incorrect indices are supplied, the
147 -- function will simply error.
148 c :: Tetrahedron -> Int -> Int -> Int -> Int -> Double
149 c t 0 0 3 0 = eval (fv t) $
150 (1/8) * (I + F + L + T + LT + FL + FT + FLT)
152 c t 0 0 0 3 = eval (fv t) $
153 (1/8) * (I + F + R + T + RT + FR + FT + FRT)
155 c t 0 0 2 1 = eval (fv t) $
156 (5/24)*(I + F + T + FT) +
157 (1/24)*(L + FL + LT + FLT)
159 c t 0 0 1 2 = eval (fv t) $
160 (5/24)*(I + F + T + FT) +
161 (1/24)*(R + FR + RT + FRT)
163 c t 0 1 2 0 = eval (fv t) $
165 (1/8)*(L + T + FL + FT) +
168 c t 0 1 0 2 = eval (fv t) $
170 (1/8)*(R + T + FR + FT) +
173 c t 0 1 1 1 = eval (fv t) $
176 (1/32)*(L + R + FL + FR) +
177 (1/96)*(LT + RT + FLT + FRT)
179 c t 0 2 1 0 = eval (fv t) $
181 (17/192)*(L + T + FL + FT) +
183 (1/64)*(R + D + FR + FD) +
184 (1/192)*(RT + LD + FRT + FLD)
186 c t 0 2 0 1 = eval (fv t) $
188 (17/192)*(R + T + FR + FT) +
190 (1/64)*(L + D + FL + FD) +
191 (1/192)*(RD + LT + FLT + FRD)
193 c t 0 3 0 0 = eval (fv t) $
195 (5/96)*(L + R + T + D + FL + FR + FT + FD) +
196 (1/192)*(RT + RD + LT + LD + FRT + FRD + FLT + FLD)
198 c t 1 0 2 0 = eval (fv t) $
201 (1/12)*(LT + FL + FT)
203 c t 1 0 0 2 = eval (fv t) $
206 (1/12)*(RT + FR + FT)
208 c t 1 0 1 1 = eval (fv t) $
213 (1/48)*(LT + RT + FL + FR)
215 c t 1 1 1 0 = eval (fv t) $
220 (1/48)*(D + R + LT) +
221 (1/96)*(FD + LD + RT + FR)
223 c t 1 1 0 1 = eval (fv t) $
228 (1/48)*(D + L + RT) +
229 (1/96)*(FD + LT + RD + FL)
231 c t 1 2 0 0 = eval (fv t) $
234 (7/96)*(L + R + T + D) +
235 (1/32)*(FL + FR + FT + FD) +
236 (1/96)*(RT + RD + LT + LD)
238 c t 2 0 1 0 = eval (fv t) $
241 (1/48)*(R + D + B + LT + FL + FT) +
242 (1/96)*(RT + BT + FR + FD + LD + BL)
244 c t 2 0 0 1 = eval (fv t) $
247 (1/48)*(L + D + B + RT + FR + FT) +
248 (1/96)*(LT + BT + FL + FD + RD + BR)
250 c t 2 1 0 0 = eval (fv t) $
252 (1/12)*(T + R + L + D) +
253 (1/64)*(FT + FR + FL + FD) +
256 (1/96)*(RT + LD + LT + RD) +
257 (1/192)*(BT + BR + BL + BD)
259 c t 3 0 0 0 = eval (fv t) $
261 (1/12)*(T + F + L + R + D + B) +
262 (1/96)*(LT + FL + FT + RT + BT + FR) +
263 (1/96)*(FD + LD + BD + BR + RD + BL)
265 c _ _ _ _ _ = error "coefficient index out of bounds"
269 -- | The matrix used in the tetrahedron volume calculation as given in
270 -- Lai & Schumaker, Definition 15.4, page 436.
271 vol_matrix :: Tetrahedron -> Matrix Double
272 vol_matrix t = (4><4)
283 -- | Computed using the formula from Lai & Schumaker, Definition 15.4,
285 volume :: Tetrahedron -> Double
287 | (v0 t) == (v1 t) = 0
288 | (v0 t) == (v2 t) = 0
289 | (v0 t) == (v3 t) = 0
290 | (v1 t) == (v2 t) = 0
291 | (v1 t) == (v3 t) = 0
292 | (v2 t) == (v3 t) = 0
293 | otherwise = (1/6)*(det (vol_matrix t))
296 -- | The barycentric coordinates of a point with respect to v0.
297 b0 :: Tetrahedron -> (RealFunction Point)
298 b0 t point = (volume inner_tetrahedron) / (precomputed_volume t)
300 inner_tetrahedron = t { v0 = point }
303 -- | The barycentric coordinates of a point with respect to v1.
304 b1 :: Tetrahedron -> (RealFunction Point)
305 b1 t point = (volume inner_tetrahedron) / (precomputed_volume t)
307 inner_tetrahedron = t { v1 = point }
310 -- | The barycentric coordinates of a point with respect to v2.
311 b2 :: Tetrahedron -> (RealFunction Point)
312 b2 t point = (volume inner_tetrahedron) / (precomputed_volume t)
314 inner_tetrahedron = t { v2 = point }
317 -- | The barycentric coordinates of a point with respect to v3.
318 b3 :: Tetrahedron -> (RealFunction Point)
319 b3 t point = (volume inner_tetrahedron) / (precomputed_volume t)
321 inner_tetrahedron = t { v3 = point }