00001 #ifndef OPENTISSUE_DYNAMICS_FEM_FEM_UPDATE_ORIENTATION_H 00002 #define OPENTISSUE_DYNAMICS_FEM_FEM_UPDATE_ORIENTATION_H 00003 // 00004 // OpenTissue Template Library 00005 // - A generic toolbox for physics-based modeling and simulation. 00006 // Copyright (C) 2008 Department of Computer Science, University of Copenhagen. 00007 // 00008 // OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php 00009 // 00010 #include <OpenTissue/configuration.h> 00011 00012 namespace OpenTissue 00013 { 00014 namespace fem 00015 { 00016 namespace detail 00017 { 00018 00022 template<typename tetrahedron_iterator> 00023 inline void update_orientation(tetrahedron_iterator const & begin, tetrahedron_iterator const & end) 00024 { 00025 typedef typename tetrahedron_iterator::value_type::real_type real_type; 00026 typedef typename tetrahedron_iterator::value_type::vector3_type vector3_type; 00027 typedef typename tetrahedron_iterator::value_type::matrix3x3_type matrix3x3_type; 00028 00029 for(tetrahedron_iterator T = begin;T!=end;++T) 00030 { 00031 real_type div6V = 1.0 / T->m_V*6.0; 00032 //--- The derivation in the orignal paper on stiffness warping were stated as 00033 //--- 00034 //--- | n1^T | 00035 //--- N = | n2^T | : WCS -> U 00036 //--- | n3^T | 00037 //--- 00038 //--- | n1'^T | 00039 //--- N' = | n2'^T | : WCS -> D 00040 //--- | n3'^T | 00041 //--- 00042 //--- From which we have 00043 //--- 00044 //--- R = N' N^T 00045 //--- 00046 //--- This is valid under the assumption that the n-vectors form a orthonormal basis. In that 00047 //--- paper the n-vectors were determined using a heuristic approach. Besides 00048 //--- the rotation were computed on a per vertex basis not on a per-tetrahedron 00049 //--- bases as we have outline above. 00050 //--- 00051 //--- 00052 //--- Later Muller et. al. used barycentric coordinates to find the transform. We 00053 //--- will here go into details on this method. Let the deformed corners be q0, q1, 00054 //--- q2, and q3 and the undeformed corners p0, p1, p2,and p3. Looking at some 00055 //--- point p = [x y z 1]^T inside the undeformed tetrahedron this can be written 00056 //--- 00057 //--- | w0 | 00058 //--- p = | p0 p1 p2 p3 | | w1 | = P w (*1) 00059 //--- | 1 1 1 1 | | w2 | 00060 //--- | w3 | 00061 //--- 00062 //--- The same point in the deformed tetrahedron has the same barycentric coordinates 00063 //--- which mean 00064 //--- 00065 //--- | w0 | 00066 //--- q = | q0 q1 q2 q3 | | w1 | = Q w (*2) 00067 //--- | 1 1 1 1 | | w2 | 00068 //--- | w3 | 00069 //--- 00070 //--- We can now use (*1) to solve for w and insert this into (*2), this yields 00071 //--- 00072 //--- q = Q P^{-1} p 00073 //--- 00074 //--- The matirx Q P^{-1} transforms p into q. Due to P and Q having their fourth rows 00075 //--- equal to 1 it can be shown that this matrix have the block structure 00076 //--- 00077 //--- Q P^{-1} = | R t | (*3) 00078 //--- | 0^T 1 | 00079 //--- 00080 //--- Which we recognize as a transformation matrix of homegeneous coordinates. The t vector 00081 //--- gives the translation, the R matrix includes, scaling, shearing and rotation. 00082 //--- 00083 //--- We thus need to extract the rotational information from this R matrix. In their 00084 //--- paper Mueller et. al. suggest using Polar Decompostions (cite Shoemake and Duff; Etzmuss). 00085 //--- However, a simpler although more imprecise approach would simply be to apply a Grahram-Schimdt 00086 //--- orthonormalization to transform R into an orthonormal matrix. This seems to work quite well 00087 //--- in practice. We have not observed any visual difference in using polar decomposition or 00088 //--- orthonormalization. 00089 //--- 00090 //--- Now for some optimizations. Since we are only interested in the R part of (*3) we can 00091 //--- compute this more efficiently exploiting the fact that barycentric coordinates sum 00092 //--- up to one. If we substitute w0 = 1 - w1 - w2 - w3 in (*1) and (*2) we can throw away 00093 //--- the fourth rows since they simply state 1=1, which is trivially true. 00094 //--- 00095 //--- | 1-w1-w2-w3 | 00096 //--- p = | p0 p1 p2 p3 | | w1 | 00097 //--- | w2 | 00098 //--- | w3 | 00099 //--- 00100 //--- Which is 00101 //--- 00102 //--- | 1 | 00103 //--- p = | p0 (p1-p0) (p2-p0) (p3-p0) | | w1 | 00104 //--- | w2 | 00105 //--- | w3 | 00106 //--- 00107 //--- We can move the first column over on the left hand side 00108 //--- 00109 //--- | w1 | 00110 //--- (p-p0) = | (p1-p0) (p2-p0) (p3-p0) | | w2 | 00111 //--- | w3 | 00112 //--- 00113 //--- Introducing e10 = p1-p0, e20 = p2-p0, e30 = p3-p0, and E = [e10 e20 e30 ] we have 00114 //--- 00115 //--- w1 00116 //--- (p-p0) = E w2 (*5) 00117 //--- w3 00118 //--- 00119 //--- Similar for *(2) we have 00120 //--- 00121 //--- w1 00122 //--- (q-q0) = E' w2 (*6) 00123 //--- w3 00124 //--- 00125 //--- Where E' = [e10' e20' e3'] and e10' = q1-q0, e20' = q2-q0, e30' = q3-q0. Now 00126 //--- inverting (*5) and insertion into (*6) yields 00127 //--- 00128 //--- (q-q0) = E' E^{-1} (p-p0) 00129 //--- 00130 //--- By comparison with (*3) we see that 00131 //--- 00132 //--- R = E' E^{-1} 00133 //--- 00134 //--- Using Cramers rule the inverse of E can be written as 00135 //--- 00136 //--- | (e2 x e3)^T | 00137 //--- E^{-1} = 1/6V | (e3 x e1)^T | 00138 //--- | (e1 x e2)^T | 00139 //--- 00140 //--- This can easily be confirmed by straigthforward computation 00141 //--- 00142 //--- | e1 \cdot (e2 x e3) e2 \cdot (e2 x e3) e3 \cdot (e2 x e3) | 00143 //--- E^{-1} E = 1/6v | e1 \cdot (e3 x e1) e2 \cdot (e3 x e1) e3 \cdot (e3 x e1) | 00144 //--- | e1 \cdot (e1 x e2) e2 \cdot (e1 x e2) e3 \cdot (e1 x e2) | 00145 //--- 00146 //--- | 6V 0 0 | 00147 //--- = 1/6V | 0 6V 0 | = I 00148 //--- | 0 0 6V | 00149 //--- 00150 //--- Using the notation 00151 //--- 00152 //--- n1 = e2 x e3 / 6V 00153 //--- n2 = e3 x e1 / 6V 00154 //--- n3 = e1 x e2 / 6V 00155 //--- 00156 //--- we write 00157 //--- 00158 //--- | n1^T | 00159 //--- E^{-1} = | n2^T | 00160 //--- | n3^T | 00161 //--- 00162 //--- And we end up with 00163 //--- 00164 //--- | n1^T | 00165 //--- R = [e10' e20' e30'] | n2^T | 00166 //--- | n3^T | 00167 //--- 00168 //--- Observe that E' is very in-expensive to compute and all non-primed quantities (n1, n2, n3) can 00169 //--- be precomputed and stored on a per tetrahedron basis if there is enough memory available. Even 00170 //--- in case where memory is not available n1, n2 and n3 are quite cheap to compute. 00171 //--- 00172 00173 real_type e1x = T->m_e10(0); 00174 real_type e1y = T->m_e10(1); 00175 real_type e1z = T->m_e10(2); 00176 real_type e2x = T->m_e20(0); 00177 real_type e2y = T->m_e20(1); 00178 real_type e2z = T->m_e20(2); 00179 real_type e3x = T->m_e30(0); 00180 real_type e3y = T->m_e30(1); 00181 real_type e3z = T->m_e30(2); 00182 real_type n1x = (e2y * e3z - e3y * e2z) * div6V; 00183 real_type n1y = (e3x * e2z - e2x * e3z) * div6V; 00184 real_type n1z = (e2x * e3y - e3x * e2y) * div6V; 00185 real_type n2x = (e1z * e3y - e1y * e3z) * div6V; 00186 real_type n2y = (e1x * e3z - e1z * e3x) * div6V; 00187 real_type n2z = (e1y * e3x - e1x * e3y) * div6V; 00188 real_type n3x = (e1y * e2z - e1z * e2y) * div6V; 00189 real_type n3y = (e1z * e2x - e1x * e2z) * div6V; 00190 real_type n3z = (e1x * e2y - e1y * e2x) * div6V; 00191 vector3_type & p0 = T->i()->m_coord; 00192 vector3_type & p1 = T->j()->m_coord; 00193 vector3_type & p2 = T->k()->m_coord; 00194 vector3_type & p3 = T->m()->m_coord; 00195 e1x = p1(0) - p0(0); 00196 e1y = p1(1) - p0(1); 00197 e1z = p1(2) - p0(2); 00198 e2x = p2(0) - p0(0); 00199 e2y = p2(1) - p0(1); 00200 e2z = p2(2) - p0(2); 00201 e3x = p3(0) - p0(0); 00202 e3y = p3(1) - p0(1); 00203 e3z = p3(2) - p0(2); 00204 T->m_Re(0,0) = e1x * n1x + e2x * n2x + e3x * n3x; T->m_Re(0,1) = e1x * n1y + e2x * n2y + e3x * n3y; T->m_Re(0,2) = e1x * n1z + e2x * n2z + e3x * n3z; 00205 T->m_Re(1,0) = e1y * n1x + e2y * n2x + e3y * n3x; T->m_Re(1,1) = e1y * n1y + e2y * n2y + e3y * n3y; T->m_Re(1,2) = e1y * n1z + e2y * n2z + e3y * n3z; 00206 T->m_Re(2,0) = e1z * n1x + e2z * n2x + e3z * n3x; T->m_Re(2,1) = e1z * n1y + e2z * n2y + e3z * n3y; T->m_Re(2,2) = e1z * n1z + e2z * n2z + e3z * n3z; 00207 00208 T->m_Re = ortonormalize(T->m_Re); 00209 00210 //matrix3x3_type M = T->m_Re,S; 00211 //OpenTissue::PolarDecomposition3x3 decomp; 00212 //decomp.eigen(M, T->m_Re, S);//--- Etzmuss-style 00213 //decomp.newton(M, T->m_Re );//--- Shoemake-Duff-style 00214 } 00215 } 00216 00217 } // namespace detail 00218 } // namespace fem 00219 } // namespace OpenTissue 00220 00221 //OPENTISSUE_DYNAMICS_FEM_FEM_UPDATE_ORIENTATION_H 00222 #endif