feat: expand Selection
This commit is contained in:
1 parent
03b0fa187b
commit
f61d9eed00
1 file changed
+164
@@ -306,3 +306,167 @@ Selection Selection::ellipsoid(const Vec3i& center, const Vec3i& radii) {
|
||||
out.kind = SelectionShape::Ellipsoid;
|
||||
out.cells = SelectionMask(lo, extentOf(lo, hi));
|
||||
|
||||
double halfX = rx + 0.5;
|
||||
double halfY = ry + 0.5;
|
||||
double halfZ = rz + 0.5;
|
||||
for (int y = lo.y; y <= hi.y; ++y)
|
||||
for (int z = lo.z; z <= hi.z; ++z)
|
||||
for (int x = lo.x; x <= hi.x; ++x) {
|
||||
double nx = (x - center.x) / halfX;
|
||||
double ny = (y - center.y) / halfY;
|
||||
double nz = (z - center.z) / halfZ;
|
||||
if (nx * nx + ny * ny + nz * nz <= 1.0) out.cells.add({x, y, z});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Selection Selection::cylinder(const Vec3i& center, int radius, int height) {
|
||||
int r = std::max(radius, 0);
|
||||
int h = std::max(height, 1);
|
||||
Vec3i lo{center.x - r, center.y, center.z - r};
|
||||
Vec3i hi{center.x + r, center.y + h - 1, center.z + r};
|
||||
|
||||
Selection out;
|
||||
out.kind = SelectionShape::Cylinder;
|
||||
out.cells = SelectionMask(lo, extentOf(lo, hi));
|
||||
|
||||
double limit = (r + 0.5) * (r + 0.5);
|
||||
for (int y = lo.y; y <= hi.y; ++y)
|
||||
for (int z = lo.z; z <= hi.z; ++z)
|
||||
for (int x = lo.x; x <= hi.x; ++x) {
|
||||
double dx = x - center.x;
|
||||
double dz = z - center.z;
|
||||
if (dx * dx + dz * dz <= limit) out.cells.add({x, y, z});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Selection Selection::hollowBox(const Vec3i& cornerA, const Vec3i& cornerB, int thickness) {
|
||||
int wall = std::max(thickness, 1);
|
||||
Vec3i lo = minCorner(cornerA, cornerB);
|
||||
Vec3i hi = maxCorner(cornerA, cornerB);
|
||||
|
||||
Selection out;
|
||||
out.kind = SelectionShape::HollowBox;
|
||||
out.cells = SelectionMask(lo, extentOf(lo, hi));
|
||||
|
||||
for (int y = lo.y; y <= hi.y; ++y)
|
||||
for (int z = lo.z; z <= hi.z; ++z)
|
||||
for (int x = lo.x; x <= hi.x; ++x) {
|
||||
bool onWall = (x - lo.x) < wall || (hi.x - x) < wall || (y - lo.y) < wall ||
|
||||
(hi.y - y) < wall || (z - lo.z) < wall || (hi.z - z) < wall;
|
||||
if (onWall) out.cells.add({x, y, z});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Selection Selection::fromMask(const SelectionMask& mask) { return derived(mask); }
|
||||
|
||||
Selection Selection::magicWand(const World& world, const Vec3i& seed, std::size_t maxCells) {
|
||||
Vec3i lo{seed.x - wandUnboundedReach, seed.y - wandUnboundedReach, seed.z - wandUnboundedReach};
|
||||
Vec3i hi{seed.x + wandUnboundedReach, seed.y + wandUnboundedReach, seed.z + wandUnboundedReach};
|
||||
return magicWandWithin(world, seed, maxCells, lo, hi);
|
||||
}
|
||||
|
||||
Selection Selection::magicWandWithin(const World& world, const Vec3i& seed, std::size_t maxCells,
|
||||
const Vec3i& limitMin, const Vec3i& limitMax) {
|
||||
Selection out;
|
||||
out.kind = SelectionShape::Wand;
|
||||
if (maxCells == 0u) return out;
|
||||
|
||||
Vec3i lo = minCorner(limitMin, limitMax);
|
||||
Vec3i hi = maxCorner(limitMin, limitMax);
|
||||
auto insideLimit = [&](const Vec3i& cell) {
|
||||
return cell.x >= lo.x && cell.x <= hi.x && cell.y >= lo.y && cell.y <= hi.y &&
|
||||
cell.z >= lo.z && cell.z <= hi.z;
|
||||
};
|
||||
if (!insideLimit(seed)) return out;
|
||||
|
||||
Block wanted = world.blockAt(seed.x, seed.y, seed.z);
|
||||
std::unordered_set<Vec3i, Vec3iHash> seen;
|
||||
std::deque<Vec3i> frontier;
|
||||
std::vector<Vec3i> found;
|
||||
seen.insert(seed);
|
||||
frontier.push_back(seed);
|
||||
|
||||
while (!frontier.empty()) {
|
||||
if (found.size() >= maxCells) {
|
||||
out.hitLimit = true;
|
||||
break;
|
||||
}
|
||||
Vec3i cell = frontier.front();
|
||||
frontier.pop_front();
|
||||
found.push_back(cell);
|
||||
for (const Vec3i& offset : faceOffsets) {
|
||||
Vec3i next = cell + offset;
|
||||
if (!insideLimit(next)) continue;
|
||||
if (seen.count(next) != 0u) continue;
|
||||
if (world.blockAt(next.x, next.y, next.z) != wanted) continue;
|
||||
seen.insert(next);
|
||||
frontier.push_back(next);
|
||||
}
|
||||
}
|
||||
|
||||
if (found.empty()) return out;
|
||||
Vec3i foundLo = found.front();
|
||||
Vec3i foundHi = found.front();
|
||||
for (const Vec3i& cell : found) {
|
||||
foundLo = minCorner(foundLo, cell);
|
||||
foundHi = maxCorner(foundHi, cell);
|
||||
}
|
||||
out.cells = SelectionMask(foundLo, extentOf(foundLo, foundHi));
|
||||
for (const Vec3i& cell : found) out.cells.add(cell);
|
||||
return out;
|
||||
}
|
||||
|
||||
Selection Selection::unionWith(const Selection& other) const {
|
||||
return derived(cells.unionWith(other.cells));
|
||||
}
|
||||
|
||||
Selection Selection::intersectWith(const Selection& other) const {
|
||||
return derived(cells.intersectWith(other.cells));
|
||||
}
|
||||
|
||||
Selection Selection::differenceWith(const Selection& other) const {
|
||||
return derived(cells.differenceWith(other.cells));
|
||||
}
|
||||
|
||||
Selection Selection::inverted() const { return derived(cells.inverted()); }
|
||||
|
||||
Selection Selection::expanded(int radius) const { return derived(cells.dilated(radius)); }
|
||||
|
||||
Selection Selection::contracted(int radius) const { return derived(cells.eroded(radius)); }
|
||||
|
||||
Selection Selection::outline() const { return derived(cells.outline()); }
|
||||
|
||||
Selection Selection::shifted(const Vec3i& delta) const {
|
||||
Selection out = *this;
|
||||
out.cells = cells.shifted(delta);
|
||||
return out;
|
||||
}
|
||||
|
||||
SelectionStats Selection::stats(const World& world) const {
|
||||
SelectionStats summary;
|
||||
summary.histogram.assign(static_cast<std::size_t>(Block::Count), 0u);
|
||||
summary.dims = cells.dims();
|
||||
summary.boundingVolume = static_cast<long long>(cells.cellCount());
|
||||
summary.blockCount = cells.count();
|
||||
cells.forEach([&](const Vec3i& cell) {
|
||||
Block block = world.blockAt(cell.x, cell.y, cell.z);
|
||||
++summary.histogram[static_cast<std::size_t>(block)];
|
||||
if (isAir(block)) {
|
||||
++summary.airCount;
|
||||
} else {
|
||||
++summary.solidCount;
|
||||
}
|
||||
});
|
||||
return summary;
|
||||
}
|
||||
|
||||
std::vector<Vec3i> Selection::blocks() const { return cells.cells(); }
|
||||
|
||||
void Selection::forEach(const std::function<void(const Vec3i&)>& visit) const {
|
||||
cells.forEach(visit);
|
||||
}
|
||||
|
||||
} // namespace vf
|
||||
Reference in new issue
Block a user