feat: add popcount64 and minCorner

This commit is contained in:
dualfroz committed 2026-09-09 16:30:08 +02:00
1 parent c3d3c57a71
commit 7ca42dcf3b
1 file changed
+155
+155
View File
@@ -0,0 +1,155 @@
#include "vf/Selection.h"
#include <algorithm>
#include <deque>
#include <unordered_set>
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<std::size_t>(size.x) * static_cast<std::size_t>(size.y) *
static_cast<std::size_t>(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<std::size_t>(position.x - corner.x);
std::size_t localY = static_cast<std::size_t>(position.y - corner.y);
std::size_t localZ = static_cast<std::size_t>(position.z - corner.z);
return (localY * static_cast<std::size_t>(size.z) + localZ) * static_cast<std::size_t>(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<std::size_t>(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;
}