| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | /// @file test/integration/CompareGrids.cc | ||
| 5 | |||
| 6 | #include "CompareGrids.h" | ||
| 7 | |||
| 8 | #include <openvdb/points/PointDataGrid.h> | ||
| 9 | #include <tbb/concurrent_vector.h> | ||
| 10 | |||
| 11 | namespace unittest_util | ||
| 12 | { | ||
| 13 | |||
| 14 | using TypeList = openvdb::TypeList< | ||
| 15 | double, | ||
| 16 | float, | ||
| 17 | int64_t, | ||
| 18 | int32_t, | ||
| 19 | int16_t, | ||
| 20 | bool, | ||
| 21 | openvdb::math::Vec2<double>, | ||
| 22 | openvdb::math::Vec2<float>, | ||
| 23 | openvdb::math::Vec2<int32_t>, | ||
| 24 | openvdb::math::Vec3<double>, | ||
| 25 | openvdb::math::Vec3<float>, | ||
| 26 | openvdb::math::Vec3<int32_t>, | ||
| 27 | openvdb::math::Vec4<double>, | ||
| 28 | openvdb::math::Vec4<float>, | ||
| 29 | openvdb::math::Vec4<int32_t>, | ||
| 30 | openvdb::math::Mat3<double>, | ||
| 31 | openvdb::math::Mat3<float>, | ||
| 32 | openvdb::math::Mat4<double>, | ||
| 33 | openvdb::math::Mat4<float>, | ||
| 34 | std::string>; | ||
| 35 | |||
| 36 | struct DiagnosticArrayData | ||
| 37 | { | ||
| 38 | DiagnosticArrayData() | ||
| 39 | ✗ | : mSizeMatch(true) | |
| 40 | , mTypesMatch(true) | ||
| 41 | , mFlagsMatch(true) | ||
| 42 | ✗ | , mArrayValueFlags() {} | |
| 43 | |||
| 44 | inline void | ||
| 45 | ✗ | flagArrayValue(const size_t idx) { | |
| 46 | ✗ | if (!mArrayValueFlags) mArrayValueFlags.reset(new std::vector<size_t>()); | |
| 47 | ✗ | (*mArrayValueFlags).push_back(idx); | |
| 48 | } | ||
| 49 | |||
| 50 | bool mSizeMatch; | ||
| 51 | bool mTypesMatch; | ||
| 52 | bool mFlagsMatch; | ||
| 53 | std::unique_ptr<std::vector<size_t>> mArrayValueFlags; | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct DiagnosticData | ||
| 57 | { | ||
| 58 | using Ptr = std::unique_ptr<DiagnosticData>; | ||
| 59 | virtual ~DiagnosticData() = default; | ||
| 60 | |||
| 61 | virtual void report(std::ostream&, | ||
| 62 | const openvdb::GridBase&, | ||
| 63 | const openvdb::GridBase&, | ||
| 64 | openvdb::MaskGrid::Accessor&, | ||
| 65 | openvdb::MaskGrid::Accessor&) = 0; | ||
| 66 | }; | ||
| 67 | |||
| 68 | template <typename NodeT> | ||
| 69 | struct NodeDD : public DiagnosticData | ||
| 70 | { | ||
| 71 | using Ptr = std::unique_ptr<NodeDD<NodeT>>; | ||
| 72 | using GridT = typename openvdb::BoolGrid::ValueConverter<typename NodeT::ValueType>::Type; | ||
| 73 | |||
| 74 | 7551 | NodeDD(const openvdb::Coord& origin) | |
| 75 | : mOrigin(origin) | ||
| 76 | , mValid(true) | ||
| 77 | , mBufferSizes(true) | ||
| 78 | , mTopologyFlags(true) | ||
| 79 | , mVoxelFlags(true) | ||
| 80 | , mDescriptorsMatch(true) | ||
| 81 |
60/120✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 45 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 45 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 64 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 64 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 64 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 69 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 69 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 69 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 62 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 62 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 62 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 69 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 69 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 69 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 65 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 65 times.
✗ Branch 34 not taken.
✓ Branch 35 taken 65 times.
✗ Branch 36 not taken.
✓ Branch 37 taken 66 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 66 times.
✗ Branch 40 not taken.
✓ Branch 41 taken 66 times.
✗ Branch 42 not taken.
✓ Branch 43 taken 69 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 69 times.
✗ Branch 46 not taken.
✓ Branch 47 taken 69 times.
✗ Branch 48 not taken.
✓ Branch 49 taken 75 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 75 times.
✗ Branch 52 not taken.
✓ Branch 53 taken 75 times.
✗ Branch 54 not taken.
✓ Branch 55 taken 116 times.
✗ Branch 56 not taken.
✓ Branch 57 taken 116 times.
✗ Branch 58 not taken.
✓ Branch 59 taken 116 times.
✗ Branch 60 not taken.
✓ Branch 61 taken 130 times.
✗ Branch 62 not taken.
✓ Branch 63 taken 130 times.
✗ Branch 64 not taken.
✓ Branch 65 taken 130 times.
✗ Branch 66 not taken.
✓ Branch 67 taken 49 times.
✗ Branch 68 not taken.
✓ Branch 69 taken 49 times.
✗ Branch 70 not taken.
✓ Branch 71 taken 49 times.
✗ Branch 72 not taken.
✓ Branch 73 taken 49 times.
✗ Branch 74 not taken.
✓ Branch 75 taken 49 times.
✗ Branch 76 not taken.
✓ Branch 77 taken 49 times.
✗ Branch 78 not taken.
✓ Branch 79 taken 49 times.
✗ Branch 80 not taken.
✓ Branch 81 taken 49 times.
✗ Branch 82 not taken.
✓ Branch 83 taken 49 times.
✗ Branch 84 not taken.
✓ Branch 85 taken 449 times.
✗ Branch 86 not taken.
✓ Branch 87 taken 449 times.
✗ Branch 88 not taken.
✓ Branch 89 taken 449 times.
✗ Branch 90 not taken.
✓ Branch 91 taken 34 times.
✗ Branch 92 not taken.
✓ Branch 93 taken 34 times.
✗ Branch 94 not taken.
✓ Branch 95 taken 34 times.
✗ Branch 96 not taken.
✓ Branch 97 taken 286 times.
✗ Branch 98 not taken.
✓ Branch 99 taken 286 times.
✗ Branch 100 not taken.
✓ Branch 101 taken 289 times.
✗ Branch 102 not taken.
✓ Branch 103 taken 99 times.
✗ Branch 104 not taken.
✓ Branch 105 taken 99 times.
✗ Branch 106 not taken.
✓ Branch 107 taken 99 times.
✗ Branch 108 not taken.
✓ Branch 109 taken 273 times.
✗ Branch 110 not taken.
✓ Branch 111 taken 273 times.
✗ Branch 112 not taken.
✓ Branch 113 taken 273 times.
✗ Branch 114 not taken.
✓ Branch 115 taken 398 times.
✗ Branch 116 not taken.
✓ Branch 117 taken 398 times.
✗ Branch 118 not taken.
✓ Branch 119 taken 398 times.
|
15102 | , mAttributeArrayData() {} |
| 82 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7551 times.
|
15102 | ~NodeDD() override = default; |
| 83 | |||
| 84 | bool hasValueFlags() const { return !mVoxelFlags.isOn(); } | ||
| 85 | bool hasTopologyFlags() const { return !mTopologyFlags.isOn(); } | ||
| 86 | ✗ | void flagVoxelTopology(const int16_t idx) { mTopologyFlags.setOff(idx); } | |
| 87 | ✗ | void flagVoxelValue(const int16_t idx) { mVoxelFlags.setOff(idx); } | |
| 88 | |||
| 89 | inline DiagnosticArrayData& | ||
| 90 | ✗ | getDiagnosticArrayData(const std::string& name) | |
| 91 | { | ||
| 92 | ✗ | if (!mAttributeArrayData) { | |
| 93 | ✗ | mAttributeArrayData.reset(new std::map<std::string, DiagnosticArrayData>()); | |
| 94 | } | ||
| 95 | ✗ | return (*mAttributeArrayData)[name]; | |
| 96 | } | ||
| 97 | |||
| 98 | bool hasDiagnosticArrayData() const { return static_cast<bool>(mAttributeArrayData); } | ||
| 99 | |||
| 100 | inline bool | ||
| 101 | hasDiagnosticArrayData(const std::string& name) const { | ||
| 102 | return (hasDiagnosticArrayData() && | ||
| 103 | mAttributeArrayData->find(name) != mAttributeArrayData->end()); | ||
| 104 | } | ||
| 105 | |||
| 106 | ✗ | inline void report(std::ostream& os, | |
| 107 | const openvdb::GridBase& grid1, | ||
| 108 | const openvdb::GridBase& grid2, | ||
| 109 | openvdb::MaskGrid::Accessor& accessorTopology, | ||
| 110 | openvdb::MaskGrid::Accessor& accessorValues) override | ||
| 111 | { | ||
| 112 | struct Local { | ||
| 113 | // flag to string | ||
| 114 | static std::string fts(const bool flag) { | ||
| 115 | ✗ | return (flag ? "[SUCCESS]" : "[FAILED]"); | |
| 116 | } | ||
| 117 | }; | ||
| 118 | |||
| 119 | const GridT& g1 = static_cast<const GridT&>(grid1); | ||
| 120 | const GridT& g2 = static_cast<const GridT&>(grid2); | ||
| 121 | |||
| 122 | ✗ | os << " Coord : " << mOrigin << std::endl; | |
| 123 | ✗ | os << " Both Valid : " << Local::fts(this->mValid) << std::endl; | |
| 124 | ✗ | if (!this->mValid) { | |
| 125 | const bool second = g1.constTree().template probeConstNode<NodeT>(mOrigin); | ||
| 126 | os << " Missing in " << (second ? "second" : "first") | ||
| 127 | ✗ | << " grid." | |
| 128 | << std::endl; | ||
| 129 | ✗ | return; | |
| 130 | } | ||
| 131 | |||
| 132 | const auto& l1 = g1.constTree().template probeConstNode<NodeT>(mOrigin); | ||
| 133 | const auto& l2 = g2.constTree().template probeConstNode<NodeT>(mOrigin); | ||
| 134 | ✗ | assert(l1 && l2); | |
| 135 | |||
| 136 | ✗ | os << " Buffer Sizes : " << Local::fts(this->mBufferSizes) << std::endl; | |
| 137 | |||
| 138 | const bool topologyMatch = !this->hasTopologyFlags(); | ||
| 139 | ✗ | os << " Topology : " << Local::fts(topologyMatch) << std::endl; | |
| 140 | |||
| 141 | ✗ | if (!topologyMatch) { | |
| 142 | os << " The following voxel topologies differ : " << std::endl; | ||
| 143 | ✗ | for (auto iter = this->mTopologyFlags.beginOff(); iter; ++iter) { | |
| 144 | ✗ | const openvdb::Coord coord = l1->offsetToGlobalCoord(iter.pos()); | |
| 145 | ✗ | os << " [" << iter.pos() << "] "<< coord | |
| 146 | ✗ | << " G1: " << l1->isValueOn(coord) | |
| 147 | ✗ | << " - G2: " << l2->isValueOn(coord) | |
| 148 | << std::endl; | ||
| 149 | ✗ | accessorTopology.setValue(coord, true); | |
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | const bool valueMatch = !this->hasValueFlags(); | ||
| 154 | ✗ | os << " Values : " << Local::fts(valueMatch) << std::endl; | |
| 155 | |||
| 156 | ✗ | if (!valueMatch) { | |
| 157 | os << " The following voxel values differ : " << std::endl; | ||
| 158 | ✗ | for (auto iter = this->mVoxelFlags.beginOff(); iter; ++iter) { | |
| 159 | ✗ | const openvdb::Coord coord = l1->offsetToGlobalCoord(iter.pos()); | |
| 160 | ✗ | os << " [" << iter.pos() << "] "<< coord | |
| 161 | ✗ | << " G1: " << l1->getValue(coord) | |
| 162 | ✗ | << " - G2: " << l2->getValue(coord) | |
| 163 | << std::endl; | ||
| 164 | ✗ | accessorValues.setValue(coord, true); | |
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | ✗ | if (g1.template isType<openvdb::points::PointDataGrid>()) { | |
| 169 | ✗ | os << " Descriptors : " << Local::fts(this->mDescriptorsMatch) << std::endl; | |
| 170 | const bool attributesMatch = !static_cast<bool>(this->mAttributeArrayData); | ||
| 171 | ✗ | os << " Array Data : " << Local::fts(attributesMatch) << std::endl; | |
| 172 | ✗ | if (!attributesMatch) { | |
| 173 | os << " The following attribute values : " << std::endl; | ||
| 174 | ✗ | for (const auto& iter : *(this->mAttributeArrayData)) { | |
| 175 | |||
| 176 | const std::string& name = iter.first; | ||
| 177 | const DiagnosticArrayData& arrayData = iter.second; | ||
| 178 | |||
| 179 | os << " Attribute Array : [" << name << "] \n" | ||
| 180 | ✗ | << " Size Match : " << Local::fts(arrayData.mSizeMatch) << "\n" | |
| 181 | ✗ | << " Type Match : " << Local::fts(arrayData.mTypesMatch) << "\n" | |
| 182 | ✗ | << " Flags Match : " << Local::fts(arrayData.mFlagsMatch) | |
| 183 | << std::endl; | ||
| 184 | |||
| 185 | const bool arrayValuesMatch = !static_cast<bool>(arrayData.mArrayValueFlags); | ||
| 186 | ✗ | os << " Array Values : " << Local::fts(arrayValuesMatch) << std::endl; | |
| 187 | ✗ | if (!arrayValuesMatch) { | |
| 188 | ✗ | for (size_t idx : *(arrayData.mArrayValueFlags)) { | |
| 189 | os << " [" << idx << "] " | ||
| 190 | << std::endl; | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | openvdb::Coord mOrigin; | ||
| 199 | bool mValid; | ||
| 200 | bool mBufferSizes; | ||
| 201 | typename NodeT::NodeMaskType mTopologyFlags; | ||
| 202 | typename NodeT::NodeMaskType mVoxelFlags; | ||
| 203 | bool mDescriptorsMatch; | ||
| 204 | std::unique_ptr<std::map<std::string, DiagnosticArrayData>> mAttributeArrayData; | ||
| 205 | }; | ||
| 206 | |||
| 207 | template <typename NodeT, | ||
| 208 | typename std::enable_if<(NodeT::LEVEL == 0)>::type* = nullptr> | ||
| 209 | 5038 | inline bool compareNodes(const NodeT& firstLeaf, | |
| 210 | const NodeT& secondLeaf, | ||
| 211 | const typename NodeT::NodeMaskType& mask, | ||
| 212 | NodeDD<NodeT>& data, | ||
| 213 | const ComparisonSettings& settings, | ||
| 214 | const typename NodeT::ValueType& tolerance) | ||
| 215 | { | ||
| 216 | using BufferT = typename NodeT::Buffer; | ||
| 217 | |||
| 218 | const BufferT& firstBuffer = firstLeaf.buffer(); | ||
| 219 | const BufferT& secondBuffer = secondLeaf.buffer(); | ||
| 220 | |||
| 221 | // if the buffers are not the same size the buffer most likely isn't | ||
| 222 | // loaded or allocated | ||
| 223 | |||
| 224 | if (firstBuffer.size() != secondBuffer.size()) { | ||
| 225 | data.mBufferSizes = false; | ||
| 226 | return false; | ||
| 227 | } | ||
| 228 | |||
| 229 | const typename NodeT::NodeMaskType& firstMask = firstLeaf.getValueMask(); | ||
| 230 | const typename NodeT::NodeMaskType& secondMask = secondLeaf.getValueMask(); | ||
| 231 | 5038 | typename NodeT::NodeMaskType::OnIterator iter = mask.beginOn(); | |
| 232 | |||
| 233 |
2/2✓ Branch 0 taken 2519 times.
✓ Branch 1 taken 2519 times.
|
10076 | for (; iter; ++iter) { |
| 234 | const openvdb::Index n = iter.pos(); | ||
| 235 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2519 times.
|
5038 | assert(n < firstBuffer.size() && n < secondBuffer.size()); |
| 236 | |||
| 237 |
2/4✓ Branch 0 taken 2519 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2519 times.
|
10076 | if (settings.mCheckActiveStates && |
| 238 | 5038 | firstMask.isOn(n) ^ secondMask.isOn(n)) { | |
| 239 | ✗ | data.flagVoxelTopology(static_cast<int16_t>(n)); | |
| 240 | } | ||
| 241 | |||
| 242 |
2/4✓ Branch 0 taken 2519 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1851 times.
|
8740 | if (settings.mCheckBufferValues && |
| 243 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
|
928 | !openvdb::math::isApproxEqual(firstBuffer[n], secondBuffer[n], tolerance)) { |
| 244 | ✗ | data.flagVoxelValue(static_cast<int16_t>(n)); | |
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 |
2/4✓ Branch 0 taken 2519 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2519 times.
|
10076 | return !data.hasValueFlags() && !data.hasTopologyFlags(); |
| 249 | } | ||
| 250 | |||
| 251 | |||
| 252 | template <typename NodeT, | ||
| 253 | typename std::enable_if<(NodeT::LEVEL != 0)>::type* = nullptr> | ||
| 254 | 10064 | inline bool compareNodes(const NodeT& n1, | |
| 255 | const NodeT& n2, | ||
| 256 | const typename NodeT::NodeMaskType& mask, | ||
| 257 | NodeDD<NodeT>& data, | ||
| 258 | const ComparisonSettings& settings, | ||
| 259 | const typename NodeT::ValueType& tolerance) | ||
| 260 | { | ||
| 261 | const auto& vmask1 = n1.getValueMask(); | ||
| 262 | const auto& vmask2 = n2.getValueMask(); | ||
| 263 | const auto& cmask1 = n1.getChildMask(); | ||
| 264 | const auto& cmask2 = n2.getChildMask(); | ||
| 265 | |||
| 266 | auto* t1 = n1.getTable(); | ||
| 267 | auto* t2 = n2.getTable(); | ||
| 268 | |||
| 269 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5032 times.
|
20128 | for (auto iter = mask.beginOn(); iter; ++iter) { |
| 270 | const openvdb::Index n = iter.pos(); | ||
| 271 | |||
| 272 | ✗ | if ((vmask1.isOn(n) ^ vmask2.isOn(n)) || | |
| 273 | ✗ | (cmask1.isOn(n) ^ cmask2.isOn(n))) { | |
| 274 | ✗ | data.flagVoxelTopology(static_cast<int16_t>(n)); | |
| 275 | ✗ | continue; // can't check values if topology is different | |
| 276 | } | ||
| 277 | |||
| 278 | ✗ | if (cmask1.isOn(n) && cmask2.isOn(n)) continue; | |
| 279 | ✗ | assert(vmask1.isOn(n) && vmask2.isOn(n)); | |
| 280 | |||
| 281 | ✗ | if (settings.mCheckBufferValues && | |
| 282 | ✗ | !openvdb::math::isApproxEqual(t1[n].getValue(), t2[n].getValue(), tolerance)) { | |
| 283 | ✗ | data.flagVoxelValue(static_cast<int16_t>(n)); | |
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 |
2/4✓ Branch 0 taken 5032 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5032 times.
|
20128 | return !data.hasValueFlags() && !data.hasTopologyFlags(); |
| 288 | } | ||
| 289 | |||
| 290 | |||
| 291 | ✗ | void compareStringArrays(const openvdb::points::AttributeArray& a1, | |
| 292 | const openvdb::points::AttributeArray& a2, | ||
| 293 | const openvdb::points::PointDataTree::LeafNodeType& leaf1, | ||
| 294 | const openvdb::points::PointDataTree::LeafNodeType& leaf2, | ||
| 295 | const std::string& name, | ||
| 296 | NodeDD<openvdb::points::PointDataTree::LeafNodeType>& data) | ||
| 297 | { | ||
| 298 | using LeafNodeT = openvdb::points::PointDataTree::LeafNodeType; | ||
| 299 | |||
| 300 | ✗ | if (a1.size() != a2.size()) { | |
| 301 | ✗ | auto& arrayData = data.getDiagnosticArrayData(name); | |
| 302 | ✗ | arrayData.mSizeMatch = false; | |
| 303 | } | ||
| 304 | |||
| 305 | const openvdb::points::AttributeSet::Descriptor& descriptor1 = leaf1.attributeSet().descriptor(); | ||
| 306 | const openvdb::points::AttributeSet::Descriptor& descriptor2 = leaf2.attributeSet().descriptor(); | ||
| 307 | |||
| 308 | ✗ | openvdb::points::StringAttributeHandle h1(a1, descriptor1.getMetadata()), h2(a2, descriptor2.getMetadata()); | |
| 309 | auto iter = leaf1.beginIndexAll(); | ||
| 310 | |||
| 311 | ✗ | for (; iter; ++iter) { | |
| 312 | ✗ | if (h1.get(*iter) != h2.get(*iter)) break; | |
| 313 | } | ||
| 314 | |||
| 315 | ✗ | if (iter) { | |
| 316 | ✗ | auto& arrayData = data.getDiagnosticArrayData(name); | |
| 317 | ✗ | for (; iter; ++iter) { | |
| 318 | ✗ | const openvdb::Index i = *iter; | |
| 319 | ✗ | if (h1.get(i) != h2.get(i)) { | |
| 320 | ✗ | arrayData.flagArrayValue(i); | |
| 321 | ✗ | data.flagVoxelValue(static_cast<int16_t>(LeafNodeT::coordToOffset(iter.getCoord()))); | |
| 322 | } | ||
| 323 | } | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | template <typename ValueType> | ||
| 328 | inline void compareArrays(const openvdb::points::AttributeArray& a1, | ||
| 329 | const openvdb::points::AttributeArray& a2, | ||
| 330 | const openvdb::points::PointDataTree::LeafNodeType& leaf, | ||
| 331 | const std::string& name, | ||
| 332 | NodeDD<openvdb::points::PointDataTree::LeafNodeType>& data) | ||
| 333 | { | ||
| 334 | using LeafNodeT = openvdb::points::PointDataTree::LeafNodeType; | ||
| 335 | |||
| 336 | if (a1.size() != a2.size()) { | ||
| 337 | auto& arrayData = data.getDiagnosticArrayData(name); | ||
| 338 | arrayData.mSizeMatch = false; | ||
| 339 | } | ||
| 340 | |||
| 341 | openvdb::points::AttributeHandle<ValueType> h1(a1), h2(a2); | ||
| 342 | auto iter = leaf.beginIndexAll(); | ||
| 343 | |||
| 344 | for (; iter; ++iter) { | ||
| 345 | if (h1.get(*iter) != h2.get(*iter)) break; | ||
| 346 | } | ||
| 347 | |||
| 348 | if (iter) { | ||
| 349 | auto& arrayData = data.getDiagnosticArrayData(name); | ||
| 350 | for (; iter; ++iter) { | ||
| 351 | const openvdb::Index i = *iter; | ||
| 352 | if (h1.get(i) != h2.get(i)) { | ||
| 353 | arrayData.flagArrayValue(i); | ||
| 354 | data.flagVoxelValue(static_cast<int16_t>(LeafNodeT::coordToOffset(iter.getCoord()))); | ||
| 355 | } | ||
| 356 | } | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | template <typename LeafNodeType> | ||
| 361 | inline bool | ||
| 362 | compareAttributes(const LeafNodeType&, | ||
| 363 | const LeafNodeType&, | ||
| 364 | NodeDD<LeafNodeType>&, | ||
| 365 | const ComparisonSettings&) { | ||
| 366 | return true; | ||
| 367 | } | ||
| 368 | |||
| 369 | template <> | ||
| 370 | inline bool | ||
| 371 | compareAttributes<openvdb::points::PointDataTree::LeafNodeType> | ||
| 372 | (const openvdb::points::PointDataTree::LeafNodeType& firstLeaf, | ||
| 373 | const openvdb::points::PointDataTree::LeafNodeType& secondLeaf, | ||
| 374 | NodeDD<openvdb::points::PointDataTree::LeafNodeType>& data, | ||
| 375 | const ComparisonSettings& settings) | ||
| 376 | { | ||
| 377 | using Descriptor = openvdb::points::AttributeSet::Descriptor; | ||
| 378 | |||
| 379 | const Descriptor& firstDescriptor = firstLeaf.attributeSet().descriptor(); | ||
| 380 | const Descriptor& secondDescriptor = secondLeaf.attributeSet().descriptor(); | ||
| 381 | |||
| 382 | if (settings.mCheckDescriptors && | ||
| 383 | !firstDescriptor.hasSameAttributes(secondDescriptor)) { | ||
| 384 | data.mDescriptorsMatch = false; | ||
| 385 | } | ||
| 386 | |||
| 387 | // check common/miss-matching attributes | ||
| 388 | |||
| 389 | std::set<std::string> attrs1, attrs2; | ||
| 390 | for (const auto& nameToPos : firstDescriptor.map()) { | ||
| 391 | attrs1.insert(nameToPos.first); | ||
| 392 | } | ||
| 393 | for (const auto& nameToPos : secondDescriptor.map()) { | ||
| 394 | attrs2.insert(nameToPos.first); | ||
| 395 | } | ||
| 396 | |||
| 397 | std::vector<std::string> commonAttributes; | ||
| 398 | std::set_intersection(attrs1.begin(), | ||
| 399 | attrs1.end(), | ||
| 400 | attrs2.begin(), | ||
| 401 | attrs2.end(), | ||
| 402 | std::back_inserter(commonAttributes)); | ||
| 403 | |||
| 404 | for (const std::string& name : commonAttributes) { | ||
| 405 | const size_t pos1 = firstDescriptor.find(name); | ||
| 406 | const size_t pos2 = secondDescriptor.find(name); | ||
| 407 | const auto& array1 = firstLeaf.constAttributeArray(pos1); | ||
| 408 | const auto& array2 = secondLeaf.constAttributeArray(pos2); | ||
| 409 | |||
| 410 | const std::string& type = array1.type().first; | ||
| 411 | if (type != array2.type().first) { | ||
| 412 | // this mismatch is also loged by differing descriptors | ||
| 413 | auto& arrayData = data.getDiagnosticArrayData(name); | ||
| 414 | arrayData.mTypesMatch = false; | ||
| 415 | continue; | ||
| 416 | } | ||
| 417 | |||
| 418 | if (settings.mCheckArrayFlags && | ||
| 419 | array1.flags() != array2.flags()) { | ||
| 420 | auto& arrayData = data.getDiagnosticArrayData(name); | ||
| 421 | arrayData.mFlagsMatch = false; | ||
| 422 | } | ||
| 423 | |||
| 424 | if (settings.mCheckArrayValues) { | ||
| 425 | if (array1.type().second == "str") { | ||
| 426 | compareStringArrays(array1, array2, firstLeaf, secondLeaf, name, data); | ||
| 427 | } | ||
| 428 | else { | ||
| 429 | bool success = false; | ||
| 430 | // Remove string types but add uint8_t types (used by group arrays) | ||
| 431 | TypeList::Remove<std::string>::Append<uint8_t>::foreach([&](auto x) { | ||
| 432 | if (type == openvdb::typeNameAsString<decltype(x)>()) { | ||
| 433 | compareArrays<decltype(x)>(array1, array2, firstLeaf, name, data); | ||
| 434 | success = true; | ||
| 435 | } | ||
| 436 | }); | ||
| 437 | |||
| 438 | if (!success) { | ||
| 439 | throw std::runtime_error("Unsupported array type for comparison: " + type); | ||
| 440 | } | ||
| 441 | } | ||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 445 | return !data.hasDiagnosticArrayData() && data.mDescriptorsMatch; | ||
| 446 | } | ||
| 447 | |||
| 448 | template<typename TreeType> | ||
| 449 | struct CompareNodes | ||
| 450 | { | ||
| 451 | using LeafManagerT = openvdb::tree::LeafManager<const openvdb::MaskTree>; | ||
| 452 | using LeafNodeType = typename TreeType::LeafNodeType; | ||
| 453 | using ConstGridAccessor = openvdb::tree::ValueAccessor<const TreeType>; | ||
| 454 | |||
| 455 | 6466 | CompareNodes(tbb::concurrent_vector<DiagnosticData::Ptr>& data, | |
| 456 | const TreeType& firstTree, | ||
| 457 | const TreeType& secondTree, | ||
| 458 | const typename TreeType::ValueType tolerance, | ||
| 459 | const ComparisonSettings& settings, | ||
| 460 | const bool useVoxelMask = true) | ||
| 461 | : mDiagnosticData(data) | ||
| 462 | , mFirst(firstTree) | ||
| 463 | , mSecond(secondTree) | ||
| 464 | , mTolerance(tolerance) | ||
| 465 | , mSettings(settings) | ||
| 466 |
1/2✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
|
12932 | , mUseVoxelMask(useVoxelMask) {} |
| 467 | |||
| 468 | void operator()(const openvdb::MaskTree::RootNodeType&) const {} | ||
| 469 | |||
| 470 | template <typename MaskNodeT> | ||
| 471 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2880 times.
|
20862 | void operator()(MaskNodeT& node) const |
| 472 | { | ||
| 473 | using NodeT = typename MaskNodeT::template ValueConverter<typename TreeType::ValueType>::Type; | ||
| 474 | |||
| 475 | const openvdb::Coord& origin = node.origin(); | ||
| 476 | 15102 | const NodeT* const n1 = mFirst.template probeConstNode<NodeT>(origin); | |
| 477 | 15102 | const NodeT* const n2 = mSecond.template probeConstNode<NodeT>(origin); | |
| 478 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7551 times.
|
15102 | if (n1 == nullptr && n2 == nullptr) return; |
| 479 | |||
| 480 | 15102 | typename NodeDD<NodeT>::Ptr data(new NodeDD<NodeT>(origin)); | |
| 481 | |||
| 482 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7551 times.
|
15102 | if (static_cast<bool>(n1) ^ static_cast<bool>(n2)) { |
| 483 | ✗ | data->mValid = false; | |
| 484 | } | ||
| 485 | else { | ||
| 486 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7551 times.
|
15102 | assert(n1 && n2); |
| 487 | const typename MaskNodeT::NodeMaskType | ||
| 488 |
1/2✓ Branch 0 taken 7551 times.
✗ Branch 1 not taken.
|
15102 | mask(mUseVoxelMask ? node.getValueMask() : true); |
| 489 |
2/4✓ Branch 1 taken 7551 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2070 times.
✗ Branch 4 not taken.
|
15102 | if (compareNodes(*n1, *n2, mask, *data, mSettings, mTolerance) && |
| 490 | compareAttributes(*n1, *n2, *data, mSettings)) { | ||
| 491 | data.reset(); | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 7551 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
15102 | if (data) mDiagnosticData.emplace_back(std::move(data)); |
| 496 | } | ||
| 497 | |||
| 498 | private: | ||
| 499 | tbb::concurrent_vector<DiagnosticData::Ptr>& mDiagnosticData; | ||
| 500 | const ConstGridAccessor mFirst; | ||
| 501 | const ConstGridAccessor mSecond; | ||
| 502 | const typename TreeType::ValueType mTolerance; | ||
| 503 | const ComparisonSettings& mSettings; | ||
| 504 | const bool mUseVoxelMask; | ||
| 505 | }; | ||
| 506 | |||
| 507 | template <typename GridType> | ||
| 508 | 11488 | bool compareGrids(ComparisonResult& resultData, | |
| 509 | const GridType& firstGrid, | ||
| 510 | const GridType& secondGrid, | ||
| 511 | const ComparisonSettings& settings, | ||
| 512 | const openvdb::MaskGrid::ConstPtr maskGrid, | ||
| 513 | const typename GridType::ValueType tolerance) | ||
| 514 | { | ||
| 515 | using TreeType = typename GridType::TreeType; | ||
| 516 | using NodeManager = openvdb::tree::NodeManager<const openvdb::MaskTree, | ||
| 517 | openvdb::MaskTree::RootNodeType::LEVEL>; | ||
| 518 | |||
| 519 | struct Local { | ||
| 520 | // flag to string | ||
| 521 | static std::string fts(const bool flag) { | ||
| 522 |
7/14✗ Branch 0 not taken.
✓ Branch 1 taken 5744 times.
✓ Branch 4 taken 5744 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3233 times.
✓ Branch 10 taken 3233 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 3233 times.
✓ Branch 15 taken 3233 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 3233 times.
✗ Branch 19 not taken.
|
24420 | return (flag ? "[SUCCESS]" : "[FAILED]"); |
| 523 | } | ||
| 524 | }; | ||
| 525 | |||
| 526 | bool result = true; | ||
| 527 | bool flag = true; | ||
| 528 | 11488 | std::ostream& os = resultData.mOs; | |
| 529 | |||
| 530 | os << "[Diagnostic : Compare Leaf Nodes Result]\n" | ||
| 531 | << " First Grid: \"" << firstGrid.getName() << "\"\n" | ||
| 532 |
1/2✓ Branch 2 taken 5744 times.
✗ Branch 3 not taken.
|
34464 | << " Second Grid: \"" << secondGrid.getName() << "\"\n" |
| 533 | << std::endl; | ||
| 534 | |||
| 535 |
3/4✓ Branch 0 taken 3235 times.
✓ Branch 1 taken 2509 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3235 times.
|
17958 | if (firstGrid.tree().hasActiveTiles() || |
| 536 | secondGrid.tree().hasActiveTiles()) { | ||
| 537 | os << "[Diagnostic : WARNING]: Grids contain active tiles which will not be compared." | ||
| 538 | << std::endl; | ||
| 539 | } | ||
| 540 | |||
| 541 |
1/2✓ Branch 0 taken 5744 times.
✗ Branch 1 not taken.
|
11488 | if (settings.mCheckTransforms) { |
| 542 | 11488 | flag = (firstGrid.constTransform() == secondGrid.constTransform()); | |
| 543 | result &= flag; | ||
| 544 | 11488 | os << "[Diagnostic]: Grid transformations: " << Local::fts(flag) | |
| 545 | << std::endl; | ||
| 546 | } | ||
| 547 | |||
| 548 | 11488 | const openvdb::Index64 leafCount1 = firstGrid.tree().leafCount(); | |
| 549 | 11488 | const openvdb::Index64 leafCount2 = secondGrid.tree().leafCount(); | |
| 550 | flag = (leafCount1 == 0 && leafCount2 == 0); | ||
| 551 |
2/2✓ Branch 0 taken 2511 times.
✓ Branch 1 taken 3233 times.
|
11488 | if (flag) { |
| 552 | os << "[Diagnostic]: Both grids contain 0 leaf nodes." | ||
| 553 | << std::endl; | ||
| 554 | 5022 | return result; | |
| 555 | } | ||
| 556 | |||
| 557 |
2/4✓ Branch 0 taken 3233 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3233 times.
✗ Branch 3 not taken.
|
6466 | if (settings.mCheckTopologyStructure && !maskGrid) { |
| 558 | flag = firstGrid.tree().hasSameTopology(secondGrid.tree()); | ||
| 559 | result &= flag; | ||
| 560 | 6466 | os << "[Diagnostic]: Topology structures: " << Local::fts(flag) | |
| 561 | << std::endl; | ||
| 562 | } | ||
| 563 | |||
| 564 | openvdb::MaskGrid::Ptr mask = openvdb::MaskGrid::create(); | ||
| 565 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3233 times.
|
6466 | if (maskGrid) { |
| 566 | mask->topologyUnion(*maskGrid); | ||
| 567 | } | ||
| 568 | else { | ||
| 569 | mask->topologyUnion(firstGrid); | ||
| 570 | mask->topologyUnion(secondGrid); | ||
| 571 | } | ||
| 572 | |||
| 573 |
1/2✓ Branch 1 taken 3233 times.
✗ Branch 2 not taken.
|
6466 | openvdb::tools::pruneInactive(mask->tree()); |
| 574 | |||
| 575 |
1/2✓ Branch 1 taken 3233 times.
✗ Branch 2 not taken.
|
12932 | NodeManager manager(mask->constTree()); |
| 576 | 6466 | tbb::concurrent_vector<DiagnosticData::Ptr> data; | |
| 577 | |||
| 578 | CompareNodes<TreeType> | ||
| 579 |
2/4✓ Branch 1 taken 3233 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 45 times.
✗ Branch 5 not taken.
|
12932 | op(data, |
| 580 | firstGrid.constTree(), | ||
| 581 | secondGrid.constTree(), | ||
| 582 | tolerance, | ||
| 583 | settings); | ||
| 584 | |||
| 585 |
1/2✓ Branch 1 taken 3233 times.
✗ Branch 2 not taken.
|
6466 | manager.foreachBottomUp(op); |
| 586 | |||
| 587 | flag = data.empty(); | ||
| 588 | result &= flag; | ||
| 589 | 6466 | os << "[Diagnostic]: Leaf Node Comparison: " << Local::fts(flag) | |
| 590 | << std::endl; | ||
| 591 | |||
| 592 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3233 times.
|
6466 | if (flag) return result; |
| 593 | |||
| 594 | openvdb::MaskGrid& differingTopology = *(resultData.mDifferingTopology); | ||
| 595 | openvdb::MaskGrid& differingValues = *(resultData.mDifferingValues); | ||
| 596 | |||
| 597 | ✗ | differingTopology.setTransform(firstGrid.transform().copy()); | |
| 598 | ✗ | differingValues.setTransform(firstGrid.transform().copy()); | |
| 599 | ✗ | differingTopology.setName("different_topology"); | |
| 600 | ✗ | differingValues.setName("different_values"); | |
| 601 | |||
| 602 | // Print diagnostic info to the stream and intialise the result topologies | ||
| 603 | |||
| 604 | openvdb::MaskGrid::Accessor accessorTopology = differingTopology.getAccessor(); | ||
| 605 | openvdb::MaskGrid::Accessor accessorValues = differingValues.getAccessor(); | ||
| 606 | |||
| 607 | os << "[Diagnostic]: Leaf Node Diagnostics:\n" << std::endl; | ||
| 608 | |||
| 609 | ✗ | for (const auto& diag : data) { | |
| 610 | ✗ | assert(diag); | |
| 611 | ✗ | diag->report(os, firstGrid, secondGrid, accessorTopology, accessorValues); | |
| 612 | } | ||
| 613 | |||
| 614 | return result; | ||
| 615 | } | ||
| 616 | |||
| 617 | template <typename ValueT> | ||
| 618 | using ConverterT = typename openvdb::BoolGrid::ValueConverter<ValueT>::Type; | ||
| 619 | |||
| 620 | 5019 | bool compareUntypedGrids(ComparisonResult &resultData, | |
| 621 | const openvdb::GridBase &firstGrid, | ||
| 622 | const openvdb::GridBase &secondGrid, | ||
| 623 | const ComparisonSettings &settings, | ||
| 624 | const openvdb::MaskGrid::ConstPtr maskGrid) | ||
| 625 | { | ||
| 626 | 5019 | bool result = false, valid = false;; | |
| 627 | 205779 | TypeList::foreach([&](auto x) { | |
| 628 | using GridT = ConverterT<decltype(x)>; | ||
| 629 |
2/2✓ Branch 1 taken 5019 times.
✓ Branch 2 taken 95361 times.
|
200760 | if (firstGrid.isType<GridT>()) { |
| 630 | 10038 | valid = true; | |
| 631 | 10038 | const GridT& firstGridTyped = static_cast<const GridT&>(firstGrid); | |
| 632 | 10038 | const GridT& secondGridTyped = static_cast<const GridT&>(secondGrid); | |
| 633 |
2/4✓ Branch 2 taken 5019 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5019 times.
|
10038 | result = compareGrids(resultData, firstGridTyped, secondGridTyped, settings, maskGrid); |
| 634 | } | ||
| 635 | 200760 | }); | |
| 636 | |||
| 637 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5019 times.
|
5019 | if (!valid) { |
| 638 | ✗ | if (firstGrid.isType<openvdb::points::PointDataGrid>()) { | |
| 639 | ✗ | valid = true; | |
| 640 | const openvdb::points::PointDataGrid& firstGridTyped = | ||
| 641 | static_cast<const openvdb::points::PointDataGrid&>(firstGrid); | ||
| 642 | const openvdb::points::PointDataGrid& secondGridTyped = | ||
| 643 | static_cast<const openvdb::points::PointDataGrid&>(secondGrid); | ||
| 644 | ✗ | result = compareGrids(resultData, firstGridTyped, secondGridTyped, settings, maskGrid); | |
| 645 | } | ||
| 646 | } | ||
| 647 | |||
| 648 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5019 times.
|
5019 | if (!valid) { |
| 649 | ✗ | OPENVDB_THROW(openvdb::TypeError, "Unsupported grid type: " + firstGrid.valueType()); | |
| 650 | } | ||
| 651 | 5019 | return result; | |
| 652 | } | ||
| 653 | |||
| 654 | |||
| 655 | } | ||
| 656 | |||
| 657 | |||
| 658 |