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