From 03b0fa187bcc09873a61a3e6ec70f23db80bd97a Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 9 Sep 2026 16:45:09 +0200 Subject: [PATCH] feat: extend Selection --- core/src/Selection.cpp | 153 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/core/src/Selection.cpp b/core/src/Selection.cpp index a221f7a..bc08cab 100644 --- a/core/src/Selection.cpp +++ b/core/src/Selection.cpp @@ -153,3 +153,156 @@ SelectionMask SelectionMask::inverted() const { return flipped; } +SelectionMask SelectionMask::dilated(int radius) const { + if (radius <= 0 || cellCount() == 0u) return *this; + Vec3i lo{corner.x - radius, corner.y - radius, corner.z - radius}; + Vec3i hi{max().x + radius, max().y + radius, max().z + radius}; + SelectionMask grown(lo, extentOf(lo, hi)); + forEach([&](const Vec3i& cell) { grown.add(cell); }); + + for (int step = 0; step < radius; ++step) { + SelectionMask next = grown; + grown.forEach([&](const Vec3i& cell) { + for (const Vec3i& offset : faceOffsets) next.add(cell + offset); + }); + grown = next; + } + return grown; +} + +SelectionMask SelectionMask::eroded(int radius) const { + if (radius <= 0 || cellCount() == 0u) return *this; + SelectionMask shrunk = *this; + for (int step = 0; step < radius; ++step) { + SelectionMask next = shrunk; + shrunk.forEach([&](const Vec3i& cell) { + for (const Vec3i& offset : faceOffsets) { + if (!shrunk.contains(cell + offset)) { + next.remove(cell); + break; + } + } + }); + shrunk = next; + if (shrunk.empty()) break; + } + return shrunk; +} + +SelectionMask SelectionMask::outline() const { + SelectionMask shell(corner, size); + forEach([&](const Vec3i& cell) { + for (const Vec3i& offset : faceOffsets) { + if (!contains(cell + offset)) { + shell.add(cell); + break; + } + } + }); + return shell; +} + +SelectionMask SelectionMask::trimmed() const { + if (empty()) return SelectionMask{}; + Vec3i lo = max(); + Vec3i hi = corner; + forEach([&](const Vec3i& cell) { + lo = minCorner(lo, cell); + hi = maxCorner(hi, cell); + }); + SelectionMask tight(lo, extentOf(lo, hi)); + forEach([&](const Vec3i& cell) { tight.add(cell); }); + return tight; +} + +SelectionMask SelectionMask::shifted(const Vec3i& delta) const { + SelectionMask moved = *this; + moved.corner = corner + delta; + return moved; +} + +bool SelectionMask::sameCells(const SelectionMask& other) const { + if (setCells != other.setCells) return false; + if (setCells == 0u) return true; + SelectionMask left = trimmed(); + SelectionMask right = other.trimmed(); + if (!(left.corner == right.corner) || !(left.size == right.size)) return false; + return left.words == right.words; +} + +void SelectionMask::forEach(const std::function& visit) const { + if (setCells == 0u) return; + for (int y = 0; y < size.y; ++y) + for (int z = 0; z < size.z; ++z) + for (int x = 0; x < size.x; ++x) { + Vec3i cell{corner.x + x, corner.y + y, corner.z + z}; + if (contains(cell)) visit(cell); + } +} + +std::vector SelectionMask::cells() const { + std::vector out; + out.reserve(setCells); + forEach([&](const Vec3i& cell) { out.push_back(cell); }); + return out; +} + +uint32_t SelectionStats::countOf(Block block) const { + std::size_t index = static_cast(block); + if (index >= histogram.size()) return 0u; + return histogram[index]; +} + +Block SelectionStats::dominantBlock() const { + Block best = Block::Air; + uint32_t bestCount = 0; + for (std::size_t index = 1; index < histogram.size(); ++index) { + if (histogram[index] > bestCount) { + bestCount = histogram[index]; + best = static_cast(index); + } + } + return best; +} + +std::size_t SelectionStats::distinctBlocks() const { + std::size_t distinct = 0; + for (uint32_t entry : histogram) + if (entry != 0u) ++distinct; + return distinct; +} + +Selection Selection::derived(SelectionMask mask) { + Selection out; + out.kind = SelectionShape::Mask; + out.cells = std::move(mask); + return out; +} + +Selection Selection::box(const Vec3i& cornerA, const Vec3i& cornerB) { + Vec3i lo = minCorner(cornerA, cornerB); + Vec3i hi = maxCorner(cornerA, cornerB); + Selection out; + out.kind = SelectionShape::Box; + out.cells = SelectionMask(lo, extentOf(lo, hi)); + out.cells.fillAll(); + return out; +} + +Selection Selection::sphere(const Vec3i& center, int radius) { + Selection out = ellipsoid(center, {radius, radius, radius}); + out.kind = SelectionShape::Sphere; + return out; +} + +Selection Selection::ellipsoid(const Vec3i& center, const Vec3i& radii) { + int rx = std::max(radii.x, 0); + int ry = std::max(radii.y, 0); + int rz = std::max(radii.z, 0); + Vec3i lo{center.x - rx, center.y - ry, center.z - rz}; + Vec3i hi{center.x + rx, center.y + ry, center.z + rz}; + + Selection out; + out.kind = SelectionShape::Ellipsoid; + out.cells = SelectionMask(lo, extentOf(lo, hi)); +