Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | #include "Maps.h" | ||
5 | #include <mutex> | ||
6 | |||
7 | namespace openvdb { | ||
8 | OPENVDB_USE_VERSION_NAMESPACE | ||
9 | namespace OPENVDB_VERSION_NAME { | ||
10 | namespace math { | ||
11 | |||
12 | namespace { | ||
13 | |||
14 | // Declare this at file scope to ensure thread-safe initialization. | ||
15 | // NOTE: Do *NOT* move this into Maps.h or else we will need to pull in | ||
16 | // Windows.h with things like 'rad2' defined! | ||
17 | std::mutex sInitMapRegistryMutex; | ||
18 | |||
19 | } // unnamed namespace | ||
20 | |||
21 | |||
22 | //////////////////////////////////////// | ||
23 | |||
24 | |||
25 | // Caller is responsible for calling this function serially. | ||
26 | MapRegistry* | ||
27 | 9324 | MapRegistry::staticInstance() | |
28 | { | ||
29 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9322 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
9324 | static MapRegistry registry; |
30 | 9324 | return ®istry; | |
31 | } | ||
32 | |||
33 | |||
34 | MapRegistry* | ||
35 | ✗ | MapRegistry::instance() | |
36 | { | ||
37 | std::lock_guard<std::mutex> lock(sInitMapRegistryMutex); | ||
38 | ✗ | return staticInstance(); | |
39 | } | ||
40 | |||
41 | |||
42 | MapBase::Ptr | ||
43 | 137 | MapRegistry::createMap(const Name& name) | |
44 | { | ||
45 | std::lock_guard<std::mutex> lock(sInitMapRegistryMutex); | ||
46 |
2/4✓ Branch 1 taken 137 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 137 times.
✗ Branch 5 not taken.
|
274 | MapDictionary::const_iterator iter = staticInstance()->mMap.find(name); |
47 | |||
48 |
2/4✓ Branch 1 taken 137 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 137 times.
|
137 | if (iter == staticInstance()->mMap.end()) { |
49 | ✗ | OPENVDB_THROW(LookupError, "Cannot create map of unregistered type " << name); | |
50 | } | ||
51 | |||
52 |
1/2✓ Branch 1 taken 137 times.
✗ Branch 2 not taken.
|
274 | return (iter->second)(); |
53 | } | ||
54 | |||
55 | |||
56 | bool | ||
57 | 157 | MapRegistry::isRegistered(const Name& name) | |
58 | { | ||
59 | std::lock_guard<std::mutex> lock(sInitMapRegistryMutex); | ||
60 |
3/6✓ Branch 1 taken 157 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 157 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 157 times.
✗ Branch 7 not taken.
|
471 | return (staticInstance()->mMap.find(name) != staticInstance()->mMap.end()); |
61 | } | ||
62 | |||
63 | |||
64 | void | ||
65 | 2681 | MapRegistry::registerMap(const Name& name, MapBase::MapFactory factory) | |
66 | { | ||
67 | std::lock_guard<std::mutex> lock(sInitMapRegistryMutex); | ||
68 | |||
69 |
3/6✓ Branch 1 taken 2681 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2681 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2681 times.
|
5362 | if (staticInstance()->mMap.find(name) != staticInstance()->mMap.end()) { |
70 | ✗ | OPENVDB_THROW(KeyError, "Map type " << name << " is already registered"); | |
71 | } | ||
72 | |||
73 |
3/6✓ Branch 1 taken 2681 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2681 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2681 times.
✗ Branch 7 not taken.
|
2681 | staticInstance()->mMap[name] = factory; |
74 | 2681 | } | |
75 | |||
76 | |||
77 | void | ||
78 | ✗ | MapRegistry::unregisterMap(const Name& name) | |
79 | { | ||
80 | std::lock_guard<std::mutex> lock(sInitMapRegistryMutex); | ||
81 | ✗ | staticInstance()->mMap.erase(name); | |
82 | } | ||
83 | |||
84 | |||
85 | void | ||
86 | 693 | MapRegistry::clear() | |
87 | { | ||
88 | std::lock_guard<std::mutex> lock(sInitMapRegistryMutex); | ||
89 |
1/2✓ Branch 1 taken 693 times.
✗ Branch 2 not taken.
|
693 | staticInstance()->mMap.clear(); |
90 | 693 | } | |
91 | |||
92 | |||
93 | //////////////////////////////////////// | ||
94 | |||
95 | // Utility methods for decomposition | ||
96 | |||
97 | |||
98 | SymmetricMap::Ptr | ||
99 | 1 | createSymmetricMap(const Mat3d& m) | |
100 | { | ||
101 | // test that the mat3 is a rotation || reflection | ||
102 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (!isSymmetric(m)) { |
103 | ✗ | OPENVDB_THROW(ArithmeticError, | |
104 | "3x3 Matrix initializing symmetric map was not symmetric"); | ||
105 | } | ||
106 | Vec3d eigenValues; | ||
107 | Mat3d Umatrix; | ||
108 | |||
109 | 1 | bool converged = math::diagonalizeSymmetricMatrix(m, Umatrix, eigenValues); | |
110 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!converged) { |
111 | ✗ | OPENVDB_THROW(ArithmeticError, "Diagonalization of the symmetric matrix failed"); | |
112 | } | ||
113 | |||
114 | 1 | UnitaryMap rotation(Umatrix); | |
115 | 1 | ScaleMap diagonal(eigenValues); | |
116 | 1 | CompoundMap<UnitaryMap, ScaleMap> first(rotation, diagonal); | |
117 | |||
118 | 1 | UnitaryMap rotationInv(Umatrix.transpose()); | |
119 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | return SymmetricMap::Ptr( new SymmetricMap(first, rotationInv)); |
120 | } | ||
121 | |||
122 | |||
123 | PolarDecomposedMap::Ptr | ||
124 | 1 | createPolarDecomposedMap(const Mat3d& m) | |
125 | { | ||
126 | // Because our internal libary left-multiplies vectors against matrices | ||
127 | // we are constructing M = Symmetric * Unitary instead of the more | ||
128 | // standard M = Unitary * Symmetric | ||
129 | Mat3d unitary, symmetric, mat3 = m.transpose(); | ||
130 | |||
131 | // factor mat3 = U * S where U is unitary and S is symmetric | ||
132 | 1 | bool gotPolar = math::polarDecomposition(mat3, unitary, symmetric); | |
133 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!gotPolar) { |
134 | ✗ | OPENVDB_THROW(ArithmeticError, "Polar decomposition of transform failed"); | |
135 | } | ||
136 | // put the result in a polar map and then copy it into the output polar | ||
137 | 1 | UnitaryMap unitary_map(unitary.transpose()); | |
138 | 1 | SymmetricMap::Ptr symmetric_map = createSymmetricMap(symmetric); | |
139 | |||
140 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | return PolarDecomposedMap::Ptr(new PolarDecomposedMap(*symmetric_map, unitary_map)); |
141 | } | ||
142 | |||
143 | |||
144 | FullyDecomposedMap::Ptr | ||
145 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | createFullyDecomposedMap(const Mat4d& m) |
146 | { | ||
147 | if (!isAffine(m)) { | ||
148 | ✗ | OPENVDB_THROW(ArithmeticError, | |
149 | "4x4 Matrix initializing Decomposition map was not affine"); | ||
150 | } | ||
151 | |||
152 | 1 | TranslationMap translate(m.getTranslation()); | |
153 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | PolarDecomposedMap::Ptr polar = createPolarDecomposedMap(m.getMat3()); |
154 | |||
155 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | UnitaryAndTranslationMap rotationAndTranslate(polar->secondMap(), translate); |
156 | |||
157 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | return FullyDecomposedMap::Ptr(new FullyDecomposedMap(polar->firstMap(), rotationAndTranslate)); |
158 | } | ||
159 | |||
160 | |||
161 | MapBase::Ptr | ||
162 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 101 times.
|
122 | simplify(AffineMap::Ptr affine) |
163 | { | ||
164 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 101 times.
|
122 | if (affine->isScale()) { // can be simplified into a ScaleMap |
165 | |||
166 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 9 times.
|
21 | Vec3d scale = affine->applyMap(Vec3d(1,1,1)); |
167 |
4/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 1 times.
|
21 | if (isApproxEqual(scale[0], scale[1]) && isApproxEqual(scale[0], scale[2])) { |
168 | 11 | return MapBase::Ptr(new UniformScaleMap(scale[0])); | |
169 | } else { | ||
170 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | return MapBase::Ptr(new ScaleMap(scale)); |
171 | } | ||
172 | |||
173 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 98 times.
|
101 | } else if (affine->isScaleTranslate()) { // can be simplified into a ScaleTranslateMap |
174 | |||
175 | 3 | Vec3d translate = affine->applyMap(Vec3d(0,0,0)); | |
176 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | Vec3d scale = affine->applyMap(Vec3d(1,1,1)) - translate; |
177 | |||
178 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | if (isApproxEqual(scale[0], scale[1]) && isApproxEqual(scale[0], scale[2])) { |
179 | 3 | return MapBase::Ptr(new UniformScaleTranslateMap(scale[0], translate)); | |
180 | } else { | ||
181 | ✗ | return MapBase::Ptr(new ScaleTranslateMap(scale, translate)); | |
182 | } | ||
183 | } | ||
184 | |||
185 | // could not simplify the general Affine map. | ||
186 | return StaticPtrCast<MapBase, AffineMap>(affine); | ||
187 | } | ||
188 | |||
189 | |||
190 | Mat4d | ||
191 | 4 | approxInverse(const Mat4d& mat4d) | |
192 | { | ||
193 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
|
4 | if (std::abs(mat4d.det()) >= 3 * math::Tolerance<double>::value()) { |
194 | try { | ||
195 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | return mat4d.inverse(); |
196 | ✗ | } catch (ArithmeticError& ) { | |
197 | // Mat4 code couldn't invert. | ||
198 | } | ||
199 | } | ||
200 | |||
201 | const Mat3d mat3 = mat4d.getMat3(); | ||
202 | const Mat3d mat3T = mat3.transpose(); | ||
203 | const Vec3d trans = mat4d.getTranslation(); | ||
204 | |||
205 | // absolute tolerance used for the symmetric test. | ||
206 | const double tol = 1.e-6; | ||
207 | |||
208 | // only create the pseudoInverse for symmetric | ||
209 | bool symmetric = true; | ||
210 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
|
12 | for (int i = 0; i < 3; ++i ) { |
211 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 9 times.
|
36 | for (int j = 0; j < 3; ++j ) { |
212 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 25 times.
|
27 | if (!isApproxEqual(mat3[i][j], mat3T[i][j], tol)) { |
213 | symmetric = false; | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | |||
218 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (!symmetric) { |
219 | |||
220 | // not symmetric, so just zero out the mat3 inverse and reverse the translation | ||
221 | |||
222 | 1 | Mat4d result = Mat4d::zero(); | |
223 | result.setTranslation(-trans); | ||
224 | 1 | result[3][3] = 1.f; | |
225 | 1 | return result; | |
226 | |||
227 | } else { | ||
228 | |||
229 | // compute the pseudo inverse | ||
230 | |||
231 | Mat3d eigenVectors; | ||
232 | Vec3d eigenValues; | ||
233 | |||
234 | 2 | diagonalizeSymmetricMatrix(mat3, eigenVectors, eigenValues); | |
235 | |||
236 | 2 | Mat3d d = Mat3d::identity(); | |
237 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | for (int i = 0; i < 3; ++i ) { |
238 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | if (std::abs(eigenValues[i]) < 10.0 * math::Tolerance<double>::value()) { |
239 | 2 | d[i][i] = 0.f; | |
240 | } else { | ||
241 | 4 | d[i][i] = 1.f/eigenValues[i]; | |
242 | } | ||
243 | } | ||
244 | // assemble the pseudo inverse | ||
245 | |||
246 | 2 | Mat3d pseudoInv = eigenVectors * d * eigenVectors.transpose(); | |
247 | 2 | Vec3d invTrans = -trans * pseudoInv; | |
248 | |||
249 | 2 | Mat4d result = Mat4d::identity(); | |
250 | result.setMat3(pseudoInv); | ||
251 | result.setTranslation(invTrans); | ||
252 | |||
253 | 2 | return result; | |
254 | } | ||
255 | } | ||
256 | |||
257 | } // namespace math | ||
258 | } // namespace OPENVDB_VERSION_NAME | ||
259 | } // namespace openvdb | ||
260 |