1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #include <openvdb/openvdb.h>
5 #include <openvdb/tools/Activate.h>
6 
7 #include <gtest/gtest.h>
8 
9 // #define BENCHMARK
10 
11 
12 class TestActivate: public ::testing::Test
13 {
14 };
15 
16 
17 ////////////////////////////////////////
18 
19 
20 // migrated from TestTools::testActivate()
TEST_F(TestActivate,testActivate)21 TEST_F(TestActivate, testActivate)
22 {
23     using namespace openvdb;
24 
25     const Vec3s background(0.0, -1.0, 1.0), foreground(42.0);
26 
27     Vec3STree tree(background);
28 
29 #ifndef BENCHMARK
30     const CoordBBox bbox1(Coord(-200), Coord(-181));
31     const CoordBBox bbox2(Coord(51), Coord(373));
32 #else
33     const CoordBBox bbox1(Coord(-200*20), Coord(-181*20));
34     const CoordBBox bbox2(Coord(51*20), Coord(373*20));
35 #endif
36 
37     // Set some non-background active voxels.
38     tree.fill(bbox1, Vec3s(0.0), /*active=*/true);
39 
40     // Mark some background voxels as active.
41     tree.fill(bbox2, background, /*active=*/true);
42     EXPECT_EQ(bbox2.volume() + bbox1.volume(), tree.activeVoxelCount());
43 
44     // Deactivate all voxels with the background value.
45     tools::deactivate(tree, background, /*tolerance=*/Vec3s(1.0e-6f));
46     // Verify that there are no longer any active voxels with the background value.
47     EXPECT_EQ(bbox1.volume(), tree.activeVoxelCount());
48 
49     // Set some voxels to the foreground value but leave them inactive.
50     tree.fill(bbox2, foreground, /*active=*/false);
51     // Verify that there are no active voxels with the background value.
52     EXPECT_EQ(bbox1.volume(), tree.activeVoxelCount());
53 
54     // Activate all voxels with the foreground value.
55     tools::activate(tree, foreground);
56     // Verify that the expected number of voxels are active.
57     EXPECT_EQ(bbox1.volume() + bbox2.volume(), tree.activeVoxelCount());
58 }
59 
TEST_F(TestActivate,testActivateLeafValues)60 TEST_F(TestActivate, testActivateLeafValues)
61 {
62     using namespace openvdb;
63 
64     { // activate leaf with a single inactive voxel
65         FloatTree tree;
66         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
67         EXPECT_TRUE(leaf->isEmpty());
68 
69         // all values are 0.0f so activate with value = 1.0f is a noop
70 
71         tools::activate(tree, 1.0f);
72         EXPECT_TRUE(leaf->isEmpty());
73 
74         // set leaf[0] to 1.0f and activate this one voxel
75 
76         leaf->setValueOff(0, 1.0f);
77         tools::activate(tree, 1.0f);
78         EXPECT_EQ(leaf->onVoxelCount(), Index64(1));
79     }
80 
81     { // activate leaf with a single inactive voxel within the tolerance
82         FloatTree tree;
83         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
84 
85         // set leaf[0] to a small tolerance above 1.0f
86 
87         leaf->setValueOff(0, 1.0f + 1e-4f);
88         tools::activate(tree, 1.0f); // default tolerance is zero
89         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
90 
91         // activate using explicit tolerance
92 
93         tools::activate(tree, 1.0f, 1e-6f); // tolerance is too small
94         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
95         tools::activate(tree, 1.0f, 1e-3f); // tolerance is now large enough
96         EXPECT_EQ(leaf->onVoxelCount(), Index64(1));
97     }
98 
99     { // activate leaf with a single inactive voxel with 0.1f value
100         FloatTree tree;
101         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
102 
103         // set leaf[0] to 0.1f (which cannot be represented exactly in floating-point)
104 
105         leaf->setValueOff(0, 0.1f);
106         tools::activate(tree, 0.1f); // default tolerance is zero
107         EXPECT_EQ(leaf->onVoxelCount(), Index64(1));
108     }
109 
110     { // activate leaf with a few active and inactive voxels
111         FloatTree tree;
112         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
113 
114         leaf->setValueOff(0, 1.0f);
115         leaf->setValueOff(1, 3.0f);
116         leaf->setValueOff(2, -3.0f);
117         leaf->setValueOn(3, 1.0f);
118         leaf->setValueOn(4, 3.0f);
119         leaf->setValueOn(5, -3.0f);
120         EXPECT_EQ(leaf->onVoxelCount(), Index64(3));
121         tools::activate(tree, 1.0f);
122         EXPECT_EQ(leaf->onVoxelCount(), Index64(4));
123     }
124 
125     { // activate an integer leaf
126         Int32Tree tree;
127         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
128 
129         leaf->setValueOff(0, 10);
130         leaf->setValueOff(1, 9);
131         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
132         tools::activate(tree, 9); // default tolerance is zero
133         EXPECT_EQ(leaf->onVoxelCount(), Index64(1));
134         tools::activate(tree, 9, /*tolerance=*/2);
135         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
136     }
137 
138     { // activate a Vec3s leaf
139         Vec3STree tree;
140         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
141 
142         leaf->setValueOff(0, Vec3s(10));
143         leaf->setValueOff(1, Vec3s(2, 3, 4));
144         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
145         tools::activate(tree, Vec3s(2, 3, 5)); // default tolerance is zero
146         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
147         tools::activate(tree, Vec3s(2, 3, 5), /*tolerance=*/Vec3s(0, 0, 2));
148         EXPECT_EQ(leaf->onVoxelCount(), Index64(1));
149         tools::activate(tree, Vec3s(10), Vec3s(0.1f));
150         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
151     }
152 
153     { // activate a mask leaf
154         MaskTree tree;
155         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
156 
157         leaf->setValueOnly(0, true);
158         leaf->setValueOnly(1, true);
159         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
160         tools::activate(tree, true); // noop
161         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
162         tools::activate(tree, false); // all inactive values become active
163         EXPECT_EQ(leaf->onVoxelCount(), Index64(512));
164     }
165 }
166 
TEST_F(TestActivate,testActivateTiles)167 TEST_F(TestActivate, testActivateTiles)
168 {
169     using namespace openvdb;
170 
171     auto getActiveTiles = [](const auto& node) -> Index
172     {
173         Index count(0);
174         for (auto iter = node.cbeginValueOn(); iter; ++iter)   count++;
175         return count;
176     };
177 
178     auto getInactiveTiles = [](const auto& node) -> Index
179     {
180         Index count(0);
181         for (auto iter = node.cbeginValueOff(); iter; ++iter)   count++;
182         return count;
183     };
184 
185     { // activate a single inactive tile
186         FloatTree tree;
187         const FloatTree::RootNodeType& root = tree.root();
188 
189         // add a root node tile
190         tree.addTile(/*level=*/3, Coord(0), 1.0f, false);
191 
192         EXPECT_EQ(getInactiveTiles(root), Index(1));
193         EXPECT_EQ(getActiveTiles(root), Index(0));
194         tools::activate(tree, 1.0f);
195         EXPECT_EQ(getInactiveTiles(root), Index(0));
196         EXPECT_EQ(getActiveTiles(root), Index(1));
197     }
198 
199     { // activate a single inactive tile with tolerance
200         FloatTree tree;
201         const FloatTree::RootNodeType& root = tree.root();
202 
203         // add a root node tile
204         tree.addTile(/*level=*/3, Coord(0), 1.0f + 1e-4f, false);
205 
206         EXPECT_EQ(getInactiveTiles(root), Index(1));
207         EXPECT_EQ(getActiveTiles(root), Index(0));
208         tools::activate(tree, 1.0f);
209         EXPECT_EQ(getInactiveTiles(root), Index(1));
210         EXPECT_EQ(getActiveTiles(root), Index(0));
211         tools::activate(tree, 1.0f, 1e-6f);
212         EXPECT_EQ(getInactiveTiles(root), Index(1));
213         EXPECT_EQ(getActiveTiles(root), Index(0));
214         tools::activate(tree, 1.0f, 1e-3f);
215         EXPECT_EQ(getInactiveTiles(root), Index(0));
216         EXPECT_EQ(getActiveTiles(root), Index(1));
217     }
218 
219     { // activate a single inactive tile from an internal node
220         FloatTree tree;
221         const FloatTree::RootNodeType& root = tree.root();
222 
223         // add an internal node tile
224         tree.addTile(/*level=*/1, Coord(0), 1.0f, false);
225 
226         const auto& child = *(root.cbeginChildOn()->cbeginChildOn());
227 
228         EXPECT_EQ(getInactiveTiles(child), Index(4096));
229         EXPECT_EQ(getActiveTiles(child), Index(0));
230         tools::activate(tree, 1.0f);
231         EXPECT_EQ(getInactiveTiles(child), Index(4095));
232         EXPECT_EQ(getActiveTiles(child), Index(1));
233     }
234 
235     { // activate a single inactive tile in a Vec3s
236         Vec3STree tree;
237         const Vec3STree::RootNodeType& root = tree.root();
238 
239         // add a root node tile
240         tree.addTile(/*level=*/3, Coord(0), Vec3s(1), false);
241 
242         EXPECT_EQ(getInactiveTiles(root), Index(1));
243         EXPECT_EQ(getActiveTiles(root), Index(0));
244         tools::activate(tree, Vec3s(1));
245         EXPECT_EQ(getInactiveTiles(root), Index(0));
246         EXPECT_EQ(getActiveTiles(root), Index(1));
247     }
248 }
249 
TEST_F(TestActivate,testDeactivateLeafValues)250 TEST_F(TestActivate, testDeactivateLeafValues)
251 {
252     using namespace openvdb;
253 
254     { // deactivate leaf with a single active voxel
255         FloatTree tree;
256         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
257         EXPECT_TRUE(leaf->isEmpty());
258 
259         // all values are 0.0f so deactivate with value = 1.0f is a noop
260 
261         tools::deactivate(tree, 1.0f);
262         EXPECT_TRUE(leaf->isEmpty());
263 
264         // set leaf[0] to 1.0f and deactivate this one voxel
265 
266         leaf->setValueOn(0, 1.0f);
267         tools::deactivate(tree, 1.0f);
268         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
269     }
270 
271     { // deactivate leaf with a single active voxel within the tolerance
272         FloatTree tree;
273         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
274 
275         // set leaf[0] to a small tolerance above 1.0f
276 
277         leaf->setValueOn(0, 1.0f + 1e-4f);
278         tools::deactivate(tree, 1.0f); // default tolerance is zero
279         EXPECT_EQ(leaf->onVoxelCount(), Index64(1));
280 
281         // deactivate using explicit tolerance
282 
283         tools::deactivate(tree, 1.0f, 1e-6f); // tolerance is too small
284         EXPECT_EQ(leaf->onVoxelCount(), Index64(1));
285         tools::deactivate(tree, 1.0f, 1e-3f); // tolerance is now large enough
286         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
287     }
288 
289     { // deactivate leaf with a single active voxel with 0.1f value
290         FloatTree tree;
291         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
292 
293         // set leaf[0] to 0.1f (which cannot be represented exactly in floating-point)
294 
295         leaf->setValueOn(0, 0.1f);
296         tools::deactivate(tree, 0.1f); // default tolerance is zero
297         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
298     }
299 
300     { // deactivate leaf with a few active and inactive voxels
301         FloatTree tree;
302         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
303 
304         leaf->setValueOff(0, 1.0f);
305         leaf->setValueOff(1, 3.0f);
306         leaf->setValueOff(2, -3.0f);
307         leaf->setValueOn(3, 1.0f);
308         leaf->setValueOn(4, 3.0f);
309         leaf->setValueOn(5, -3.0f);
310         EXPECT_EQ(leaf->onVoxelCount(), Index64(3));
311         tools::deactivate(tree, 1.0f);
312         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
313     }
314 
315     { // deactivate an integer leaf
316         Int32Tree tree;
317         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
318 
319         leaf->setValueOn(0, 10);
320         leaf->setValueOn(1, 9);
321         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
322         tools::deactivate(tree, 9); // default tolerance is zero
323         EXPECT_EQ(leaf->onVoxelCount(), Index64(1));
324         tools::deactivate(tree, 9, /*tolerance=*/2);
325         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
326     }
327 
328     { // deactivate a Vec3s leaf
329         Vec3STree tree;
330         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
331 
332         leaf->setValueOn(0, Vec3s(10));
333         leaf->setValueOn(1, Vec3s(2, 3, 4));
334         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
335         tools::deactivate(tree, Vec3s(2, 3, 5)); // default tolerance is zero
336         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
337         tools::deactivate(tree, Vec3s(2, 3, 5), /*tolerance=*/Vec3s(0, 0, 2));
338         EXPECT_EQ(leaf->onVoxelCount(), Index64(1));
339         tools::deactivate(tree, Vec3s(10), Vec3s(0.1f));
340         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
341     }
342 
343     { // deactivate a mask leaf
344         MaskTree tree;
345         auto* leaf = tree.touchLeaf(openvdb::Coord(0));
346 
347         leaf->setValueOnly(0, true);
348         leaf->setValueOnly(1, true);
349         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
350         tools::deactivate(tree, false); // noop
351         EXPECT_EQ(leaf->onVoxelCount(), Index64(2));
352         tools::deactivate(tree, true); // all active values become inactive
353         EXPECT_EQ(leaf->onVoxelCount(), Index64(0));
354     }
355 }
356 
TEST_F(TestActivate,testDeactivateTiles)357 TEST_F(TestActivate, testDeactivateTiles)
358 {
359     using namespace openvdb;
360 
361     auto getActiveTiles = [](const auto& node) -> Index
362     {
363         Index count(0);
364         for (auto iter = node.cbeginValueOn(); iter; ++iter)   count++;
365         return count;
366     };
367 
368     auto getInactiveTiles = [](const auto& node) -> Index
369     {
370         Index count(0);
371         for (auto iter = node.cbeginValueOff(); iter; ++iter)   count++;
372         return count;
373     };
374 
375     { // deactivate a single active tile
376         FloatTree tree;
377         const FloatTree::RootNodeType& root = tree.root();
378 
379         // add a root node tile
380         tree.addTile(/*level=*/3, Coord(0), 1.0f, true);
381 
382         EXPECT_EQ(getInactiveTiles(root), Index(0));
383         EXPECT_EQ(getActiveTiles(root), Index(1));
384         tools::deactivate(tree, 1.0f);
385         EXPECT_EQ(getInactiveTiles(root), Index(1));
386         EXPECT_EQ(getActiveTiles(root), Index(0));
387     }
388 
389     { // deactivate a single active tile with tolerance
390         FloatTree tree;
391         const FloatTree::RootNodeType& root = tree.root();
392 
393         // add a root node tile
394         tree.addTile(/*level=*/3, Coord(0), 1.0f + 1e-4f, true);
395 
396         EXPECT_EQ(getInactiveTiles(root), Index(0));
397         EXPECT_EQ(getActiveTiles(root), Index(1));
398         tools::deactivate(tree, 1.0f);
399         EXPECT_EQ(getInactiveTiles(root), Index(0));
400         EXPECT_EQ(getActiveTiles(root), Index(1));
401         tools::deactivate(tree, 1.0f, 1e-6f);
402         EXPECT_EQ(getInactiveTiles(root), Index(0));
403         EXPECT_EQ(getActiveTiles(root), Index(1));
404         tools::deactivate(tree, 1.0f, 1e-3f);
405         EXPECT_EQ(getInactiveTiles(root), Index(1));
406         EXPECT_EQ(getActiveTiles(root), Index(0));
407     }
408 
409     { // deactivate a single active tile from an internal node
410         FloatTree tree;
411         const FloatTree::RootNodeType& root = tree.root();
412 
413         // add an internal node tile
414         tree.addTile(/*level=*/1, Coord(0), 1.0f, true);
415 
416         const auto& child = *(root.cbeginChildOn()->cbeginChildOn());
417 
418         EXPECT_EQ(getInactiveTiles(child), Index(4095));
419         EXPECT_EQ(getActiveTiles(child), Index(1));
420         tools::deactivate(tree, 1.0f);
421         EXPECT_EQ(getInactiveTiles(child), Index(4096));
422         EXPECT_EQ(getActiveTiles(child), Index(0));
423     }
424 
425     { // deactivate a single active tile in a Vec3s
426         Vec3STree tree;
427         const Vec3STree::RootNodeType& root = tree.root();
428 
429         // add a root node tile
430         tree.addTile(/*level=*/3, Coord(0), Vec3s(1), true);
431 
432         EXPECT_EQ(getInactiveTiles(root), Index(0));
433         EXPECT_EQ(getActiveTiles(root), Index(1));
434         tools::deactivate(tree, Vec3s(1));
435         EXPECT_EQ(getInactiveTiles(root), Index(1));
436         EXPECT_EQ(getActiveTiles(root), Index(0));
437     }
438 }
439