feat: add popcount64 and minCorner
This commit is contained in:
1 parent
c3d3c57a71
commit
7ca42dcf3b
1 file changed
+155
@@ -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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user