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 |