From 7ca42dcf3b430b8d549f541de7277093ad515262 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 9 Sep 2026 16:30:08 +0200 Subject: [PATCH] feat: add popcount64 and minCorner --- core/src/Selection.cpp | 155 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 core/src/Selection.cpp diff --git a/core/src/Selection.cpp b/core/src/Selection.cpp new file mode 100644 index 0000000..a221f7a --- /dev/null +++ b/core/src/Selection.cpp @@ -0,0 +1,155 @@ +#include "vf/Selection.h" + +#include +#include +#include + +namespace vf { + +namespace { + +constexpr int wandUnboundedReach = 1 << 20; + +const Vec3i faceOffsets[6] = { + {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}, +}; + +int popcount64(uint64_t value) { + int bits = 0; + while (value != 0u) { + value &= value - 1u; + ++bits; + } + return bits; +} + +Vec3i minCorner(const Vec3i& a, const Vec3i& b) { + return {std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z)}; +} + +Vec3i maxCorner(const Vec3i& a, const Vec3i& b) { + return {std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z)}; +} + +Vec3i extentOf(const Vec3i& lo, const Vec3i& hi) { + return {hi.x - lo.x + 1, hi.y - lo.y + 1, hi.z - lo.z + 1}; +} + +} // namespace + +SelectionMask::SelectionMask(const Vec3i& minCornerIn, const Vec3i& dims) { + if (dims.x <= 0 || dims.y <= 0 || dims.z <= 0) return; + corner = minCornerIn; + size = dims; + words.assign((cellCount() + 63u) / 64u, 0u); +} + +Vec3i SelectionMask::max() const { + return {corner.x + size.x - 1, corner.y + size.y - 1, corner.z + size.z - 1}; +} + +std::size_t SelectionMask::cellCount() const { + return static_cast(size.x) * static_cast(size.y) * + static_cast(size.z); +} + +bool SelectionMask::inBounds(const Vec3i& position) const { + return position.x >= corner.x && position.x < corner.x + size.x && + position.y >= corner.y && position.y < corner.y + size.y && + position.z >= corner.z && position.z < corner.z + size.z; +} + +std::size_t SelectionMask::indexOf(const Vec3i& position) const { + std::size_t localX = static_cast(position.x - corner.x); + std::size_t localY = static_cast(position.y - corner.y); + std::size_t localZ = static_cast(position.z - corner.z); + return (localY * static_cast(size.z) + localZ) * static_cast(size.x) + + localX; +} + +bool SelectionMask::contains(const Vec3i& position) const { + if (!inBounds(position)) return false; + std::size_t index = indexOf(position); + return (words[index >> 6] >> (index & 63u)) & 1u; +} + +void SelectionMask::add(const Vec3i& position) { setCell(position, true); } + +void SelectionMask::remove(const Vec3i& position) { setCell(position, false); } + +void SelectionMask::setCell(const Vec3i& position, bool on) { + if (!inBounds(position)) return; + std::size_t index = indexOf(position); + uint64_t bit = uint64_t(1) << (index & 63u); + bool was = (words[index >> 6] & bit) != 0u; + if (was == on) return; + if (on) { + words[index >> 6] |= bit; + ++setCells; + } else { + words[index >> 6] &= ~bit; + --setCells; + } +} + +void SelectionMask::fillAll() { + std::size_t total = cellCount(); + if (total == 0u) return; + std::fill(words.begin(), words.end(), ~uint64_t(0)); + std::size_t tail = total & 63u; + if (tail != 0u) words.back() = (uint64_t(1) << tail) - 1u; + setCells = total; +} + +void SelectionMask::clearAll() { + std::fill(words.begin(), words.end(), uint64_t(0)); + setCells = 0; +} + +void SelectionMask::recount() { + std::size_t total = 0; + for (uint64_t word : words) total += static_cast(popcount64(word)); + setCells = total; +} + +SelectionMask SelectionMask::unionWith(const SelectionMask& other) const { + if (cellCount() == 0u) return other; + if (other.cellCount() == 0u) return *this; + Vec3i lo = minCorner(corner, other.corner); + Vec3i hi = maxCorner(max(), other.max()); + SelectionMask merged(lo, extentOf(lo, hi)); + forEach([&](const Vec3i& cell) { merged.add(cell); }); + other.forEach([&](const Vec3i& cell) { merged.add(cell); }); + return merged; +} + +SelectionMask SelectionMask::intersectWith(const SelectionMask& other) const { + Vec3i lo = maxCorner(corner, other.corner); + Vec3i hi = minCorner(max(), other.max()); + if (cellCount() == 0u || other.cellCount() == 0u) return SelectionMask{}; + if (hi.x < lo.x || hi.y < lo.y || hi.z < lo.z) return SelectionMask{}; + SelectionMask shared(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) { + Vec3i cell{x, y, z}; + if (contains(cell) && other.contains(cell)) shared.add(cell); + } + return shared; +} + +SelectionMask SelectionMask::differenceWith(const SelectionMask& other) const { + SelectionMask remainder = *this; + other.forEach([&](const Vec3i& cell) { remainder.remove(cell); }); + return remainder; +} + +SelectionMask SelectionMask::inverted() const { + SelectionMask flipped(corner, size); + if (flipped.cellCount() == 0u) return flipped; + flipped.fillAll(); + for (std::size_t word = 0; word < words.size(); ++word) flipped.words[word] &= ~words[word]; + flipped.recount(); + return flipped; +} +