1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 #include <openvdb/Exceptions.h>
5 #include <openvdb/openvdb.h>
6 #include <openvdb/Types.h>
7 #include <openvdb/util/Name.h>
8 #include <openvdb/math/Transform.h>
9 #include <openvdb/Grid.h>
10 #include <openvdb/tree/Tree.h>
11 #include <openvdb/util/CpuTimer.h>
12 #include "gtest/gtest.h"
13 #include <iostream>
14 #include <memory> // for std::make_unique
15
16 #define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \
17 EXPECT_NEAR((expected), (actual), /*tolerance=*/0.0);
18
19 class TestGrid: public ::testing::Test
20 {
21 };
22
23
24 ////////////////////////////////////////
25
26
27 class ProxyTree: public openvdb::TreeBase
28 {
29 public:
30 using ValueType = int;
31 using BuildType = int;
32 using LeafNodeType = void;
33 using ValueAllCIter = void;
34 using ValueAllIter = void;
35 using ValueOffCIter = void;
36 using ValueOffIter = void;
37 using ValueOnCIter = void;
38 using ValueOnIter = void;
39 using TreeBasePtr = openvdb::TreeBase::Ptr;
40 using Ptr = openvdb::SharedPtr<ProxyTree>;
41 using ConstPtr = openvdb::SharedPtr<const ProxyTree>;
42
43 static const openvdb::Index DEPTH;
44 static const ValueType backg;
45
ProxyTree()46 ProxyTree() {}
ProxyTree(const ValueType &)47 ProxyTree(const ValueType&) {}
48 ProxyTree(const ProxyTree&) = default;
49 ~ProxyTree() override = default;
50
treeType()51 static const openvdb::Name& treeType() { static const openvdb::Name s("proxy"); return s; }
type() const52 const openvdb::Name& type() const override { return treeType(); }
valueType() const53 openvdb::Name valueType() const override { return "proxy"; }
background() const54 const ValueType& background() const { return backg; }
55
copy() const56 TreeBasePtr copy() const override { return TreeBasePtr(new ProxyTree(*this)); }
57
readTopology(std::istream & is,bool=false)58 void readTopology(std::istream& is, bool = false) override { is.seekg(0, std::ios::beg); }
writeTopology(std::ostream & os,bool=false) const59 void writeTopology(std::ostream& os, bool = false) const override { os.seekp(0); }
60
readBuffers(std::istream & is,const openvdb::CoordBBox &,bool=false)61 void readBuffers(std::istream& is,
62 const openvdb::CoordBBox&, bool /*saveFloatAsHalf*/=false) override { is.seekg(0); }
readNonresidentBuffers() const63 void readNonresidentBuffers() const override {}
readBuffers(std::istream & is,bool=false)64 void readBuffers(std::istream& is, bool /*saveFloatAsHalf*/=false) override { is.seekg(0); }
writeBuffers(std::ostream & os,bool=false) const65 void writeBuffers(std::ostream& os, bool /*saveFloatAsHalf*/=false) const override
66 { os.seekp(0, std::ios::beg); }
67
empty() const68 bool empty() const { return true; }
clear()69 void clear() {}
prune(const ValueType &=0)70 void prune(const ValueType& = 0) {}
clip(const openvdb::CoordBBox &)71 void clip(const openvdb::CoordBBox&) {}
clipUnallocatedNodes()72 void clipUnallocatedNodes() override {}
unallocatedLeafCount() const73 openvdb::Index32 unallocatedLeafCount() const override { return 0; }
74
getIndexRange(openvdb::CoordBBox &) const75 void getIndexRange(openvdb::CoordBBox&) const override {}
evalLeafBoundingBox(openvdb::CoordBBox & bbox) const76 bool evalLeafBoundingBox(openvdb::CoordBBox& bbox) const override
77 { bbox.min() = bbox.max() = openvdb::Coord(0, 0, 0); return false; }
evalActiveVoxelBoundingBox(openvdb::CoordBBox & bbox) const78 bool evalActiveVoxelBoundingBox(openvdb::CoordBBox& bbox) const override
79 { bbox.min() = bbox.max() = openvdb::Coord(0, 0, 0); return false; }
evalActiveVoxelDim(openvdb::Coord & dim) const80 bool evalActiveVoxelDim(openvdb::Coord& dim) const override
81 { dim = openvdb::Coord(0, 0, 0); return false; }
evalLeafDim(openvdb::Coord & dim) const82 bool evalLeafDim(openvdb::Coord& dim) const override
83 { dim = openvdb::Coord(0, 0, 0); return false; }
84
treeDepth() const85 openvdb::Index treeDepth() const override { return 0; }
leafCount() const86 openvdb::Index leafCount() const override { return 0; }
87 #if OPENVDB_ABI_VERSION_NUMBER >= 7
nodeCount() const88 std::vector<openvdb::Index32> nodeCount() const override
89 { return std::vector<openvdb::Index32>(DEPTH, 0); }
90 #endif
nonLeafCount() const91 openvdb::Index nonLeafCount() const override { return 0; }
activeVoxelCount() const92 openvdb::Index64 activeVoxelCount() const override { return 0UL; }
inactiveVoxelCount() const93 openvdb::Index64 inactiveVoxelCount() const override { return 0UL; }
activeLeafVoxelCount() const94 openvdb::Index64 activeLeafVoxelCount() const override { return 0UL; }
inactiveLeafVoxelCount() const95 openvdb::Index64 inactiveLeafVoxelCount() const override { return 0UL; }
activeTileCount() const96 openvdb::Index64 activeTileCount() const override { return 0UL; }
97 };
98
99 const openvdb::Index ProxyTree::DEPTH = 0;
100 const ProxyTree::ValueType ProxyTree::backg = 0;
101
102 using ProxyGrid = openvdb::Grid<ProxyTree>;
103
104
105 ////////////////////////////////////////
106
TEST_F(TestGrid,testGridRegistry)107 TEST_F(TestGrid, testGridRegistry)
108 {
109 using namespace openvdb::tree;
110
111 using TreeType = Tree<RootNode<InternalNode<LeafNode<float, 3>, 2> > >;
112 using GridType = openvdb::Grid<TreeType>;
113
114 openvdb::GridBase::clearRegistry();
115
116 EXPECT_TRUE(!GridType::isRegistered());
117 GridType::registerGrid();
118 EXPECT_TRUE(GridType::isRegistered());
119 EXPECT_THROW(GridType::registerGrid(), openvdb::KeyError);
120 GridType::unregisterGrid();
121 EXPECT_TRUE(!GridType::isRegistered());
122 EXPECT_NO_THROW(GridType::unregisterGrid());
123 EXPECT_TRUE(!GridType::isRegistered());
124 EXPECT_NO_THROW(GridType::registerGrid());
125 EXPECT_TRUE(GridType::isRegistered());
126
127 openvdb::GridBase::clearRegistry();
128 }
129
130
TEST_F(TestGrid,testConstPtr)131 TEST_F(TestGrid, testConstPtr)
132 {
133 using namespace openvdb;
134
135 GridBase::ConstPtr constgrid = ProxyGrid::create();
136
137 EXPECT_EQ(Name("proxy"), constgrid->type());
138 }
139
140
TEST_F(TestGrid,testGetGrid)141 TEST_F(TestGrid, testGetGrid)
142 {
143 using namespace openvdb;
144
145 GridBase::Ptr grid = FloatGrid::create(/*bg=*/0.0);
146 GridBase::ConstPtr constGrid = grid;
147
148 EXPECT_TRUE(grid->baseTreePtr());
149
150 EXPECT_TRUE(!gridPtrCast<DoubleGrid>(grid));
151 EXPECT_TRUE(!gridPtrCast<DoubleGrid>(grid));
152
153 EXPECT_TRUE(gridConstPtrCast<FloatGrid>(constGrid));
154 EXPECT_TRUE(!gridConstPtrCast<DoubleGrid>(constGrid));
155 }
156
157
TEST_F(TestGrid,testIsType)158 TEST_F(TestGrid, testIsType)
159 {
160 using namespace openvdb;
161
162 GridBase::Ptr grid = FloatGrid::create();
163 EXPECT_TRUE(grid->isType<FloatGrid>());
164 EXPECT_TRUE(!grid->isType<DoubleGrid>());
165 }
166
167
TEST_F(TestGrid,testIsTreeUnique)168 TEST_F(TestGrid, testIsTreeUnique)
169 {
170 using namespace openvdb;
171
172 FloatGrid::Ptr grid = FloatGrid::create();
173 EXPECT_TRUE(grid->isTreeUnique());
174
175 // a shallow copy shares the same tree
176 FloatGrid::Ptr grid2 = grid->copy();
177 EXPECT_TRUE(!grid->isTreeUnique());
178 EXPECT_TRUE(!grid2->isTreeUnique());
179
180 // cleanup the shallow copy
181 grid2.reset();
182 EXPECT_TRUE(grid->isTreeUnique());
183
184 // copy with new tree
185 GridBase::Ptr grid3 = grid->copyGridWithNewTree();
186 EXPECT_TRUE(grid->isTreeUnique());
187
188 #if OPENVDB_ABI_VERSION_NUMBER >= 8
189 // shallow copy using GridBase
190 GridBase::Ptr grid4 = grid->copyGrid();
191 EXPECT_TRUE(!grid4->isTreeUnique());
192
193 // copy with new tree using GridBase
194 GridBase::Ptr grid5 = grid->copyGridWithNewTree();
195 EXPECT_TRUE(grid5->isTreeUnique());
196 #endif
197 }
198
199
TEST_F(TestGrid,testTransform)200 TEST_F(TestGrid, testTransform)
201 {
202 ProxyGrid grid;
203
204 // Verify that the grid has a valid default transform.
205 EXPECT_TRUE(grid.transformPtr());
206
207 // Verify that a null transform pointer is not allowed.
208 EXPECT_THROW(grid.setTransform(openvdb::math::Transform::Ptr()),
209 openvdb::ValueError);
210
211 grid.setTransform(openvdb::math::Transform::createLinearTransform());
212
213 EXPECT_TRUE(grid.transformPtr());
214
215 // Verify that calling Transform-related Grid methods (Grid::voxelSize(), etc.)
216 // is the same as calling those methods on the Transform.
217
218 EXPECT_TRUE(grid.transform().voxelSize().eq(grid.voxelSize()));
219 EXPECT_TRUE(grid.transform().voxelSize(openvdb::Vec3d(0.1, 0.2, 0.3)).eq(
220 grid.voxelSize(openvdb::Vec3d(0.1, 0.2, 0.3))));
221
222 EXPECT_TRUE(grid.transform().indexToWorld(openvdb::Vec3d(0.1, 0.2, 0.3)).eq(
223 grid.indexToWorld(openvdb::Vec3d(0.1, 0.2, 0.3))));
224 EXPECT_TRUE(grid.transform().indexToWorld(openvdb::Coord(1, 2, 3)).eq(
225 grid.indexToWorld(openvdb::Coord(1, 2, 3))));
226 EXPECT_TRUE(grid.transform().worldToIndex(openvdb::Vec3d(0.1, 0.2, 0.3)).eq(
227 grid.worldToIndex(openvdb::Vec3d(0.1, 0.2, 0.3))));
228 }
229
230
TEST_F(TestGrid,testCopyGrid)231 TEST_F(TestGrid, testCopyGrid)
232 {
233 using namespace openvdb;
234
235 // set up a grid
236 const float fillValue1=5.0f;
237 FloatGrid::Ptr grid1 = createGrid<FloatGrid>(/*bg=*/fillValue1);
238 FloatTree& tree1 = grid1->tree();
239 tree1.setValue(Coord(-10,40,845), 3.456f);
240 tree1.setValue(Coord(1,-50,-8), 1.0f);
241
242 // create a new grid, copying the first grid
243 GridBase::Ptr grid2 = grid1->deepCopy();
244
245 // cast down to the concrete type to query values
246 FloatTree& tree2 = gridPtrCast<FloatGrid>(grid2)->tree();
247
248 // compare topology
249 EXPECT_TRUE(tree1.hasSameTopology(tree2));
250 EXPECT_TRUE(tree2.hasSameTopology(tree1));
251
252 // trees should be equal
253 ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue1, tree2.getValue(Coord(1,2,3)));
254 ASSERT_DOUBLES_EXACTLY_EQUAL(3.456f, tree2.getValue(Coord(-10,40,845)));
255 ASSERT_DOUBLES_EXACTLY_EQUAL(1.0f, tree2.getValue(Coord(1,-50,-8)));
256
257 // change 1 value in tree2
258 Coord changeCoord(1, -500, -8);
259 tree2.setValue(changeCoord, 1.0f);
260
261 // topology should no longer match
262 EXPECT_TRUE(!tree1.hasSameTopology(tree2));
263 EXPECT_TRUE(!tree2.hasSameTopology(tree1));
264
265 // query changed value and make sure it's different between trees
266 ASSERT_DOUBLES_EXACTLY_EQUAL(fillValue1, tree1.getValue(changeCoord));
267 ASSERT_DOUBLES_EXACTLY_EQUAL(1.0f, tree2.getValue(changeCoord));
268
269 #if OPENVDB_ABI_VERSION_NUMBER >= 7
270 // shallow-copy a const grid but supply a new transform and meta map
271 EXPECT_EQ(1.0, grid1->transform().voxelSize().x());
272 EXPECT_EQ(size_t(0), grid1->metaCount());
273 EXPECT_EQ(Index(2), grid1->tree().leafCount());
274
275 math::Transform::Ptr xform(math::Transform::createLinearTransform(/*voxelSize=*/0.25));
276 MetaMap meta;
277 meta.insertMeta("test", Int32Metadata(4));
278
279 FloatGrid::ConstPtr constGrid1 = ConstPtrCast<const FloatGrid>(grid1);
280
281 GridBase::ConstPtr grid3 = constGrid1->copyGridReplacingMetadataAndTransform(meta, xform);
282 const FloatTree& tree3 = gridConstPtrCast<FloatGrid>(grid3)->tree();
283
284 EXPECT_EQ(0.25, grid3->transform().voxelSize().x());
285 EXPECT_EQ(size_t(1), grid3->metaCount());
286 EXPECT_EQ(Index(2), tree3.leafCount());
287 EXPECT_EQ(long(3), constGrid1->constTreePtr().use_count());
288 #endif
289 }
290
291
TEST_F(TestGrid,testValueConversion)292 TEST_F(TestGrid, testValueConversion)
293 {
294 using namespace openvdb;
295
296 const Coord c0(-10, 40, 845), c1(1, -50, -8), c2(1, 2, 3);
297 const float fval0 = 3.25f, fval1 = 1.0f, fbkgd = 5.0f;
298
299 // Create a FloatGrid.
300 FloatGrid fgrid(fbkgd);
301 FloatTree& ftree = fgrid.tree();
302 ftree.setValue(c0, fval0);
303 ftree.setValue(c1, fval1);
304
305 // Copy the FloatGrid to a DoubleGrid.
306 DoubleGrid dgrid(fgrid);
307 DoubleTree& dtree = dgrid.tree();
308 // Compare topology.
309 EXPECT_TRUE(dtree.hasSameTopology(ftree));
310 EXPECT_TRUE(ftree.hasSameTopology(dtree));
311 // Compare values.
312 ASSERT_DOUBLES_EXACTLY_EQUAL(double(fbkgd), dtree.getValue(c2));
313 ASSERT_DOUBLES_EXACTLY_EQUAL(double(fval0), dtree.getValue(c0));
314 ASSERT_DOUBLES_EXACTLY_EQUAL(double(fval1), dtree.getValue(c1));
315
316 // Copy the FloatGrid to a BoolGrid.
317 BoolGrid bgrid(fgrid);
318 BoolTree& btree = bgrid.tree();
319 // Compare topology.
320 EXPECT_TRUE(btree.hasSameTopology(ftree));
321 EXPECT_TRUE(ftree.hasSameTopology(btree));
322 // Compare values.
323 EXPECT_EQ(bool(fbkgd), btree.getValue(c2));
324 EXPECT_EQ(bool(fval0), btree.getValue(c0));
325 EXPECT_EQ(bool(fval1), btree.getValue(c1));
326
327 // Copy the FloatGrid to a Vec3SGrid.
328 Vec3SGrid vgrid(fgrid);
329 Vec3STree& vtree = vgrid.tree();
330 // Compare topology.
331 EXPECT_TRUE(vtree.hasSameTopology(ftree));
332 EXPECT_TRUE(ftree.hasSameTopology(vtree));
333 // Compare values.
334 EXPECT_EQ(Vec3s(fbkgd), vtree.getValue(c2));
335 EXPECT_EQ(Vec3s(fval0), vtree.getValue(c0));
336 EXPECT_EQ(Vec3s(fval1), vtree.getValue(c1));
337
338 // Verify that a Vec3SGrid can't be copied to an Int32Grid
339 // (because an Int32 can't be constructed from a Vec3S).
340 EXPECT_THROW(Int32Grid igrid2(vgrid), openvdb::TypeError);
341
342 // Verify that a grid can't be converted to another type with a different
343 // tree configuration.
344 using DTree23 = tree::Tree3<double, 2, 3>::Type;
345 using DGrid23 = Grid<DTree23>;
346 EXPECT_THROW(DGrid23 d23grid(fgrid), openvdb::TypeError);
347 }
348
349
350 ////////////////////////////////////////
351
352
353 template<typename GridT>
354 void
validateClippedGrid(const GridT & clipped,const typename GridT::ValueType & fg)355 validateClippedGrid(const GridT& clipped, const typename GridT::ValueType& fg)
356 {
357 using namespace openvdb;
358
359 using ValueT = typename GridT::ValueType;
360
361 const CoordBBox bbox = clipped.evalActiveVoxelBoundingBox();
362 EXPECT_EQ(4, bbox.min().x());
363 EXPECT_EQ(4, bbox.min().y());
364 EXPECT_EQ(-6, bbox.min().z());
365 EXPECT_EQ(4, bbox.max().x());
366 EXPECT_EQ(4, bbox.max().y());
367 EXPECT_EQ(6, bbox.max().z());
368 EXPECT_EQ(6 + 6 + 1, int(clipped.activeVoxelCount()));
369 EXPECT_EQ(2, int(clipped.constTree().leafCount()));
370
371 typename GridT::ConstAccessor acc = clipped.getConstAccessor();
372 const ValueT bg = clipped.background();
373 Coord xyz;
374 int &x = xyz[0], &y = xyz[1], &z = xyz[2];
375 for (x = -10; x <= 10; ++x) {
376 for (y = -10; y <= 10; ++y) {
377 for (z = -10; z <= 10; ++z) {
378 if (x == 4 && y == 4 && z >= -6 && z <= 6) {
379 EXPECT_EQ(fg, acc.getValue(Coord(4, 4, z)));
380 } else {
381 EXPECT_EQ(bg, acc.getValue(Coord(x, y, z)));
382 }
383 }
384 }
385 }
386 }
387
388
389 // See also TestTools::testClipping()
TEST_F(TestGrid,testClipping)390 TEST_F(TestGrid, testClipping)
391 {
392 using namespace openvdb;
393
394 const BBoxd clipBox(Vec3d(4.0, 4.0, -6.0), Vec3d(4.9, 4.9, 6.0));
395
396 {
397 const float fg = 5.f;
398 FloatGrid cube(0.f);
399 cube.fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/fg, /*active=*/true);
400 cube.clipGrid(clipBox);
401 validateClippedGrid(cube, fg);
402 }
403 {
404 const bool fg = true;
405 BoolGrid cube(false);
406 cube.fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/fg, /*active=*/true);
407 cube.clipGrid(clipBox);
408 validateClippedGrid(cube, fg);
409 }
410 {
411 const Vec3s fg(1.f, -2.f, 3.f);
412 Vec3SGrid cube(Vec3s(0.f));
413 cube.fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/fg, /*active=*/true);
414 cube.clipGrid(clipBox);
415 validateClippedGrid(cube, fg);
416 }
417 /*
418 {// Benchmark multi-threaded copy construction
419 openvdb::util::CpuTimer timer;
420 openvdb::initialize();
421 openvdb::io::File file("/usr/pic1/Data/OpenVDB/LevelSetModels/crawler.vdb");
422 file.open();
423 openvdb::GridBase::Ptr baseGrid = file.readGrid("ls_crawler");
424 file.close();
425 openvdb::FloatGrid::Ptr grid = openvdb::gridPtrCast<openvdb::FloatGrid>(baseGrid);
426 //grid->tree().print();
427 timer.start("\nCopy construction");
428 openvdb::FloatTree fTree(grid->tree());
429 timer.stop();
430
431 timer.start("\nBoolean topology copy construction");
432 openvdb::BoolTree bTree(grid->tree(), false, openvdb::TopologyCopy());
433 timer.stop();
434
435 timer.start("\nBoolean topology union");
436 bTree.topologyUnion(fTree);
437 timer.stop();
438 //bTree.print();
439 }
440 */
441 }
442
443
444 ////////////////////////////////////////
445
446
447 namespace {
448
449 struct GridOp
450 {
451 bool isConst = false;
operator ()__anon7337ad640111::GridOp452 template<typename GridT> void operator()(const GridT&) { isConst = true; }
operator ()__anon7337ad640111::GridOp453 template<typename GridT> void operator()(GridT&) { isConst = false; }
454 };
455
456 } // anonymous namespace
457
458
TEST_F(TestGrid,testApply)459 TEST_F(TestGrid, testApply)
460 {
461 using namespace openvdb;
462
463 const GridBase::Ptr
464 boolGrid = BoolGrid::create(),
465 floatGrid = FloatGrid::create(),
466 doubleGrid = DoubleGrid::create(),
467 intGrid = Int32Grid::create();
468
469 const GridBase::ConstPtr
470 boolCGrid = BoolGrid::create(),
471 floatCGrid = FloatGrid::create(),
472 doubleCGrid = DoubleGrid::create(),
473 intCGrid = Int32Grid::create();
474
475 {
476 using AllowedGridTypes = TypeList<>;
477
478 // Verify that the functor is not applied to any of the grids.
479 GridOp op;
480 EXPECT_TRUE(!boolGrid->apply<AllowedGridTypes>(op));
481 EXPECT_TRUE(!boolCGrid->apply<AllowedGridTypes>(op));
482 EXPECT_TRUE(!floatGrid->apply<AllowedGridTypes>(op));
483 EXPECT_TRUE(!floatCGrid->apply<AllowedGridTypes>(op));
484 EXPECT_TRUE(!doubleGrid->apply<AllowedGridTypes>(op));
485 EXPECT_TRUE(!doubleCGrid->apply<AllowedGridTypes>(op));
486 EXPECT_TRUE(!intGrid->apply<AllowedGridTypes>(op));
487 EXPECT_TRUE(!intCGrid->apply<AllowedGridTypes>(op));
488 }
489 {
490 using AllowedGridTypes = TypeList<FloatGrid, FloatGrid, DoubleGrid>;
491
492 // Verify that the functor is applied only to grids of the allowed types
493 // and that their constness is respected.
494 GridOp op;
495 EXPECT_TRUE(!boolGrid->apply<AllowedGridTypes>(op));
496 EXPECT_TRUE(!intGrid->apply<AllowedGridTypes>(op));
497 EXPECT_TRUE(floatGrid->apply<AllowedGridTypes>(op)); EXPECT_TRUE(!op.isConst);
498 EXPECT_TRUE(doubleGrid->apply<AllowedGridTypes>(op)); EXPECT_TRUE(!op.isConst);
499
500 EXPECT_TRUE(!boolCGrid->apply<AllowedGridTypes>(op));
501 EXPECT_TRUE(!intCGrid->apply<AllowedGridTypes>(op));
502 EXPECT_TRUE(floatCGrid->apply<AllowedGridTypes>(op)); EXPECT_TRUE(op.isConst);
503 EXPECT_TRUE(doubleCGrid->apply<AllowedGridTypes>(op)); EXPECT_TRUE(op.isConst);
504 }
505 {
506 using AllowedGridTypes = TypeList<FloatGrid, DoubleGrid>;
507
508 // Verify that rvalue functors are supported.
509 int n = 0;
510 EXPECT_TRUE( !boolGrid->apply<AllowedGridTypes>([&n](GridBase&) { ++n; }));
511 EXPECT_TRUE( !intGrid->apply<AllowedGridTypes>([&n](GridBase&) { ++n; }));
512 EXPECT_TRUE( floatGrid->apply<AllowedGridTypes>([&n](GridBase&) { ++n; }));
513 EXPECT_TRUE( doubleGrid->apply<AllowedGridTypes>([&n](GridBase&) { ++n; }));
514 EXPECT_TRUE( !boolCGrid->apply<AllowedGridTypes>([&n](const GridBase&) { ++n; }));
515 EXPECT_TRUE( !intCGrid->apply<AllowedGridTypes>([&n](const GridBase&) { ++n; }));
516 EXPECT_TRUE( floatCGrid->apply<AllowedGridTypes>([&n](const GridBase&) { ++n; }));
517 EXPECT_TRUE(doubleCGrid->apply<AllowedGridTypes>([&n](const GridBase&) { ++n; }));
518 EXPECT_EQ(4, n);
519 }
520 }
521