test: cover testSelection

This commit is contained in:
dualfroz committed 2026-09-13 12:39:08 +02:00
1 parent b6fe9d78eb
commit 83f9ea128d
1 file changed
+165
+165
View File
@@ -155,3 +155,168 @@ VF_TEST(dilationGrowsOneCellIntoASixNeighbourCrossAndKeepsGoing) {
CHECK(!once.contains({1, 1, 0})); CHECK(!once.contains({1, 1, 0}));
CHECK(once.min() == Vec3i(-1, -1, -1)); CHECK(once.min() == Vec3i(-1, -1, -1));
// Manhattan ball of radius two: 1 + 6 + 18 cells.
Selection twice = seed.expanded(2);
CHECK_EQ(twice.count(), 25u);
CHECK(twice.contains({1, 1, 0}));
CHECK(twice.contains({2, 0, 0}));
CHECK(!twice.contains({1, 1, 1}));
}
VF_TEST(erosionPeelsExactlyOneShellOffASolidBoxPerStep) {
Selection box = Selection::box({0, 0, 0}, {4, 4, 4});
CHECK_EQ(box.count(), 125u);
Selection once = box.contracted(1);
CHECK_EQ(once.count(), 27u);
CHECK(once.contains({2, 2, 2}));
CHECK(!once.contains({0, 2, 2}));
Selection twice = box.contracted(2);
CHECK_EQ(twice.count(), 1u);
CHECK(twice.contains({2, 2, 2}));
Selection gone = box.contracted(3);
CHECK(gone.empty());
}
VF_TEST(expandingThenContractingASolidBoxReturnsTheSameCells) {
Selection box = Selection::box({-2, 7, 3}, {5, 9, 8});
Selection roundTrip = box.expanded(2).contracted(2);
CHECK_EQ(roundTrip.count(), box.count());
CHECK(roundTrip.mask().sameCells(box.mask()));
}
VF_TEST(outlineKeepsOnlyCellsWithANeighbourOutsideTheSelection) {
Selection box = Selection::box({0, 0, 0}, {4, 4, 4});
Selection shell = box.outline();
CHECK_EQ(shell.count(), 125u - 27u);
CHECK(shell.contains({0, 0, 0}));
CHECK(shell.contains({2, 4, 2}));
CHECK(!shell.contains({2, 2, 2}));
Selection single = Selection::box({9, 9, 9}, {9, 9, 9});
CHECK_EQ(single.outline().count(), 1u);
}
VF_TEST(shiftingASelectionMovesEveryCellAndKeepsItsShape) {
Selection sphere = Selection::sphere({0, 0, 0}, 3);
Selection moved = sphere.shifted({100, -50, 7});
CHECK_EQ(moved.count(), sphere.count());
CHECK(moved.min() == Vec3i(97, -53, 4));
sphere.forEach([&](const Vec3i& cell) {
CHECK(moved.contains({cell.x + 100, cell.y - 50, cell.z + 7}));
});
}
VF_TEST(magicWandFollowsConnectedCellsOfTheSameBlockOnly) {
World world;
for (int z = 0; z < 3; ++z)
for (int x = 0; x < 3; ++x) world.setBlock(x, 0, z, Block::Stone);
world.setBlock(1, 0, 1, Block::Dirt);
world.setBlock(10, 0, 10, Block::Stone);
Selection wand = Selection::magicWand(world, {0, 0, 0}, 1000u);
CHECK(wand.shape() == SelectionShape::Wand);
CHECK_EQ(wand.count(), 8u);
CHECK(!wand.truncated());
CHECK(!wand.contains({1, 0, 1}));
CHECK(!wand.contains({10, 0, 10}));
CHECK(wand.contains({2, 0, 2}));
Selection islandWand = Selection::magicWand(world, {10, 0, 10}, 1000u);
CHECK_EQ(islandWand.count(), 1u);
}
VF_TEST(magicWandStopsAtTheCellLimitAndSaysSo) {
World world = slabWorld(40, 40, 0, Block::Stone);
Selection capped = Selection::magicWand(world, {0, 0, 0}, 50u);
CHECK_EQ(capped.count(), 50u);
CHECK(capped.truncated());
Selection whole = Selection::magicWand(world, {0, 0, 0}, 40u * 40u);
CHECK_EQ(whole.count(), 1600u);
CHECK(!whole.truncated());
}
VF_TEST(magicWandStaysInsideTheBoundingLimitItWasGiven) {
World world = slabWorld(20, 20, 0, Block::Stone);
Selection boxed =
Selection::magicWandWithin(world, {5, 0, 5}, 10000u, {2, 0, 2}, {6, 0, 6});
CHECK_EQ(boxed.count(), 25u);
CHECK(boxed.min() == Vec3i(2, 0, 2));
CHECK(boxed.max() == Vec3i(6, 0, 6));
CHECK(!boxed.contains({7, 0, 5}));
CHECK_EQ(cellsInsideBox(boxed, {2, 0, 2}, {6, 0, 6}), boxed.count());
}
VF_TEST(selectionStatsReportCountsDimensionsAndABlockHistogram) {
World world;
for (int z = 0; z < 4; ++z)
for (int x = 0; x < 4; ++x) {
world.setBlock(x, 0, z, Block::Stone);
world.setBlock(x, 1, z, Block::Dirt);
}
world.setBlock(0, 1, 0, Block::Grass);
Selection region = Selection::box({0, 0, 0}, {3, 2, 3});
SelectionStats summary = region.stats(world);
CHECK_EQ(summary.blockCount, 48u);
CHECK(summary.dims == Vec3i(4, 3, 4));
CHECK_EQ(summary.boundingVolume, 48);
CHECK_EQ(summary.countOf(Block::Stone), 16u);
CHECK_EQ(summary.countOf(Block::Dirt), 15u);
CHECK_EQ(summary.countOf(Block::Grass), 1u);
CHECK_EQ(summary.countOf(Block::Air), 16u);
CHECK_EQ(summary.solidCount, 32u);
CHECK_EQ(summary.airCount, 16u);
CHECK(summary.dominantBlock() == Block::Stone);
CHECK_EQ(summary.distinctBlocks(), 4u);
}
VF_TEST(anEmptySelectionHasNoCellsAndNoStatistics) {
Selection nothing;
CHECK(nothing.empty());
CHECK_EQ(nothing.count(), 0u);
CHECK_EQ(nothing.blocks().size(), 0u);
World world;
SelectionStats summary = nothing.stats(world);
CHECK_EQ(summary.blockCount, 0u);
CHECK_EQ(summary.boundingVolume, 0);
CHECK_EQ(summary.distinctBlocks(), 0u);
CHECK(summary.dominantBlock() == Block::Air);
CHECK_EQ(nothing.expanded(3).count(), 0u);
CHECK_EQ(nothing.outline().count(), 0u);
CHECK(nothing.unionWith(Selection::box({0, 0, 0}, {1, 1, 1})).count() == 8u);
}
VF_TEST(setOperationsComposeIntoARingShapedSelection) {
Selection outer = Selection::sphere({0, 0, 0}, 5);
Selection inner = Selection::sphere({0, 0, 0}, 3);
Selection ring = outer.differenceWith(inner);
CHECK_EQ(ring.count(), outer.count() - inner.count());
CHECK(ring.contains({5, 0, 0}));
CHECK(!ring.contains({0, 0, 0}));
Selection cut = Selection::box({-10, 1, -10}, {10, 10, 10});
Selection lowerRing = ring.differenceWith(cut);
lowerRing.forEach([&](const Vec3i& cell) { CHECK(cell.y <= 0); });
CHECK(lowerRing.count() < ring.count());
CHECK(lowerRing.count() > 0u);
}
VF_TEST(trimmingAMaskShrinksTheBoundingBoxWithoutLosingCells) {
SelectionMask sparse(Vec3i{0, 0, 0}, Vec3i{16, 16, 16});
sparse.add({4, 5, 6});
sparse.add({7, 5, 6});
CHECK_EQ(sparse.cellCount(), 4096u);
SelectionMask tight = sparse.trimmed();
CHECK_EQ(tight.count(), 2u);
CHECK(tight.min() == Vec3i(4, 5, 6));
CHECK(tight.max() == Vec3i(7, 5, 6));
CHECK_EQ(tight.cellCount(), 4u);
CHECK(tight.sameCells(sparse));
}