158 lines
5.4 KiB
C++
158 lines
5.4 KiB
C++
#include "testHarness.h"
|
|
#include "vf/Selection.h"
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
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));
|
|
|