4 import Numeric.LinearAlgebra hiding (i, scale)
5 import Prelude hiding (LT)
6 import Test.QuickCheck (Arbitrary(..), Gen)
9 import Comparisons (nearly_ge)
11 import Misc (factorial)
14 import ThreeDimensional
16 data Tetrahedron = Tetrahedron { fv :: FunctionValues,
21 precomputed_volume :: Double }
25 instance Arbitrary Tetrahedron where
27 rnd_v0 <- arbitrary :: Gen Point
28 rnd_v1 <- arbitrary :: Gen Point
29 rnd_v2 <- arbitrary :: Gen Point
30 rnd_v3 <- arbitrary :: Gen Point
31 rnd_fv <- arbitrary :: Gen FunctionValues
32 rnd_vol <- arbitrary :: Gen Double
33 return (Tetrahedron rnd_fv rnd_v0 rnd_v1 rnd_v2 rnd_v3 rnd_vol)
36 instance Show Tetrahedron where
37 show t = "Tetrahedron:\n" ++
38 " fv: " ++ (show (fv t)) ++ "\n" ++
39 " v0: " ++ (show (v0 t)) ++ "\n" ++
40 " v1: " ++ (show (v1 t)) ++ "\n" ++
41 " v2: " ++ (show (v2 t)) ++ "\n" ++
42 " v3: " ++ (show (v3 t)) ++ "\n"
45 instance ThreeDimensional Tetrahedron where
46 center t = ((v0 t) + (v1 t) + (v2 t) + (v3 t)) `scale` (1/4)
48 b0_unscaled `nearly_ge` 0 &&
49 b1_unscaled `nearly_ge` 0 &&
50 b2_unscaled `nearly_ge` 0 &&
51 b3_unscaled `nearly_ge` 0
53 -- Drop the useless division and volume calculation that we
54 -- would do if we used the regular b0,..b3 functions.
56 b0_unscaled = volume inner_tetrahedron
57 where inner_tetrahedron = t { v0 = p }
60 b1_unscaled = volume inner_tetrahedron
61 where inner_tetrahedron = t { v1 = p }
64 b2_unscaled = volume inner_tetrahedron
65 where inner_tetrahedron = t { v2 = p }
68 b3_unscaled = volume inner_tetrahedron
69 where inner_tetrahedron = t { v3 = p }
72 polynomial :: Tetrahedron -> (RealFunction Point)
74 sum [ (c t i j k l) `cmult` (beta t i j k l) | i <- [0..3],
81 -- | Returns the domain point of t with indices i,j,k,l.
82 -- Simply an alias for the domain_point function.
83 xi :: Tetrahedron -> Int -> Int -> Int -> Int -> Point
86 -- | Returns the domain point of t with indices i,j,k,l.
87 domain_point :: Tetrahedron -> Int -> Int -> Int -> Int -> Point
88 domain_point t i j k l
89 | i + j + k + l == 3 = weighted_sum `scale` (1/3)
90 | otherwise = error "domain point index out of bounds"
92 v0' = (v0 t) `scale` (fromIntegral i)
93 v1' = (v1 t) `scale` (fromIntegral j)
94 v2' = (v2 t) `scale` (fromIntegral k)
95 v3' = (v3 t) `scale` (fromIntegral l)
96 weighted_sum = v0' + v1' + v2' + v3'
99 -- | The Bernstein polynomial on t with indices i,j,k,l. Denoted by a
100 -- capital 'B' in the Sorokina/Zeilfelder paper.
101 beta :: Tetrahedron -> Int -> Int -> Int -> Int -> (RealFunction Point)
103 | (i + j + k + l == 3) =
104 coefficient `cmult` (b0_term * b1_term * b2_term * b3_term)
105 | otherwise = error "basis function index out of bounds"
107 denominator = (factorial i)*(factorial j)*(factorial k)*(factorial l)
108 coefficient = 6 / (fromIntegral denominator)
109 b0_term = (b0 t) `fexp` i
110 b1_term = (b1 t) `fexp` j
111 b2_term = (b2 t) `fexp` k
112 b3_term = (b3 t) `fexp` l
115 -- | The coefficient function. c t i j k l returns the coefficient
116 -- c_ijkl with respect to the tetrahedron t. The definition uses
117 -- pattern matching to mimic the definitions given in Sorokina and
118 -- Zeilfelder, pp. 84-86. If incorrect indices are supplied, the
119 -- function will simply error.
120 c :: Tetrahedron -> Int -> Int -> Int -> Int -> Double
121 c t 0 0 3 0 = eval (fv t) $
122 (1/8) * (I + F + L + T + LT + FL + FT + FLT)
124 c t 0 0 0 3 = eval (fv t) $
125 (1/8) * (I + F + R + T + RT + FR + FT + FRT)
127 c t 0 0 2 1 = eval (fv t) $
128 (5/24)*(I + F + T + FT) +
129 (1/24)*(L + FL + LT + FLT)
131 c t 0 0 1 2 = eval (fv t) $
132 (5/24)*(I + F + T + FT) +
133 (1/24)*(R + FR + RT + FRT)
135 c t 0 1 2 0 = eval (fv t) $
137 (1/8)*(L + T + FL + FT) +
140 c t 0 1 0 2 = eval (fv t) $
142 (1/8)*(R + T + FR + FT) +
145 c t 0 1 1 1 = eval (fv t) $
148 (1/32)*(L + R + FL + FR) +
149 (1/96)*(LT + RT + FLT + FRT)
151 c t 0 2 1 0 = eval (fv t) $
153 (17/192)*(L + T + FL + FT) +
155 (1/64)*(R + D + FR + FD) +
156 (1/192)*(RT + LD + FRT + FLD)
158 c t 0 2 0 1 = eval (fv t) $
160 (17/192)*(R + T + FR + FT) +
162 (1/64)*(L + D + FL + FD) +
163 (1/192)*(RD + LT + FLT + FRD)
165 c t 0 3 0 0 = eval (fv t) $
167 (5/96)*(L + R + T + D + FL + FR + FT + FD) +
168 (1/192)*(RT + RD + LT + LD + FRT + FRD + FLT + FLD)
170 c t 1 0 2 0 = eval (fv t) $
173 (1/12)*(LT + FL + FT)
175 c t 1 0 0 2 = eval (fv t) $
178 (1/12)*(RT + FR + FT)
180 c t 1 0 1 1 = eval (fv t) $
185 (1/48)*(LT + RT + FL + FR)
187 c t 1 1 1 0 = eval (fv t) $
192 (1/48)*(D + R + LT) +
193 (1/96)*(FD + LD + RT + FR)
195 c t 1 1 0 1 = eval (fv t) $
200 (1/48)*(D + L + RT) +
201 (1/96)*(FD + LT + RD + FL)
203 c t 1 2 0 0 = eval (fv t) $
206 (7/96)*(L + R + T + D) +
207 (1/32)*(FL + FR + FT + FD) +
208 (1/96)*(RT + RD + LT + LD)
210 c t 2 0 1 0 = eval (fv t) $
213 (1/48)*(R + D + B + LT + FL + FT) +
214 (1/96)*(RT + BT + FR + FD + LD + BL)
216 c t 2 0 0 1 = eval (fv t) $
219 (1/48)*(L + D + B + RT + FR + FT) +
220 (1/96)*(LT + BT + FL + FD + RD + BR)
222 c t 2 1 0 0 = eval (fv t) $
224 (1/12)*(T + R + L + D) +
225 (1/64)*(FT + FR + FL + FD) +
228 (1/96)*(RT + LD + LT + RD) +
229 (1/192)*(BT + BR + BL + BD)
231 c t 3 0 0 0 = eval (fv t) $
233 (1/12)*(T + F + L + R + D + B) +
234 (1/96)*(LT + FL + FT + RT + BT + FR) +
235 (1/96)*(FD + LD + BD + BR + RD + BL)
237 c _ _ _ _ _ = error "coefficient index out of bounds"
241 -- | The matrix used in the tetrahedron volume calculation as given in
242 -- Lai & Schumaker, Definition 15.4, page 436.
243 vol_matrix :: Tetrahedron -> Matrix Double
244 vol_matrix t = (4><4)
255 -- | Computed using the formula from Lai & Schumaker, Definition 15.4,
257 volume :: Tetrahedron -> Double
259 | (v0 t) == (v1 t) = 0
260 | (v0 t) == (v2 t) = 0
261 | (v0 t) == (v3 t) = 0
262 | (v1 t) == (v2 t) = 0
263 | (v1 t) == (v3 t) = 0
264 | (v2 t) == (v3 t) = 0
265 | otherwise = (1/6)*(det (vol_matrix t))
268 -- | The barycentric coordinates of a point with respect to v0.
269 b0 :: Tetrahedron -> (RealFunction Point)
270 b0 t point = (volume inner_tetrahedron) / (volume t)
272 inner_tetrahedron = t { v0 = point }
275 -- | The barycentric coordinates of a point with respect to v1.
276 b1 :: Tetrahedron -> (RealFunction Point)
277 b1 t point = (volume inner_tetrahedron) / (volume t)
279 inner_tetrahedron = t { v1 = point }
282 -- | The barycentric coordinates of a point with respect to v2.
283 b2 :: Tetrahedron -> (RealFunction Point)
284 b2 t point = (volume inner_tetrahedron) / (volume t)
286 inner_tetrahedron = t { v2 = point }
289 -- | The barycentric coordinates of a point with respect to v3.
290 b3 :: Tetrahedron -> (RealFunction Point)
291 b3 t point = (volume inner_tetrahedron) / (volume t)
293 inner_tetrahedron = t { v3 = point }