| 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 |