]> gitweb.michael.orlitzky.com - spline3.git/blob - src/Tetrahedron.hs
87332b94440afdb999b173c13f681095d48ac796
[spline3.git] / src / Tetrahedron.hs
1 module Tetrahedron
2 where
3
4 import qualified Data.Vector as V (
5 singleton,
6 snoc,
7 sum
8 )
9 import Numeric.LinearAlgebra hiding (i, scale)
10 import Prelude hiding (LT)
11 import Test.QuickCheck (Arbitrary(..), Gen, choose)
12
13 import Cardinal
14 import Comparisons (nearly_ge)
15 import FunctionValues
16 import Misc (factorial)
17 import Point
18 import RealFunction
19 import ThreeDimensional
20
21 data Tetrahedron =
22 Tetrahedron { fv :: FunctionValues,
23 v0 :: Point,
24 v1 :: Point,
25 v2 :: Point,
26 v3 :: Point,
27 precomputed_volume :: Double,
28
29 -- | Between 0 and 23; used to quickly determine which
30 -- tetrahedron I am in the parent 'Cube' without
31 -- having to compare them all.
32 number :: Int
33 }
34 deriving (Eq)
35
36
37 instance Arbitrary Tetrahedron where
38 arbitrary = do
39 rnd_v0 <- arbitrary :: Gen Point
40 rnd_v1 <- arbitrary :: Gen Point
41 rnd_v2 <- arbitrary :: Gen Point
42 rnd_v3 <- arbitrary :: Gen Point
43 rnd_fv <- arbitrary :: Gen FunctionValues
44 rnd_no <- choose (0,23)
45
46 -- We can't assign an incorrect precomputed volume,
47 -- so we have to calculate the correct one here.
48 let t' = Tetrahedron rnd_fv rnd_v0 rnd_v1 rnd_v2 rnd_v3 0 rnd_no
49 let vol = volume t'
50 return (Tetrahedron rnd_fv rnd_v0 rnd_v1 rnd_v2 rnd_v3 vol rnd_no)
51
52
53 instance Show Tetrahedron where
54 show t = "Tetrahedron:\n" ++
55 " no: " ++ (show (number t)) ++ "\n" ++
56 " fv: " ++ (show (fv t)) ++ "\n" ++
57 " v0: " ++ (show (v0 t)) ++ "\n" ++
58 " v1: " ++ (show (v1 t)) ++ "\n" ++
59 " v2: " ++ (show (v2 t)) ++ "\n" ++
60 " v3: " ++ (show (v3 t)) ++ "\n"
61
62
63 instance ThreeDimensional Tetrahedron where
64 center (Tetrahedron _ v0' v1' v2' v3' _ _) =
65 (v0' + v1' + v2' + v3') `scale` (1/4)
66
67 contains_point t p =
68 b0_unscaled `nearly_ge` 0 &&
69 b1_unscaled `nearly_ge` 0 &&
70 b2_unscaled `nearly_ge` 0 &&
71 b3_unscaled `nearly_ge` 0
72 where
73 -- Drop the useless division and volume calculation that we
74 -- would do if we used the regular b0,..b3 functions.
75 b0_unscaled :: Double
76 b0_unscaled = volume inner_tetrahedron
77 where inner_tetrahedron = t { v0 = p }
78
79 b1_unscaled :: Double
80 b1_unscaled = volume inner_tetrahedron
81 where inner_tetrahedron = t { v1 = p }
82
83 b2_unscaled :: Double
84 b2_unscaled = volume inner_tetrahedron
85 where inner_tetrahedron = t { v2 = p }
86
87 b3_unscaled :: Double
88 b3_unscaled = volume inner_tetrahedron
89 where inner_tetrahedron = t { v3 = p }
90
91
92 polynomial :: Tetrahedron -> (RealFunction Point)
93 polynomial t =
94 V.sum $ V.singleton ((c t 0 0 0 3) `cmult` (beta t 0 0 0 3)) `V.snoc`
95 ((c t 0 0 1 2) `cmult` (beta t 0 0 1 2)) `V.snoc`
96 ((c t 0 0 2 1) `cmult` (beta t 0 0 2 1)) `V.snoc`
97 ((c t 0 0 3 0) `cmult` (beta t 0 0 3 0)) `V.snoc`
98 ((c t 0 1 0 2) `cmult` (beta t 0 1 0 2)) `V.snoc`
99 ((c t 0 1 1 1) `cmult` (beta t 0 1 1 1)) `V.snoc`
100 ((c t 0 1 2 0) `cmult` (beta t 0 1 2 0)) `V.snoc`
101 ((c t 0 2 0 1) `cmult` (beta t 0 2 0 1)) `V.snoc`
102 ((c t 0 2 1 0) `cmult` (beta t 0 2 1 0)) `V.snoc`
103 ((c t 0 3 0 0) `cmult` (beta t 0 3 0 0)) `V.snoc`
104 ((c t 1 0 0 2) `cmult` (beta t 1 0 0 2)) `V.snoc`
105 ((c t 1 0 1 1) `cmult` (beta t 1 0 1 1)) `V.snoc`
106 ((c t 1 0 2 0) `cmult` (beta t 1 0 2 0)) `V.snoc`
107 ((c t 1 1 0 1) `cmult` (beta t 1 1 0 1)) `V.snoc`
108 ((c t 1 1 1 0) `cmult` (beta t 1 1 1 0)) `V.snoc`
109 ((c t 1 2 0 0) `cmult` (beta t 1 2 0 0)) `V.snoc`
110 ((c t 2 0 0 1) `cmult` (beta t 2 0 0 1)) `V.snoc`
111 ((c t 2 0 1 0) `cmult` (beta t 2 0 1 0)) `V.snoc`
112 ((c t 2 1 0 0) `cmult` (beta t 2 1 0 0)) `V.snoc`
113 ((c t 3 0 0 0) `cmult` (beta t 3 0 0 0))
114
115
116 -- | Returns the domain point of t with indices i,j,k,l.
117 -- Simply an alias for the domain_point function.
118 xi :: Tetrahedron -> Int -> Int -> Int -> Int -> Point
119 xi = domain_point
120
121 -- | Returns the domain point of t with indices i,j,k,l.
122 domain_point :: Tetrahedron -> Int -> Int -> Int -> Int -> Point
123 domain_point t i j k l
124 | i + j + k + l == 3 = weighted_sum `scale` (1/3)
125 | otherwise = error "domain point index out of bounds"
126 where
127 v0' = (v0 t) `scale` (fromIntegral i)
128 v1' = (v1 t) `scale` (fromIntegral j)
129 v2' = (v2 t) `scale` (fromIntegral k)
130 v3' = (v3 t) `scale` (fromIntegral l)
131 weighted_sum = v0' + v1' + v2' + v3'
132
133
134 -- | The Bernstein polynomial on t with indices i,j,k,l. Denoted by a
135 -- capital 'B' in the Sorokina/Zeilfelder paper.
136 beta :: Tetrahedron -> Int -> Int -> Int -> Int -> (RealFunction Point)
137 beta t i j k l
138 | (i + j + k + l == 3) =
139 coefficient `cmult` (b0_term * b1_term * b2_term * b3_term)
140 | otherwise = error "basis function index out of bounds"
141 where
142 denominator = (factorial i)*(factorial j)*(factorial k)*(factorial l)
143 coefficient = 6 / (fromIntegral denominator)
144 b0_term = (b0 t) `fexp` i
145 b1_term = (b1 t) `fexp` j
146 b2_term = (b2 t) `fexp` k
147 b3_term = (b3 t) `fexp` l
148
149
150 -- | The coefficient function. c t i j k l returns the coefficient
151 -- c_ijkl with respect to the tetrahedron t. The definition uses
152 -- pattern matching to mimic the definitions given in Sorokina and
153 -- Zeilfelder, pp. 84-86. If incorrect indices are supplied, the
154 -- function will simply error.
155 c :: Tetrahedron -> Int -> Int -> Int -> Int -> Double
156 c t 0 0 3 0 = eval (fv t) $
157 (1/8) * (I + F + L + T + LT + FL + FT + FLT)
158
159 c t 0 0 0 3 = eval (fv t) $
160 (1/8) * (I + F + R + T + RT + FR + FT + FRT)
161
162 c t 0 0 2 1 = eval (fv t) $
163 (5/24)*(I + F + T + FT) +
164 (1/24)*(L + FL + LT + FLT)
165
166 c t 0 0 1 2 = eval (fv t) $
167 (5/24)*(I + F + T + FT) +
168 (1/24)*(R + FR + RT + FRT)
169
170 c t 0 1 2 0 = eval (fv t) $
171 (5/24)*(I + F) +
172 (1/8)*(L + T + FL + FT) +
173 (1/24)*(LT + FLT)
174
175 c t 0 1 0 2 = eval (fv t) $
176 (5/24)*(I + F) +
177 (1/8)*(R + T + FR + FT) +
178 (1/24)*(RT + FRT)
179
180 c t 0 1 1 1 = eval (fv t) $
181 (13/48)*(I + F) +
182 (7/48)*(T + FT) +
183 (1/32)*(L + R + FL + FR) +
184 (1/96)*(LT + RT + FLT + FRT)
185
186 c t 0 2 1 0 = eval (fv t) $
187 (13/48)*(I + F) +
188 (17/192)*(L + T + FL + FT) +
189 (1/96)*(LT + FLT) +
190 (1/64)*(R + D + FR + FD) +
191 (1/192)*(RT + LD + FRT + FLD)
192
193 c t 0 2 0 1 = eval (fv t) $
194 (13/48)*(I + F) +
195 (17/192)*(R + T + FR + FT) +
196 (1/96)*(RT + FRT) +
197 (1/64)*(L + D + FL + FD) +
198 (1/192)*(RD + LT + FLT + FRD)
199
200 c t 0 3 0 0 = eval (fv t) $
201 (13/48)*(I + F) +
202 (5/96)*(L + R + T + D + FL + FR + FT + FD) +
203 (1/192)*(RT + RD + LT + LD + FRT + FRD + FLT + FLD)
204
205 c t 1 0 2 0 = eval (fv t) $
206 (1/4)*I +
207 (1/6)*(F + L + T) +
208 (1/12)*(LT + FL + FT)
209
210 c t 1 0 0 2 = eval (fv t) $
211 (1/4)*I +
212 (1/6)*(F + R + T) +
213 (1/12)*(RT + FR + FT)
214
215 c t 1 0 1 1 = eval (fv t) $
216 (1/3)*I +
217 (5/24)*(F + T) +
218 (1/12)*FT +
219 (1/24)*(L + R) +
220 (1/48)*(LT + RT + FL + FR)
221
222 c t 1 1 1 0 = eval (fv t) $
223 (1/3)*I +
224 (5/24)*F +
225 (1/8)*(L + T) +
226 (5/96)*(FL + FT) +
227 (1/48)*(D + R + LT) +
228 (1/96)*(FD + LD + RT + FR)
229
230 c t 1 1 0 1 = eval (fv t) $
231 (1/3)*I +
232 (5/24)*F +
233 (1/8)*(R + T) +
234 (5/96)*(FR + FT) +
235 (1/48)*(D + L + RT) +
236 (1/96)*(FD + LT + RD + FL)
237
238 c t 1 2 0 0 = eval (fv t) $
239 (1/3)*I +
240 (5/24)*F +
241 (7/96)*(L + R + T + D) +
242 (1/32)*(FL + FR + FT + FD) +
243 (1/96)*(RT + RD + LT + LD)
244
245 c t 2 0 1 0 = eval (fv t) $
246 (3/8)*I +
247 (7/48)*(F + T + L) +
248 (1/48)*(R + D + B + LT + FL + FT) +
249 (1/96)*(RT + BT + FR + FD + LD + BL)
250
251 c t 2 0 0 1 = eval (fv t) $
252 (3/8)*I +
253 (7/48)*(F + T + R) +
254 (1/48)*(L + D + B + RT + FR + FT) +
255 (1/96)*(LT + BT + FL + FD + RD + BR)
256
257 c t 2 1 0 0 = eval (fv t) $
258 (3/8)*I +
259 (1/12)*(T + R + L + D) +
260 (1/64)*(FT + FR + FL + FD) +
261 (7/48)*F +
262 (1/48)*B +
263 (1/96)*(RT + LD + LT + RD) +
264 (1/192)*(BT + BR + BL + BD)
265
266 c t 3 0 0 0 = eval (fv t) $
267 (3/8)*I +
268 (1/12)*(T + F + L + R + D + B) +
269 (1/96)*(LT + FL + FT + RT + BT + FR) +
270 (1/96)*(FD + LD + BD + BR + RD + BL)
271
272 c _ _ _ _ _ = error "coefficient index out of bounds"
273
274
275
276 -- | The matrix used in the tetrahedron volume calculation as given in
277 -- Lai & Schumaker, Definition 15.4, page 436.
278 vol_matrix :: Tetrahedron -> Matrix Double
279 vol_matrix t = (4><4)
280 [1, 1, 1, 1,
281 x1, x2, x3, x4,
282 y1, y2, y3, y4,
283 z1, z2, z3, z4 ]
284 where
285 (x1, y1, z1) = v0 t
286 (x2, y2, z2) = v1 t
287 (x3, y3, z3) = v2 t
288 (x4, y4, z4) = v3 t
289
290 -- | Computed using the formula from Lai & Schumaker, Definition 15.4,
291 -- page 436.
292 volume :: Tetrahedron -> Double
293 volume t
294 | (v0 t) == (v1 t) = 0
295 | (v0 t) == (v2 t) = 0
296 | (v0 t) == (v3 t) = 0
297 | (v1 t) == (v2 t) = 0
298 | (v1 t) == (v3 t) = 0
299 | (v2 t) == (v3 t) = 0
300 | otherwise = (1/6)*(det (vol_matrix t))
301
302
303 -- | The barycentric coordinates of a point with respect to v0.
304 b0 :: Tetrahedron -> (RealFunction Point)
305 b0 t point = (volume inner_tetrahedron) / (precomputed_volume t)
306 where
307 inner_tetrahedron = t { v0 = point }
308
309
310 -- | The barycentric coordinates of a point with respect to v1.
311 b1 :: Tetrahedron -> (RealFunction Point)
312 b1 t point = (volume inner_tetrahedron) / (precomputed_volume t)
313 where
314 inner_tetrahedron = t { v1 = point }
315
316
317 -- | The barycentric coordinates of a point with respect to v2.
318 b2 :: Tetrahedron -> (RealFunction Point)
319 b2 t point = (volume inner_tetrahedron) / (precomputed_volume t)
320 where
321 inner_tetrahedron = t { v2 = point }
322
323
324 -- | The barycentric coordinates of a point with respect to v3.
325 b3 :: Tetrahedron -> (RealFunction Point)
326 b3 t point = (volume inner_tetrahedron) / (precomputed_volume t)
327 where
328 inner_tetrahedron = t { v3 = point }