From b6fe9d78ebbff43eef553487fab43098c536e67d Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sat, 12 Sep 2026 23:13:59 +0200 Subject: [PATCH] test: cover slabWorld --- tests/testSelection.cpp | 157 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 tests/testSelection.cpp diff --git a/tests/testSelection.cpp b/tests/testSelection.cpp new file mode 100644 index 0000000..d4346c1 --- /dev/null +++ b/tests/testSelection.cpp @@ -0,0 +1,157 @@ +#include "testHarness.h" +#include "vf/Selection.h" + +#include +#include + +using namespace vf; + +namespace { + +World slabWorld(int spanX, int spanZ, int surfaceY, Block block) { + World world; + for (int z = 0; z < spanZ; ++z) + for (int x = 0; x < spanX; ++x) world.setBlock(x, surfaceY, z, block); + return world; +} + +std::size_t cellsInsideBox(const Selection& selection, const Vec3i& lo, const Vec3i& hi) { + std::size_t inside = 0; + selection.forEach([&](const Vec3i& cell) { + if (cell.x >= lo.x && cell.x <= hi.x && cell.y >= lo.y && cell.y <= hi.y && + cell.z >= lo.z && cell.z <= hi.z) + ++inside; + }); + return inside; +} + +} // namespace + +VF_TEST(boxSelectionCoversEveryCellBetweenTheTwoCorners) { + Selection box = Selection::box({4, 1, -2}, {1, 3, 0}); + CHECK(box.shape() == SelectionShape::Box); + CHECK(box.min() == Vec3i(1, 1, -2)); + CHECK(box.max() == Vec3i(4, 3, 0)); + CHECK(box.dims() == Vec3i(4, 3, 3)); + CHECK_EQ(box.count(), 36u); + CHECK(box.contains({1, 1, -2})); + CHECK(box.contains({4, 3, 0})); + CHECK(!box.contains({5, 3, 0})); + CHECK(!box.contains({1, 0, 0})); +} + +VF_TEST(sphereSelectionIsSymmetricAroundItsCentreAndFitsItsBoundingBox) { + Selection sphere = Selection::sphere({10, 10, 10}, 4); + CHECK(sphere.shape() == SelectionShape::Sphere); + CHECK(sphere.dims() == Vec3i(9, 9, 9)); + CHECK(sphere.contains({10, 10, 10})); + CHECK(sphere.contains({14, 10, 10})); + CHECK(!sphere.contains({14, 14, 10})); + CHECK(sphere.count() < 9u * 9u * 9u); + CHECK(sphere.count() > 200u); + + sphere.forEach([&](const Vec3i& cell) { + Vec3i mirrored{20 - cell.x, 20 - cell.y, 20 - cell.z}; + CHECK(sphere.contains(mirrored)); + }); +} + +VF_TEST(ellipsoidSelectionRespectsAPerAxisRadius) { + Selection blob = Selection::ellipsoid({0, 0, 0}, {6, 1, 2}); + CHECK(blob.dims() == Vec3i(13, 3, 5)); + CHECK(blob.contains({6, 0, 0})); + CHECK(blob.contains({0, 1, 0})); + CHECK(blob.contains({0, 0, 2})); + CHECK(!blob.contains({6, 1, 2})); + CHECK(!blob.contains({0, 2, 0})); +} + +VF_TEST(cylinderSelectionRepeatsTheSameDiscOnEveryLayer) { + Selection tube = Selection::cylinder({0, 5, 0}, 3, 4); + CHECK(tube.min() == Vec3i(-3, 5, -3)); + CHECK(tube.max() == Vec3i(3, 8, 3)); + + std::size_t perLayer = 0; + for (int z = -3; z <= 3; ++z) + for (int x = -3; x <= 3; ++x) + if (tube.contains({x, 5, z})) ++perLayer; + CHECK(perLayer > 0u); + CHECK_EQ(tube.count(), perLayer * 4u); + for (int y = 5; y <= 8; ++y) { + std::size_t layer = 0; + for (int z = -3; z <= 3; ++z) + for (int x = -3; x <= 3; ++x) + if (tube.contains({x, y, z})) ++layer; + CHECK_EQ(layer, perLayer); + } + CHECK(!tube.contains({0, 9, 0})); +} + +VF_TEST(hollowBoxKeepsOnlyTheWallsOfTheRequestedThickness) { + Selection walls = Selection::hollowBox({0, 0, 0}, {4, 4, 4}, 1); + CHECK_EQ(walls.count(), 125u - 27u); + CHECK(walls.contains({0, 0, 0})); + CHECK(walls.contains({4, 2, 2})); + CHECK(!walls.contains({2, 2, 2})); + + Selection thick = Selection::hollowBox({0, 0, 0}, {6, 6, 6}, 2); + CHECK_EQ(thick.count(), 343u - 27u); + CHECK(thick.contains({1, 1, 1})); + CHECK(!thick.contains({3, 3, 3})); +} + +VF_TEST(maskUnionCollectsCellsFromBothOperandsWithoutDoubleCounting) { + Selection left = Selection::box({0, 0, 0}, {3, 0, 0}); + Selection right = Selection::box({2, 0, 0}, {5, 0, 0}); + Selection merged = left.unionWith(right); + CHECK_EQ(merged.count(), 6u); + CHECK(merged.min() == Vec3i(0, 0, 0)); + CHECK(merged.max() == Vec3i(5, 0, 0)); + for (int x = 0; x <= 5; ++x) CHECK(merged.contains({x, 0, 0})); + CHECK(merged.shape() == SelectionShape::Mask); +} + +VF_TEST(maskIntersectionKeepsOnlyTheOverlapAndIsEmptyWhenBoxesMiss) { + Selection left = Selection::box({0, 0, 0}, {3, 3, 3}); + Selection right = Selection::box({2, 2, 2}, {5, 5, 5}); + Selection shared = left.intersectWith(right); + CHECK_EQ(shared.count(), 8u); + CHECK(shared.min() == Vec3i(2, 2, 2)); + CHECK(shared.max() == Vec3i(3, 3, 3)); + + Selection apart = Selection::box({20, 20, 20}, {21, 21, 21}); + CHECK(left.intersectWith(apart).empty()); + CHECK_EQ(left.intersectWith(apart).count(), 0u); +} + +VF_TEST(maskDifferenceRemovesEveryCellOfTheSecondOperand) { + Selection block = Selection::box({0, 0, 0}, {3, 3, 3}); + Selection bite = Selection::box({0, 0, 0}, {1, 1, 1}); + Selection carved = block.differenceWith(bite); + CHECK_EQ(carved.count(), 64u - 8u); + CHECK(!carved.contains({0, 0, 0})); + CHECK(carved.contains({2, 0, 0})); + CHECK(carved.min() == Vec3i(0, 0, 0)); +} + +VF_TEST(maskNegationFlipsEveryCellInsideTheBoundingBox) { + Selection sphere = Selection::sphere({0, 0, 0}, 3); + Selection hole = sphere.inverted(); + CHECK_EQ(sphere.count() + hole.count(), 7u * 7u * 7u); + CHECK(hole.contains({3, 3, 3})); + CHECK(!hole.contains({0, 0, 0})); + CHECK(sphere.intersectWith(hole).empty()); + CHECK_EQ(sphere.unionWith(hole).count(), 7u * 7u * 7u); +} + +VF_TEST(dilationGrowsOneCellIntoASixNeighbourCrossAndKeepsGoing) { + Selection seed = Selection::box({0, 0, 0}, {0, 0, 0}); + CHECK_EQ(seed.count(), 1u); + + Selection once = seed.expanded(1); + CHECK_EQ(once.count(), 7u); + CHECK(once.contains({1, 0, 0})); + CHECK(once.contains({0, -1, 0})); + CHECK(!once.contains({1, 1, 0})); + CHECK(once.min() == Vec3i(-1, -1, -1)); +