41template <
class TPo
intType>
42 requires std::signed_integral<typename TPointType::NumberType>
49inline constexpr bool is_bit_matrix_v =
false;
51template <
class TPo
intType>
52inline constexpr bool is_bit_matrix_v<BitMatrix<TPointType>> =
true;
68template <
class Int,
class Number>
69[[nodiscard]] Int gridCoordinate(
const Number& value) {
70 if constexpr (std::same_as<Int, Number>) {
73 if (!value.isInteger()) {
74 throw std::logic_error(
"pgl::BitMatrix: a coordinate is not an integer");
77 }
else if constexpr (std::is_floating_point_v<Number>) {
78 if (!std::isfinite(value) || value != std::floor(value)) {
79 throw std::logic_error(
"pgl::BitMatrix: a coordinate is not an integer");
84 const Number low =
static_cast<Number
>(numeric_limits<Int>::min());
85 if (value < low || value >= -low) {
86 throw std::logic_error(
"pgl::BitMatrix: a coordinate does not fit the grid");
88 return static_cast<Int
>(value);
93 constexpr bool comparable = !numeric_limits<Number>::is_specialized
94 || numeric_limits<Number>::digits >= numeric_limits<Int>::digits;
95 if constexpr (comparable) {
96 if (value <
static_cast<Number
>(numeric_limits<Int>::min())
97 || value >
static_cast<Number
>(numeric_limits<Int>::max())) {
98 throw std::logic_error(
"pgl::BitMatrix: a coordinate does not fit the grid");
101 return static_cast<Int
>(value);
106template <
class Gr
idPo
intType,
class OtherPo
intType>
107[[nodiscard]] GridPointType gridPoint(
const OtherPointType& point) {
108 using Int =
typename GridPointType::NumberType;
109 return GridPointType(gridCoordinate<Int>(point.x()), gridCoordinate<Int>(point.y()));
119template <
class Gr
idPo
intType,
class OtherPo
intType,
class TLabel>
121 std::vector<GridPointType> vertices;
122 vertices.reserve(ring.size());
123 for (
const OtherPointType&
vertex : ring) {
124 vertices.push_back(gridPoint<GridPointType>(
vertex));
130template <
class Gr
idPo
intType,
class OtherPo
intType,
class TLabel>
133 std::vector<Polygon<GridPointType>> holes;
134 holes.reserve(region.holeCount());
135 for (
const auto& hole : region.holes()) {
136 holes.push_back(gridRing<GridPointType>(hole));
143template <
class Gr
idPo
intType,
class OtherPo
intType,
class TLabel>
145 std::vector<PolygonWithHoles<GridPointType>> components;
146 components.reserve(set.componentCount());
147 for (
const auto& component : set.components()) {
148 components.push_back(gridRegion<GridPointType>(component));
160template <
class Po
intType,
class Int>
161using grid_point_t = std::conditional_t<std::same_as<Int, typename PointType::NumberType>, PointType,
220template <
class TPo
intType = Po
int<
int>>
221 requires std::signed_integral<typename TPointType::NumberType>
261 origin_ = std::move(
origin);
265 words_ = wordsPerRow(width_);
266 bits_.assign(words_ *
static_cast<std::size_t
>(height_), 0);
281 box.
empty() ? 0 : static_cast<int>(box.
width()),
282 box.
empty() ? 0 : static_cast<int>(box.
height())) {}
301 template <
class TLabel>
304 Crossings crossings(
static_cast<std::size_t
>(height_));
305 addRegionCrossings(region, crossings);
306 fillCrossings(crossings);
326 template <
class OtherPo
intType,
class TLabel>
341 template <
class TLabel>
344 Crossings crossings(
static_cast<std::size_t
>(height_));
345 addRingCrossings(polygon, crossings);
346 fillCrossings(crossings);
360 template <
class OtherPo
intType,
class TLabel>
376 template <
class TLabel>
379 Crossings crossings(
static_cast<std::size_t
>(height_));
381 addRegionCrossings(component, crossings);
383 fillCrossings(crossings);
397 template <
class OtherPo
intType,
class TLabel>
426 template <std::ranges::input_range Range = std::initializer_list<Po
intType>>
427 requires(detail::is_point_v<std::remove_cvref_t<std::ranges::range_value_t<Range>>>
429 && !detail::is_bit_matrix_v<std::remove_cvref_t<Range>>)
431 using SourcePoint = std::remove_cvref_t<std::ranges::range_value_t<Range>>;
432 if constexpr (std::ranges::forward_range<Range>
433 && std::same_as<typename SourcePoint::NumberType, NumberType>) {
434 *
this = emptyOver(points);
435 for (
const auto& point : points) {
436 set(point.x(), point.y());
442 std::vector<PointType>
cells;
443 for (
const auto& point : points) {
444 cells.push_back(detail::gridPoint<PointType>(point));
457 [[nodiscard]]
int width()
const {
return width_; }
460 [[nodiscard]]
int height()
const {
return height_; }
468 origin_.y() +
static_cast<NumberType>(height_)));
476 const std::int64_t i = localX(
x), j = localY(
y);
477 return i >= 0 && i < width_ && j >= 0 && j < height_;
485 return width_ == other.width_ && height_ == other.height_ && origin_ == other.origin_;
495 result.combine(*
this, [](std::uint64_t, std::uint64_t source) {
return source; });
512 const std::int64_t i = localX(
x), j = localY(
y);
513 if (i < 0 || i >= width_ || j < 0 || j >= height_) {
516 return ((row(
static_cast<int>(j))[
static_cast<std::size_t
>(i) / 64] >> (i % 64)) & 1) != 0;
520 [[nodiscard]]
bool get(
const PointType& cell)
const {
return get(cell.x(), cell.y()); }
524 const std::int64_t i = localX(
x), j = localY(
y);
525 if (i < 0 || i >= width_ || j < 0 || j >= height_) {
528 row(
static_cast<int>(j))[
static_cast<std::size_t
>(i) / 64] |= std::uint64_t(1) << (i % 64);
562 template <std::ranges::input_range Range = std::initializer_list<Po
intType>>
563 requires(detail::is_point_v<std::remove_cvref_t<std::ranges::range_value_t<Range>>>
565 && !detail::is_bit_matrix_v<std::remove_cvref_t<Range>>)
566 void set(Range&& points) {
567 for (
const auto& point : points) {
568 set(detail::gridPoint<PointType>(point));
574 const std::int64_t i = localX(
x), j = localY(
y);
575 if (i < 0 || i >= width_ || j < 0 || j >= height_) {
578 row(
static_cast<int>(j))[
static_cast<std::size_t
>(i) / 64] &= ~(std::uint64_t(1) << (i % 64));
598 template <std::ranges::input_range Range = std::initializer_list<Po
intType>>
599 requires(detail::is_point_v<std::remove_cvref_t<std::ranges::range_value_t<Range>>>
601 && !detail::is_bit_matrix_v<std::remove_cvref_t<Range>>)
603 for (
const auto& point : points) {
604 reset(detail::gridPoint<PointType>(point));
610 const std::int64_t i = localX(
x), j = localY(
y);
611 if (i < 0 || i >= width_ || j < 0 || j >= height_) {
614 row(
static_cast<int>(j))[
static_cast<std::size_t
>(i) / 64] ^= std::uint64_t(1) << (i % 64);
635 template <std::ranges::input_range Range = std::initializer_list<Po
intType>>
636 requires(detail::is_point_v<std::remove_cvref_t<std::ranges::range_value_t<Range>>>
638 && !detail::is_bit_matrix_v<std::remove_cvref_t<Range>>)
640 for (
const auto& point : points) {
641 flip(detail::gridPoint<PointType>(point));
647 std::fill(bits_.begin(), bits_.end(), ~std::uint64_t(0));
652 void clear() { std::fill(bits_.begin(), bits_.end(), 0); }
659 for (std::uint64_t word : bits_) {
668 [[nodiscard]] std::size_t
count()
const {
669 std::size_t total = 0;
670 for (std::uint64_t word : bits_) {
671 total +=
static_cast<std::size_t
>(std::popcount(word));
682 template <
class ResultNumber = NumberType>
683 [[nodiscard]] ResultNumber
area()
const {
684 return ResultNumber(
static_cast<std::int64_t
>(
count()));
696 template <
class ResultNumber = NumberType>
698 std::size_t adjacent = 0;
699 for (
int j = 0; j < height_; ++j) {
700 const std::uint64_t* here = row(j);
701 const std::uint64_t* above = j + 1 < height_ ? row(j + 1) :
nullptr;
702 for (std::size_t w = 0; w < words_; ++w) {
703 const std::uint64_t word = here[w];
707 adjacent +=
static_cast<std::size_t
>(
708 std::popcount(word & shiftedWord(here, words_,
static_cast<std::int64_t
>(w) * 64 + 1)));
709 if (above !=
nullptr) {
710 adjacent +=
static_cast<std::size_t
>(std::popcount(word & above[w]));
714 return ResultNumber(
static_cast<std::int64_t
>(4 *
count() - 2 * adjacent));
726 template <
class ResultNumber = division_result_t<NumberType>>
730 std::int64_t sumX = 0, sumY = 0,
cells = 0;
731 for (
int j = 0; j < height_; ++j) {
732 const std::uint64_t* here = row(j);
733 const std::int64_t
y =
static_cast<std::int64_t
>(origin_.y()) + j;
734 for (std::size_t w = 0; w < words_; ++w) {
735 std::uint64_t word = here[w];
739 const std::int64_t inWord = std::popcount(word);
740 const std::int64_t base =
741 static_cast<std::int64_t
>(origin_.x()) +
static_cast<std::int64_t
>(w) * 64;
742 std::int64_t offsets = 0;
743 for (; word != 0; word &= word - 1) {
744 offsets += std::countr_zero(word);
746 sumX += base * inWord + offsets;
752 throw std::logic_error(
"pgl::BitMatrix::centroid: no cell is set");
756 const ResultNumber total = ResultNumber(2 *
cells);
758 ResultNumber(2 * sumY +
cells) / total);
793 std::views::transform([](
const PointType& cell) {
return cellSquare(cell); });
804 [[nodiscard]] std::vector<PointType>
lattice()
const {
805 std::vector<PointType> result;
806 result.reserve(
count());
808 result.push_back(cell);
820 [[nodiscard]] std::vector<RectangleType>
cells()
const {
821 std::vector<RectangleType> result;
822 result.reserve(
count());
824 result.push_back(cellSquare(cell));
836 [[nodiscard]] std::vector<RectangleType>
rectangles()
const {
837 std::vector<RectangleType> result;
838 for (
int j = 0; j < height_; ++j) {
841 if (!localGet(j, i)) {
845 const std::int64_t start = i;
846 while (i < width_ && localGet(j, i)) {
853 origin_.y() +
static_cast<NumberType>(j + 1)));
866 std::int64_t minX = width_, maxX = -1, minY = height_, maxY = -1;
867 for (
int j = 0; j < height_; ++j) {
868 const std::uint64_t* here = row(j);
869 std::size_t first = 0;
870 while (first < words_ && here[first] == 0) {
873 if (first == words_) {
876 std::size_t last = words_ - 1;
877 while (here[last] == 0) {
880 minX = std::min(minX,
static_cast<std::int64_t
>(first) * 64 + std::countr_zero(here[first]));
881 maxX = std::max(maxX,
static_cast<std::int64_t
>(last) * 64 + 63 - std::countl_zero(here[last]));
882 minY = std::min(minY,
static_cast<std::int64_t
>(j));
883 maxY = std::max(maxY,
static_cast<std::int64_t
>(j));
891 origin_.y() +
static_cast<NumberType>(maxY + 1)));
899 template <
class ResultNumber =
double>
913 template <
class ResultNumber = division_result_t<NumberType>>
915 const std::optional<PointType> cell = firstSetCell();
917 throw std::logic_error(
"pgl::BitMatrix::pointInside: no cell is set");
919 const ResultNumber half = ResultNumber(1) / ResultNumber(2);
920 return Point<ResultNumber>(ResultNumber(cell->x()) + half, ResultNumber(cell->y()) + half);
934 throw std::logic_error(
"pgl::BitMatrix::asPolygonWithHoles: the cells are not edge-connected");
936 return regionFromLoops(boundaryLoops(
937 [](
const detail::PolyCell&,
const detail::PolyCell&) {
return true; }));
965 std::size_t component;
967 std::vector<std::vector<LabeledRun>> rowRuns(
static_cast<std::size_t
>(height_));
968 std::size_t total = 0;
970 for (
const CellRun& run : runs) {
971 rowRuns[
static_cast<std::size_t
>(run.y)].push_back({run.x0, run.x1, total});
978 for (std::vector<LabeledRun>& runs : rowRuns) {
979 std::sort(runs.begin(), runs.end(),
980 [](
const LabeledRun& a,
const LabeledRun& b) { return a.x0 < b.x0; });
983 auto componentAt = [&](
const detail::PolyCell& cell) {
984 const std::vector<LabeledRun>& runs = rowRuns[
static_cast<std::size_t
>(cell.second)];
985 const auto after = std::upper_bound(
986 runs.begin(), runs.end(),
static_cast<std::int64_t
>(cell.first),
987 [](std::int64_t at,
const LabeledRun& run) { return at < run.x0; });
988 assert(after != runs.begin() && cell.first < std::prev(after)->x1 &&
989 "pgl::BitMatrix::asPolygonSet: a boundary edge borders no cell");
990 return std::prev(after)->component;
996 std::vector<std::vector<std::vector<detail::PolyCell>>> grouped(total);
997 for (std::vector<detail::PolyCell>& loop :
998 boundaryLoops([&](
const detail::PolyCell& here,
const detail::PolyCell& across) {
999 return componentAt(here) == componentAt(across);
1001 grouped[componentAt(loopSeedCell(loop))].push_back(std::move(loop));
1004 std::vector<RegionType> components;
1005 components.reserve(total);
1006 for (
const std::vector<std::vector<detail::PolyCell>>& loops : grouped) {
1007 components.push_back(regionFromLoops(loops));
1014 std::vector<PointType> corners;
1015 corners.reserve(
static_cast<std::size_t
>(height_) * 4);
1016 for (
int j = 0; j < height_; ++j) {
1017 const std::optional<std::pair<std::int64_t, std::int64_t>> extent = rowExtent(j);
1025 corners.emplace_back(low, bottom);
1026 corners.emplace_back(low, top);
1027 corners.emplace_back(high, bottom);
1028 corners.emplace_back(high, top);
1066 BitMatrix result(origin_, width_, height_);
1067 for (std::size_t w = 0; w < bits_.size(); ++w) {
1068 result.bits_[w] = ~bits_[w];
1076 combine(other, [](std::uint64_t left, std::uint64_t right) {
return left & right; });
1082 combine(other, [](std::uint64_t left, std::uint64_t right) {
return left | right; });
1088 combine(other, [](std::uint64_t left, std::uint64_t right) {
return left ^ right; });
1116 result.combine(other, [](std::uint64_t left, std::uint64_t right) {
return left & ~right; });
1131 return width_ == other.width_ && height_ == other.height_ && origin_ == other.origin_ &&
1132 bits_ == other.bits_;
1144 if (
const std::strong_ordering order = origin_ <=> other.origin_; order != 0) {
1147 if (
const std::strong_ordering order = width_ <=> other.width_; order != 0) {
1150 if (
const std::strong_ordering order = height_ <=> other.height_; order != 0) {
1153 return bits_ <=> other.bits_;
1163 const std::size_t here =
count();
1204 for (std::int64_t dy = -1; dy <= 1; ++dy) {
1205 for (std::int64_t dx = -1; dx <= 1; ++dx) {
1206 if (anyCommonCell(other, dx, dy)) {
1230 std::size_t total = 0;
1231 forEachAlignedWord(other, [&](std::uint64_t left, std::uint64_t right) {
1232 total +=
static_cast<std::size_t
>(std::popcount(left & right));
1256 result.origin_ =
PointType(origin_.x() + vector.x(), origin_.y() + vector.y());
1270 origin_ =
PointType(origin_.x() + vector.x(), origin_.y() + vector.y());
1276 origin_ =
PointType(origin_.x() - vector.x(), origin_.y() - vector.y());
1289 return std::pair<std::int64_t, std::int64_t>(-
x - 1, -
y - 1);
1297 return std::pair<std::int64_t, std::int64_t>(
x, -
y - 1);
1302 return std::pair<std::int64_t, std::int64_t>(-
x - 1,
y);
1314 return std::pair<std::int64_t, std::int64_t>(
y,
x);
1323 const int turns = ((k % 4) + 4) % 4;
1324 return mapped([turns](std::int64_t
x, std::int64_t
y) {
1326 case 1:
return std::pair<std::int64_t, std::int64_t>(-
y - 1,
x);
1327 case 2:
return std::pair<std::int64_t, std::int64_t>(-
x - 1, -
y - 1);
1328 case 3:
return std::pair<std::int64_t, std::int64_t>(
y, -
x - 1);
1329 default:
return std::pair<std::int64_t, std::int64_t>(
x,
y);
1353 return std::pair<std::int64_t, std::int64_t>(-
x, -
y);
1358 return std::pair<std::int64_t, std::int64_t>(
x, -
y);
1363 return std::pair<std::int64_t, std::int64_t>(-
x,
y);
1375 const int turns = ((k % 4) + 4) % 4;
1376 return mapped([turns](std::int64_t
x, std::int64_t
y) {
1378 case 1:
return std::pair<std::int64_t, std::int64_t>(-
y,
x);
1379 case 2:
return std::pair<std::int64_t, std::int64_t>(-
x, -
y);
1380 case 3:
return std::pair<std::int64_t, std::int64_t>(
y, -
x);
1381 default:
return std::pair<std::int64_t, std::int64_t>(
x,
y);
1409 const bool stampIsRight = right.
count() < left.
count();
1410 const BitMatrix& stamp = stampIsRight ? right : left;
1411 const BitMatrix& canvas = stampIsRight ? left : right;
1414 left.origin_.y() + right.origin_.y()),
1415 left.width_ + right.width_ - 1, left.height_ + right.height_ - 1);
1417 result.orShifted(canvas,
static_cast<std::int64_t
>(cell.x() - stamp.origin_.x()),
1418 static_cast<std::int64_t
>(cell.y() - stamp.origin_.y()));
1486 BitMatrix result(origin_, width_, height_);
1490 const int resultWidth = width_ - stamp.width_ + 1;
1491 const int resultHeight = height_ - stamp.height_ + 1;
1492 BitMatrix result(
PointType(origin_.x() - stamp.origin_.x(), origin_.y() - stamp.origin_.y()),
1493 resultWidth, resultHeight);
1499 result.andShifted(*
this,
static_cast<std::int64_t
>(cell.x() - stamp.origin_.x()),
1500 static_cast<std::int64_t
>(cell.y() - stamp.origin_.y()));
1525 for (
const auto& [dx, dy] : neighborOffsets(adjacency)) {
1526 result.andShifted(*
this, dx, dy);
1551 std::vector<BitMatrix> result;
1552 visitComponentRuns(adjacency, [&](
const std::vector<CellRun>& runs) {
1553 std::int64_t minX = runs.front().x0, maxX = runs.front().x1 - 1;
1554 int minY = runs.front().y, maxY = minY;
1555 for (
const CellRun& run : runs) {
1556 minX = std::min(minX, run.x0);
1557 maxX = std::max(maxX, run.x1 - 1);
1558 minY = std::min(minY, run.y);
1559 maxY = std::max(maxY, run.y);
1562 origin_.y() +
static_cast<NumberType>(minY)),
1563 static_cast<int>(maxX - minX) + 1, maxY - minY + 1);
1564 for (
const CellRun& run : runs) {
1565 component.setLocalRange(run.y - minY, run.x0 - minX, run.x1 - minX);
1567 result.push_back(std::move(component));
1579 std::size_t total = 0;
1580 visitComponentRuns(adjacency, [&](
const std::vector<CellRun>&) { ++total; });
1608 (
~resized(padded)).floodFrom(padded.
min(), complementary(adjacency));
1609 return (~outside).resized(
window());
1618 return static_cast<std::size_t
>(
static_cast<std::int64_t
>(
componentCount(adjacency)) -
1634 const std::size_t blockWords =
static_cast<std::size_t
>((width_ + 1 + 63) / 64);
1635 const int tail = (width_ + 1) % 64;
1636 const std::uint64_t lastMask = tail == 0 ? ~std::uint64_t(0) : (std::uint64_t(1) << tail) - 1;
1637 const std::vector<std::uint64_t> zeros(words_, 0);
1639 std::int64_t ones = 0, threes = 0, diagonals = 0;
1640 for (
int j = -1; j < height_; ++j) {
1641 const std::uint64_t* lower = j >= 0 ? row(j) : zeros.data();
1642 const std::uint64_t* upper = j + 1 < height_ ? row(j + 1) : zeros.data();
1643 for (std::size_t w = 0; w < blockWords; ++w) {
1644 const std::int64_t base =
static_cast<std::int64_t
>(w) * 64 - 1;
1645 const std::uint64_t a = shiftedWord(lower, words_, base);
1646 const std::uint64_t b = shiftedWord(lower, words_, base + 1);
1647 const std::uint64_t c = shiftedWord(upper, words_, base);
1648 const std::uint64_t d = shiftedWord(upper, words_, base + 1);
1649 const std::uint64_t valid = w + 1 == blockWords ? lastMask : ~std::uint64_t(0);
1650 const std::uint64_t one = (a & ~b & ~c & ~d) | (~a & b & ~c & ~d) |
1651 (~a & ~b & c & ~d) | (~a & ~b & ~c & d);
1652 const std::uint64_t three = (~a & b & c & d) | (a & ~b & c & d) |
1653 (a & b & ~c & d) | (a & b & c & ~d);
1654 const std::uint64_t diagonal = (a & ~b & ~c & d) | (~a & b & c & ~d);
1655 ones += std::popcount(one & valid);
1656 threes += std::popcount(three & valid);
1657 diagonals += std::popcount(diagonal & valid);
1661 return (ones - threes + sign * diagonals) / 4;
1673 bool changed =
false;
1674 for (
int j = 0; j < height_; ++j) {
1675 if (
const std::optional<std::pair<std::int64_t, std::int64_t>> extent = rowExtent(j)) {
1676 changed |= setLocalRange(j, extent->first, extent->second + 1);
1693 std::vector<std::uint64_t> below(bits_.size(), 0);
1694 for (
int j = 1; j < height_; ++j) {
1695 for (std::size_t w = 0; w < words_; ++w) {
1696 below[
static_cast<std::size_t
>(j) * words_ + w] =
1697 below[
static_cast<std::size_t
>(j - 1) * words_ + w] | row(j - 1)[w];
1701 bool changed =
false;
1702 std::vector<std::uint64_t> above(words_, 0);
1703 for (
int j = height_ - 1; j >= 0; --j) {
1704 std::uint64_t* here = row(j);
1705 for (std::size_t w = 0; w < words_; ++w) {
1706 const std::uint64_t original = here[w];
1707 const std::uint64_t
fill =
1708 above[w] & below[
static_cast<std::size_t
>(j) * words_ + w] & ~original;
1710 above[w] |= original;
1711 changed |=
fill != 0;
1728 const std::size_t before =
count();
1731 for (
bool changed =
true; changed;) {
1734 changed = rows || columns;
1736 return count() - before;
1746 for (
int j = 0; j < height_; ++j) {
1747 const std::optional<std::pair<std::int64_t, std::int64_t>> extent = rowExtent(j);
1748 if (extent &&
static_cast<std::int64_t
>(countRow(j)) !=
1749 extent->second - extent->first + 1) {
1764 std::vector<std::uint64_t> seen(words_, 0), closed(words_, 0);
1765 for (
int j = 0; j < height_; ++j) {
1766 const std::uint64_t* here = row(j);
1767 for (std::size_t w = 0; w < words_; ++w) {
1768 if ((here[w] & closed[w]) != 0) {
1771 closed[w] |= seen[w] & ~here[w];
1815 return owner_ == other.owner_ && (owner_ ==
nullptr || (rowIndex_ == other.rowIndex_ &&
1816 wordIndex_ == other.wordIndex_ &&
1817 rest_ == other.rest_));
1827 if (owner_->words_ == 0) {
1831 rest_ = owner_->row(0)[0];
1838 const int bit = std::countr_zero(rest_);
1841 owner_->origin_.x() +
1842 static_cast<NumberType>(
static_cast<std::int64_t
>(wordIndex_) * 64 + bit),
1843 owner_->origin_.y() +
static_cast<NumberType>(rowIndex_));
1847 if (wordIndex_ >= owner_->words_) {
1851 if (rowIndex_ >= owner_->height_) {
1855 rest_ = owner_->row(rowIndex_)[wordIndex_];
1861 std::size_t wordIndex_ = 0;
1862 std::uint64_t rest_ = 0;
1878 static constexpr std::array<std::pair<int, int>, 4> boundarySteps{
1879 {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}};
1887 static std::size_t wordsPerRow(
int width) {
1888 return static_cast<std::size_t
>((
width + 63) / 64);
1895 const std::uint64_t span =
1896 static_cast<std::uint64_t
>(high) -
static_cast<std::uint64_t
>(low);
1897 if (span >=
static_cast<std::uint64_t
>(detail::numeric_limits<int>::max())) {
1898 throw std::logic_error(
"pgl::BitMatrix: the points do not fit a window");
1900 return static_cast<int>(span) + 1;
1904 template <
class Range>
1905 static BitMatrix emptyOver(Range&& points) {
1906 auto it = std::ranges::begin(points);
1907 const auto last = std::ranges::end(points);
1913 for (++it; it != last; ++it) {
1914 minX = std::min<NumberType>(minX, (*it).x());
1915 maxX = std::max<NumberType>(maxX, (*it).x());
1916 minY = std::min<NumberType>(minY, (*it).y());
1917 maxY = std::max<NumberType>(maxY, (*it).y());
1922 std::uint64_t* row(
int j) {
return bits_.data() +
static_cast<std::size_t
>(j) * words_; }
1923 const std::uint64_t* row(
int j)
const {
1924 return bits_.data() +
static_cast<std::size_t
>(j) * words_;
1927 [[nodiscard]] std::int64_t localX(
NumberType x)
const {
1928 return static_cast<std::int64_t
>(
x) -
static_cast<std::int64_t
>(origin_.x());
1930 [[nodiscard]] std::int64_t localY(
NumberType y)
const {
1931 return static_cast<std::int64_t
>(
y) -
static_cast<std::int64_t
>(origin_.y());
1934 [[nodiscard]]
bool localGet(
int j, std::int64_t i)
const {
1935 return ((row(j)[
static_cast<std::size_t
>(i) / 64] >> (i % 64)) & 1) != 0;
1938 [[nodiscard]] std::size_t countRow(
int j)
const {
1939 std::size_t total = 0;
1940 for (std::size_t w = 0; w < words_; ++w) {
1941 total +=
static_cast<std::size_t
>(std::popcount(row(j)[w]));
1952 [[nodiscard]] std::optional<std::pair<std::int64_t, std::int64_t>> rowExtent(
int j)
const {
1953 const std::uint64_t* here = row(j);
1954 std::size_t first = 0;
1955 while (first < words_ && here[first] == 0) {
1958 if (first == words_) {
1959 return std::nullopt;
1961 std::size_t last = words_ - 1;
1962 while (here[last] == 0) {
1965 return std::pair{
static_cast<std::int64_t
>(first) * 64 + std::countr_zero(here[first]),
1966 static_cast<std::int64_t
>(last) * 64 + 63 - std::countl_zero(here[last])};
1970 using Crossings = std::vector<std::vector<NumberType>>;
1981 template <
class Ring>
1982 void addRingCrossings(
const Ring& ring, Crossings& crossings)
const {
1983 for (std::size_t i = 0; i < ring.size(); ++i) {
1985 const PointType q = ring[(i + 1) % ring.size()];
1986 if (p.x() == q.x()) {
1987 for (
NumberType y = std::min(p.y(), q.y());
y < std::max(p.y(), q.y()); ++
y) {
1988 crossings[
static_cast<std::size_t
>(
y - origin_.y())].push_back(p.x());
1990 }
else if (p.y() != q.y()) {
1991 throw std::logic_error(
"pgl::BitMatrix: the shape is not rectilinear");
1997 template <
class Region>
1998 void addRegionCrossings(
const Region& region, Crossings& crossings)
const {
1999 addRingCrossings(region.outer(), crossings);
2000 for (
const Polygon<PointType>& hole : region.holes()) {
2001 addRingCrossings(hole, crossings);
2009 void fillCrossings(Crossings& crossings) {
2010 for (
int j = 0; j < height_; ++j) {
2011 std::vector<NumberType>& crossingsInRow = crossings[
static_cast<std::size_t
>(j)];
2012 std::sort(crossingsInRow.begin(), crossingsInRow.end());
2013 for (std::size_t i = 0; i + 1 < crossingsInRow.size(); i += 2) {
2014 setLocalRange(j,
static_cast<std::int64_t
>(crossingsInRow[i] - origin_.x()),
2015 static_cast<std::int64_t
>(crossingsInRow[i + 1] - origin_.x()));
2025 bool setLocalRange(
int j, std::int64_t low, std::int64_t high) {
2026 low = std::max<std::int64_t>(low, 0);
2027 high = std::min<std::int64_t>(high, width_);
2028 bool changed =
false;
2029 for (std::int64_t i = low; i < high;) {
2030 const std::size_t w =
static_cast<std::size_t
>(i) / 64;
2031 const int offset =
static_cast<int>(i % 64);
2032 const int bits =
static_cast<int>(std::min<std::int64_t>(64 - offset, high - i));
2033 const std::uint64_t mask =
2034 bits == 64 ? ~std::uint64_t(0) : ((std::uint64_t(1) << bits) - 1) << offset;
2035 changed |= (row(j)[w] & mask) != mask;
2043 static void clearWordRange(std::uint64_t* here, std::int64_t low, std::int64_t high) {
2044 for (std::int64_t i = low; i < high;) {
2045 const std::size_t w =
static_cast<std::size_t
>(i) / 64;
2046 const int offset =
static_cast<int>(i % 64);
2047 const int bits =
static_cast<int>(std::min<std::int64_t>(64 - offset, high - i));
2048 const std::uint64_t mask =
2049 bits == 64 ? ~std::uint64_t(0) : ((std::uint64_t(1) << bits) - 1) << offset;
2068 [[nodiscard]] std::int64_t nextSetInRow(
const std::uint64_t* here, std::int64_t from)
const {
2069 assert(from >= 0 && from <= width_);
2070 if (from >= width_) {
2073 std::size_t w =
static_cast<std::size_t
>(from) / 64;
2074 std::uint64_t rest = here[w] & (~std::uint64_t(0) << (from % 64));
2076 if (++w >= words_) {
2081 return static_cast<std::int64_t
>(w) * 64 + std::countr_zero(rest);
2091 [[nodiscard]] std::int64_t nextClearInRow(
const std::uint64_t* here, std::int64_t from)
const {
2092 assert(from >= 0 && from < width_);
2093 std::size_t w =
static_cast<std::size_t
>(from) / 64;
2094 std::uint64_t rest = ~here[w] & (~std::uint64_t(0) << (from % 64));
2096 if (++w >= words_) {
2101 return std::min<std::int64_t>(
2102 static_cast<std::int64_t
>(w) * 64 + std::countr_zero(rest), width_);
2110 [[nodiscard]] std::int64_t runStartInRow(
const std::uint64_t* here, std::int64_t at)
const {
2111 assert(at >= 0 && at < width_ && ((here[
static_cast<std::size_t
>(at) / 64] >>
2112 (at % 64)) & 1) != 0);
2113 std::size_t w =
static_cast<std::size_t
>(at) / 64;
2114 const int bit =
static_cast<int>(at % 64);
2115 std::uint64_t rest = ~here[w] & (~std::uint64_t(0) >> (63 - bit));
2122 return static_cast<std::int64_t
>(w) * 64 + 64 - std::countl_zero(rest);
2132 const int tail = width_ % 64;
2136 const std::uint64_t mask = (std::uint64_t(1) << tail) - 1;
2137 for (
int j = 0; j < height_; ++j) {
2138 row(j)[words_ - 1] &= mask;
2142 static std::uint64_t wordAt(
const std::uint64_t* words, std::size_t
count, std::int64_t index) {
2143 return index < 0 || index >=
static_cast<std::int64_t
>(
count) ? 0
2144 : words[static_cast<std::size_t>(index)];
2154 static std::uint64_t shiftedWord(
const std::uint64_t* words, std::size_t
count,
2155 std::int64_t offset) {
2156 const std::int64_t index = offset >= 0 ? offset / 64 : -((-offset + 63) / 64);
2157 const int rest =
static_cast<int>(offset - index * 64);
2158 const std::uint64_t low = wordAt(words,
count, index) >> rest;
2162 return low | (wordAt(words,
count, index + 1) << (64 - rest));
2166 template <
class WordOp>
2167 void combine(
const BitMatrix& other, WordOp op) {
2168 const std::int64_t shift =
2169 static_cast<std::int64_t
>(other.origin_.x()) -
static_cast<std::int64_t
>(origin_.x());
2170 for (
int j = 0; j < height_; ++j) {
2171 const std::int64_t sourceRow =
static_cast<std::int64_t
>(origin_.y()) + j -
2172 static_cast<std::int64_t
>(other.origin_.y());
2173 const bool inside = sourceRow >= 0 && sourceRow < other.height_;
2174 const std::uint64_t* source = inside ? other.row(
static_cast<int>(sourceRow)) :
nullptr;
2175 std::uint64_t* destination = row(j);
2176 for (std::size_t w = 0; w < words_; ++w) {
2177 const std::uint64_t word =
2180 : shiftedWord(source, other.words_,
static_cast<std::int64_t
>(w) * 64 - shift);
2181 destination[w] = op(destination[w], word);
2189 void forEachAlignedWord(
const BitMatrix& other, Fn fn)
const {
2190 const std::int64_t shift =
2191 static_cast<std::int64_t
>(other.origin_.x()) -
static_cast<std::int64_t
>(origin_.x());
2192 for (
int j = 0; j < height_; ++j) {
2193 const std::int64_t sourceRow =
static_cast<std::int64_t
>(origin_.y()) + j -
2194 static_cast<std::int64_t
>(other.origin_.y());
2195 if (sourceRow < 0 || sourceRow >= other.height_) {
2198 const std::uint64_t* source = other.row(
static_cast<int>(sourceRow));
2199 const std::uint64_t* here = row(j);
2200 for (std::size_t w = 0; w < words_; ++w) {
2204 fn(here[w], shiftedWord(source, other.words_,
static_cast<std::int64_t
>(w) * 64 - shift));
2216 [[nodiscard]]
bool anyCommonCell(
const BitMatrix& other, std::int64_t dx, std::int64_t dy)
const {
2217 const std::int64_t shift =
static_cast<std::int64_t
>(other.origin_.x()) + dx -
2218 static_cast<std::int64_t
>(origin_.x());
2219 for (
int j = 0; j < height_; ++j) {
2220 const std::int64_t sourceRow =
static_cast<std::int64_t
>(origin_.y()) + j -
2221 static_cast<std::int64_t
>(other.origin_.y()) - dy;
2222 if (sourceRow < 0 || sourceRow >= other.height_) {
2225 const std::uint64_t* source = other.row(
static_cast<int>(sourceRow));
2226 const std::uint64_t* here = row(j);
2227 for (std::size_t w = 0; w < words_; ++w) {
2231 if ((here[w] & shiftedWord(source, other.words_,
2232 static_cast<std::int64_t
>(w) * 64 - shift)) != 0) {
2246 void orShifted(
const BitMatrix& source, std::int64_t dx, std::int64_t dy) {
2247 assert(dx >= 0 && dy >= 0);
2248 assert(dy + source.height_ <= height_);
2249 const std::size_t wordShift =
static_cast<std::size_t
>(dx / 64);
2250 const int bitShift =
static_cast<int>(dx % 64);
2251 for (
int j = 0; j < source.height_; ++j) {
2252 const std::uint64_t* from = source.row(j);
2253 std::uint64_t* to = row(j +
static_cast<int>(dy));
2254 for (std::size_t w = 0; w < source.words_; ++w) {
2255 const std::uint64_t word = from[w];
2259 if (w + wordShift < words_) {
2260 to[w + wordShift] |= word << bitShift;
2262 if (bitShift != 0 && w + wordShift + 1 < words_) {
2263 to[w + wordShift + 1] |= word >> (64 - bitShift);
2270 void andShifted(
const BitMatrix& source, std::int64_t dx, std::int64_t dy) {
2271 for (
int j = 0; j < height_; ++j) {
2272 const std::int64_t sourceRow =
static_cast<std::int64_t
>(j) + dy;
2273 std::uint64_t* to = row(j);
2274 if (sourceRow < 0 || sourceRow >= source.height_) {
2275 std::fill(to, to + words_, std::uint64_t(0));
2278 const std::uint64_t* from = source.row(
static_cast<int>(sourceRow));
2279 for (std::size_t w = 0; w < words_; ++w) {
2280 to[w] &= shiftedWord(from, source.words_,
static_cast<std::int64_t
>(w) * 64 + dx);
2286 template <
class Map>
2287 [[nodiscard]]
BitMatrix mapped(Map map)
const {
2291 const std::int64_t x0 =
static_cast<std::int64_t
>(origin_.x());
2292 const std::int64_t y0 =
static_cast<std::int64_t
>(origin_.y());
2293 const std::int64_t x1 = x0 + width_ - 1, y1 = y0 + height_ - 1;
2294 std::int64_t minX = 0, maxX = 0, minY = 0, maxY = 0;
2296 for (
const std::int64_t
x : {x0, x1}) {
2297 for (
const std::int64_t
y : {y0, y1}) {
2298 const auto [imageX, imageY] = map(
x,
y);
2299 minX = first ? imageX : std::min(minX, imageX);
2300 maxX = first ? imageX : std::max(maxX, imageX);
2301 minY = first ? imageY : std::min(minY, imageY);
2302 maxY = first ? imageY : std::max(maxY, imageY);
2307 static_cast<int>(maxX - minX) + 1,
static_cast<int>(maxY - minY) + 1);
2309 const auto [imageX, imageY] =
2310 map(
static_cast<std::int64_t
>(cell.x()),
static_cast<std::int64_t
>(cell.y()));
2317 static constexpr std::array<std::pair<std::int64_t, std::int64_t>, 8> allNeighborOffsets{
2318 {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}}};
2320 static std::span<const std::pair<std::int64_t, std::int64_t>> neighborOffsets(
2322 return std::span(allNeighborOffsets)
2333 [[nodiscard]]
static BitMatrix unitSquareSum() {
2344 [[nodiscard]] std::optional<PointType> firstSetCell()
const {
2345 for (
int j = 0; j < height_; ++j) {
2346 const std::uint64_t* here = row(j);
2347 for (std::size_t w = 0; w < words_; ++w) {
2350 origin_.x() +
static_cast<NumberType>(
static_cast<std::int64_t
>(w) * 64 +
2351 std::countr_zero(here[w])),
2356 return std::nullopt;
2373 void floodRuns(std::uint64_t* remaining,
int j, std::int64_t i, std::int64_t reach,
2374 std::vector<CellRun>& runs)
const {
2375 const std::size_t first = runs.size();
2376 auto rowOf = [&](
int at) {
return remaining +
static_cast<std::size_t
>(at) * words_; };
2377 auto take = [&](
int at, std::int64_t cell) {
2378 std::uint64_t* here = rowOf(at);
2379 const CellRun run{at, runStartInRow(here, cell), nextClearInRow(here, cell)};
2380 clearWordRange(here, run.x0, run.x1);
2381 runs.push_back(run);
2384 auto scan = [&](
int at, std::int64_t low, std::int64_t high) {
2385 if (at < 0 || at >= height_) {
2388 const std::uint64_t* here = rowOf(at);
2389 high = std::min<std::int64_t>(high, width_);
2390 for (std::int64_t cell = nextSetInRow(here, std::max<std::int64_t>(low, 0)); cell < high;
2391 cell = nextSetInRow(here, cell)) {
2392 cell = take(at, cell).x1;
2397 for (std::size_t next = first; next < runs.size(); ++next) {
2398 const CellRun run = runs[next];
2399 scan(run.y - 1, run.x0 - reach, run.x1 + reach);
2400 scan(run.y + 1, run.x0 - reach, run.x1 + reach);
2406 BitMatrix result(origin_, width_, height_);
2410 std::vector<std::uint64_t> remaining = bits_;
2411 std::vector<CellRun> runs;
2412 floodRuns(remaining.data(),
static_cast<int>(localY(seed.y())), localX(seed.x()),
2414 for (
const CellRun& run : runs) {
2415 result.setLocalRange(run.y, run.x0, run.x1);
2430 void visitComponentRuns(
GridAdjacency adjacency, Fn fn)
const {
2434 std::vector<std::uint64_t> remaining = bits_;
2437 std::vector<CellRun> runs;
2438 for (
int j = 0; j < height_; ++j) {
2439 const std::uint64_t* here = remaining.data() +
static_cast<std::size_t
>(j) * words_;
2440 for (std::int64_t i = nextSetInRow(here, 0); i < width_; i = nextSetInRow(here, i)) {
2442 floodRuns(remaining.data(), j, i, reach, runs);
2443 fn(std::as_const(runs));
2481 template <
class PinchCrosses>
2482 [[nodiscard]] std::vector<std::vector<detail::PolyCell>> boundaryLoops(
2483 PinchCrosses crosses)
const {
2484 std::vector<std::vector<detail::PolyCell>> loops;
2488 const std::size_t vertexWords = wordsPerRow(width_ + 1);
2489 const std::size_t vertexRows =
static_cast<std::size_t
>(height_) + 1;
2490 const std::size_t plane = vertexRows * vertexWords;
2491 std::vector<std::uint64_t> present(4 * plane, 0);
2492 std::vector<std::uint64_t> used(4 * plane, 0);
2494 auto cellWord = [&](
int j, std::size_t w) -> std::uint64_t {
2495 return j < 0 || j >= height_ || w >= words_ ? 0 : row(j)[w];
2499 std::vector<std::uint64_t> rawNorth(vertexWords), rawWest(vertexWords);
2500 for (std::size_t vertexRow = 0; vertexRow < vertexRows; ++vertexRow) {
2501 const int above =
static_cast<int>(vertexRow);
2502 const int below = above - 1;
2503 for (std::size_t w = 0; w < vertexWords; ++w) {
2504 const std::uint64_t upper = cellWord(above, w);
2505 const std::uint64_t lower = cellWord(below, w);
2506 const std::uint64_t upperRight = (upper >> 1) | (cellWord(above, w + 1) << 63);
2507 const std::uint64_t lowerLeft =
2508 (lower << 1) | (w == 0 ? 0 : cellWord(below, w - 1) >> 63);
2509 present[0 * plane + vertexRow * vertexWords + w] = upper & ~lower;
2510 present[3 * plane + vertexRow * vertexWords + w] = lower & ~lowerLeft;
2511 rawNorth[w] = upper & ~upperRight;
2512 rawWest[w] = lower & ~upper;
2514 std::uint64_t carryNorth = 0, carryWest = 0;
2515 for (std::size_t w = 0; w < vertexWords; ++w) {
2516 present[1 * plane + vertexRow * vertexWords + w] = (rawNorth[w] << 1) | carryNorth;
2517 present[2 * plane + vertexRow * vertexWords + w] = (rawWest[w] << 1) | carryWest;
2518 carryNorth = rawNorth[w] >> 63;
2519 carryWest = rawWest[w] >> 63;
2523 auto index = [&](
int direction,
int y,
int x) {
2524 return static_cast<std::size_t
>(direction) * plane +
2525 static_cast<std::size_t
>(
y) * vertexWords +
static_cast<std::size_t
>(
x) / 64;
2527 auto hasEdge = [&](
int direction,
int y,
int x) {
2528 return ((present[index(direction,
y,
x)] >> (
x % 64)) & 1) != 0;
2530 auto markUsed = [&](
int direction,
int y,
int x) {
2531 used[index(direction,
y,
x)] |= std::uint64_t(1) << (
x % 64);
2534 auto trace = [&](
int direction,
int startX,
int startY) {
2535 std::vector<detail::PolyCell> loop;
2536 int x = startX,
y = startY;
2538 markUsed(direction,
y,
x);
2539 loop.emplace_back(
x,
y);
2540 x += boundarySteps[
static_cast<std::size_t
>(direction)].first;
2541 y += boundarySteps[
static_cast<std::size_t
>(direction)].second;
2542 if (
x == startX &&
y == startY) {
2545 int next = (direction + 3) % 4;
2546 const int left = (direction + 1) % 4;
2547 if (hasEdge(next,
y,
x) && hasEdge(left,
y,
x)) {
2550 const auto& step = boundarySteps[
static_cast<std::size_t
>(direction)];
2551 if (!crosses(leftCell(direction,
x - step.first,
y - step.second),
2552 leftCell(next,
x,
y))) {
2557 for (
int turn = 0; turn < 3 && !hasEdge(next,
y,
x); ++turn) {
2558 next = (next + 1) % 4;
2561 assert(hasEdge(next,
y,
x) &&
"pgl::BitMatrix: the boundary walk ran off an edge");
2567 for (
int direction = 0; direction < 4; ++direction) {
2568 for (
int y = 0;
y <= height_; ++
y) {
2569 for (std::size_t w = 0; w < vertexWords; ++w) {
2570 const std::size_t at =
2571 static_cast<std::size_t
>(direction) * plane +
2572 static_cast<std::size_t
>(
y) * vertexWords + w;
2573 while (
const std::uint64_t rest = present[at] & ~used[at]) {
2574 loops.push_back(trace(
2575 direction,
static_cast<int>(w) * 64 + std::countr_zero(rest),
y));
2584 static detail::PolyCell leftCell(
int direction,
int x,
int y) {
2585 switch (direction) {
2591 return {
x - 1,
y - 1};
2598 static detail::PolyCell loopSeedCell(
const std::vector<detail::PolyCell>& loop) {
2599 const auto [
x,
y] = loop.front();
2600 const auto [nextX, nextY] = loop[1];
2601 return leftCell(nextX >
x ? 0 : nextY >
y ? 1 : nextX <
x ? 2 : 3,
x,
y);
2613 const std::vector<std::vector<detail::PolyCell>>& loops)
const {
2615 std::vector<Polygon<Point<NumberType>>> holes;
2616 for (
const std::vector<detail::PolyCell>& loop : loops) {
2617 if (detail::loopTwiceArea(loop) > 0) {
2620 holes.emplace_back(detail::loopCorners<NumberType>(loop));
2629 if (left.emptyWindow()) {
2630 return right.window();
2632 if (right.emptyWindow()) {
2633 return left.window();
2637 PointType(std::max(a.max().x(), b.max().x()), std::max(a.max().y(), b.max().y())),
2642 if (left.emptyWindow() || right.emptyWindow()) {
2647 PointType(std::min(a.max().x(), b.max().x()), std::min(a.max().y(), b.max().y())),
2654 std::size_t words_ = 0;
2655 std::vector<std::uint64_t> bits_;
2659template <
class Po
intType>
2667template <
class Po
intType>
2670template <
class Po
intType,
class LabelType>
2673template <
class Po
intType,
class LabelType>
2676template <
class Po
intType,
class LabelType>
2679template <
class Po
intType,
class LabelType>
2685template <std::ranges::input_range Range>
2686 requires(detail::is_point_v<std::remove_cvref_t<std::ranges::range_value_t<Range>>>
2688 && !detail::is_bit_matrix_v<std::remove_cvref_t<Range>>)
2695template <
class Po
intType_,
class TLabel>
2696template <
class ResultNumber>
2697 requires(std::signed_integral<ResultNumber>)
2702template <
class Po
intType_,
class TLabel>
2703template <
class ResultNumber>
2704 requires(std::signed_integral<ResultNumber>)
2709template <
class Po
intType_,
class TLabel>
2710template <
class ResultNumber>
2711 requires(std::signed_integral<ResultNumber>)
2719template <
class Po
intType,
class Predicate>
2721 using Number =
typename PointType::NumberType;
2723 for (
int j = 0; j < result.height(); ++j) {
2724 for (
int i = 0; i < result.width(); ++i) {
2725 const Number
x = result.origin().x() +
static_cast<Number
>(i);
2726 const Number
y = result.origin().y() +
static_cast<Number
>(j);
2728 PointType(
x + Number(1),
y + Number(1)),
true))) {
2751template <
class Po
intType,
class ShapeType>
2753 return detail::rasterize<PointType>(
2767template <
class Po
intType,
class ShapeType>
2769 return detail::rasterize<PointType>(
2779template <
class Po
intType = Po
int<
int>,
class ShapeType>
2780 requires std::signed_integral<
2781 std::remove_cvref_t<decltype(std::declval<const ShapeType&>().
bbox().
min().x())>>
2792template <
class Po
intType = Po
int<
int>,
class ShapeType>
2793 requires std::signed_integral<
2794 std::remove_cvref_t<decltype(std::declval<const ShapeType&>().
bbox().
min().x())>>
2812template <
class Po
intType>
2815 std::size_t seed = 0;
2816 pgl::detail::hashCombine(seed, matrix.
origin());
2817 pgl::detail::hashCombine(seed, matrix.
width());
2818 pgl::detail::hashCombine(seed, matrix.
height());
2819 for (
const std::uint64_t word : matrix.bits_) {
2820 pgl::detail::hashCombine(seed, word);
Forward iterator over the set cells, in row-major order.
Definition bitmatrix.hpp:1784
Iterator operator++(int)
Advances to the next set cell, returning the previous one.
Definition bitmatrix.hpp:1807
Iterator & operator++()
Advances to the next set cell.
Definition bitmatrix.hpp:1801
const PointType & reference
Definition bitmatrix.hpp:1790
const PointType * pointer
Definition bitmatrix.hpp:1789
bool operator==(const Iterator &other) const
Whether two iterators are on the same cell of the same matrix.
Definition bitmatrix.hpp:1814
reference operator*() const
The cell the iterator is on.
Definition bitmatrix.hpp:1795
std::ptrdiff_t difference_type
Definition bitmatrix.hpp:1788
std::forward_iterator_tag iterator_category
Definition bitmatrix.hpp:1786
pointer operator->() const
The cell the iterator is on.
Definition bitmatrix.hpp:1798
PointType value_type
Definition bitmatrix.hpp:1787
friend class BitMatrix
Definition bitmatrix.hpp:1824
bool operator!=(const Iterator &other) const
Whether two iterators are on different cells.
Definition bitmatrix.hpp:1821
A bit per cell of a rectangular window of the integer grid.
Definition bitmatrix.hpp:222
void flip(Range &&points)
Flips one cell per point of a range; cells outside the window are dropped.
Definition bitmatrix.hpp:639
Point< ResultNumber > centroid() const
Centroid of the covered region.
Definition bitmatrix.hpp:727
void set(NumberType x, NumberType y)
Sets the cell; a cell outside the window is silently dropped.
Definition bitmatrix.hpp:523
Iterator iterator
Iteration is read-only, so both iterator types are Iterator.
Definition bitmatrix.hpp:241
Convex< PointType > ConvexType
Convex type convexHull produces.
Definition bitmatrix.hpp:235
RegionType asPolygonWithHoles() const
The covered region, as one region with holes.
Definition bitmatrix.hpp:932
RectangleType bbox() const
The rectangle the set cells cover, empty when no cell is set.
Definition bitmatrix.hpp:865
int height() const
Number of rows of the window.
Definition bitmatrix.hpp:460
std::size_t count() const
Number of set cells.
Definition bitmatrix.hpp:668
bool boundaryContains(const BitMatrix &other) const
Whether the boundary of the covered region contains the other one.
Definition bitmatrix.hpp:1193
bool inWindow(const PointType &cell) const
Whether a cell is inside the window, and so can be set.
Definition bitmatrix.hpp:481
void clear()
Clears every cell, keeping the window.
Definition bitmatrix.hpp:652
PointType value_type
Cell type the iterators yield.
Definition bitmatrix.hpp:237
bool fillRows()
Fills the gaps of every row.
Definition bitmatrix.hpp:1672
void reset(const PointType &cell)
Clears the cell; a cell outside the window is silently dropped.
Definition bitmatrix.hpp:582
void setAll()
Sets every cell of the window.
Definition bitmatrix.hpp:646
BitMatrix latticeTransposed() const
Returns {(y, x)}; the same as transposed, which the two readings agree on.
Definition bitmatrix.hpp:1367
PolygonSet< PointType > PolygonSetType
Region-set type asPolygonSet produces.
Definition bitmatrix.hpp:233
BitMatrix minkowskiSum(const BitMatrix &other) const
Returns the Minkowski sum of the two covered regions.
Definition bitmatrix.hpp:1435
Iterator begin() const
Iterator over the set lattice points, in row-major order.
Definition bitmatrix.hpp:765
bool fillColumns()
Fills the gaps of every column.
Definition bitmatrix.hpp:1687
BitMatrix(const Polygon< PointType, TLabel > &polygon)
Rasterizes a rectilinear polygon, one bit per covered cell.
Definition bitmatrix.hpp:342
Point< ResultNumber > pointInside() const
A point in the interior of the covered region.
Definition bitmatrix.hpp:914
bool interiorsIntersect(const BitMatrix &other) const
Whether the interiors of the two covered regions share a point.
Definition bitmatrix.hpp:1220
void set(const PointType &cell)
Sets the cell; a cell outside the window is silently dropped.
Definition bitmatrix.hpp:532
BitMatrix operator~() const
Returns the complement within the same window.
Definition bitmatrix.hpp:1065
BitMatrix rotated90(int k=1) const
Returns the rotation of the covered region about the origin.
Definition bitmatrix.hpp:1322
BitMatrix & rotate90(int k=1)
Rotates the covered region about the origin.
Definition bitmatrix.hpp:1339
BitMatrix fillHoles(GridAdjacency adjacency=GridAdjacency::edge) const
Returns the cells together with every hole they enclose.
Definition bitmatrix.hpp:1600
ResultNumber area() const
Area the set cells cover, which is their number since each is a unit square.
Definition bitmatrix.hpp:683
BitMatrix reflected() const
Returns the reflection of the covered region through the origin.
Definition bitmatrix.hpp:1288
BitMatrix difference(const BitMatrix &other) const
The cells this matrix has and the other does not, over this window.
Definition bitmatrix.hpp:1114
void set(NumberType x, NumberType y, bool value)
Sets or clears the cell; a cell outside the window is dropped.
Definition bitmatrix.hpp:535
bool get(const PointType &cell) const
Whether the cell is set; cells outside the window are not.
Definition bitmatrix.hpp:520
BitMatrix minkowskiErosion(const BitMatrix &other) const
Returns the regularized Minkowski erosion of the covered regions.
Definition bitmatrix.hpp:1469
RectangleType window() const
The window, as the rectangle its cells cover.
Definition bitmatrix.hpp:463
BitMatrix operator^(const BitMatrix &other) const
The cells exactly one matrix has, over the hull of the windows.
Definition bitmatrix.hpp:1107
BitMatrix operator+(const BitMatrix &other) const
Returns the Minkowski sum of the regions; the same as minkowskiSum.
Definition bitmatrix.hpp:1443
BitMatrix & operator&=(const BitMatrix &other)
Drops every cell the other matrix does not have.
Definition bitmatrix.hpp:1075
bool isConnected(GridAdjacency adjacency=GridAdjacency::edge) const
Whether the set cells form exactly one connected group.
Definition bitmatrix.hpp:1585
ConvexType convexHull() const
Convex hull of the covered region.
Definition bitmatrix.hpp:1013
BitMatrix latticeMinkowskiErosion(const BitMatrix &other) const
Returns the Minkowski erosion {p : p + other is inside *this}.
Definition bitmatrix.hpp:1483
BitMatrix(Range &&points)
Sets one cell per point of a range, over the smallest window holding them.
Definition bitmatrix.hpp:430
std::size_t makeHvConvex()
Fills every cell that has set cells on both sides in its row and in its column, until nothing changes...
Definition bitmatrix.hpp:1727
typename PointType::NumberType NumberType
Coordinate type of a cell.
Definition bitmatrix.hpp:227
BitMatrix resized(const RectangleType &box) const
Returns the same cells over another window, dropping those outside.
Definition bitmatrix.hpp:493
BitMatrix latticeMinkowskiSum(const BitMatrix &other) const
Returns the Minkowski sum {a + b : a in *this, b in other}.
Definition bitmatrix.hpp:1403
BitMatrix reflectedY() const
Returns the reflection of the covered region across the y-axis.
Definition bitmatrix.hpp:1301
bool inWindow(NumberType x, NumberType y) const
Whether a cell is inside the window, and so can be set.
Definition bitmatrix.hpp:475
bool isRowConvex() const
Whether every row meets the set cells in a single interval.
Definition bitmatrix.hpp:1745
int width() const
Number of cells per row of the window.
Definition bitmatrix.hpp:457
BitMatrix symmetricDifference(const BitMatrix &other) const
The cells exactly one matrix has; the same as operator^.
Definition bitmatrix.hpp:1121
BitMatrix(const PolygonWithHoles< OtherPointType, TLabel > ®ion)
Rasterizes a rectilinear region given over another coordinate type.
Definition bitmatrix.hpp:327
std::vector< PointType > lattice() const
The set cells as lattice points, in row-major order.
Definition bitmatrix.hpp:804
BitMatrix latticeOpening(const BitMatrix &other) const
Returns the opening, a lattice erosion by other followed by a lattice sum.
Definition bitmatrix.hpp:1506
BitMatrix latticeRotated90(int k=1) const
Returns the rotation of the cells as lattice points.
Definition bitmatrix.hpp:1374
BitMatrix & operator^=(const BitMatrix &other)
Flips every cell the other matrix has and this window holds.
Definition bitmatrix.hpp:1087
PolygonWithHoles< PointType > RegionType
Region type asPolygonWithHoles produces.
Definition bitmatrix.hpp:231
BitMatrix operator-(const PointType &vector) const
Returns the same cells translated by the opposite of a vector.
Definition bitmatrix.hpp:1264
BitMatrix(const Polygon< OtherPointType, TLabel > &polygon)
Rasterizes a rectilinear polygon given over another coordinate type.
Definition bitmatrix.hpp:361
BitMatrix latticeReflectedX() const
Returns the reflection of the cells as lattice points, {(x, -y)}.
Definition bitmatrix.hpp:1357
BitMatrix reflectedX() const
Returns the reflection of the covered region across the x-axis.
Definition bitmatrix.hpp:1296
BitMatrix(PointType origin, int width, int height)
Creates an empty matrix covering a window of the grid.
Definition bitmatrix.hpp:259
BitMatrix interior(GridAdjacency adjacency=GridAdjacency::edge) const
Returns the set cells all of whose neighbors are set.
Definition bitmatrix.hpp:1523
PolygonSetType asPolygonSet() const
The covered region, as a set of regions with holes.
Definition bitmatrix.hpp:955
friend Canvas & operator<<(Canvas &canvas, const BitMatrix &matrix)
Draws the covered region to a canvas.
Definition bitmatrix.hpp:1044
bool isColumnConvex() const
Whether every column meets the set cells in a single interval.
Definition bitmatrix.hpp:1763
bool isHvConvex() const
Whether every row and every column meets the cells in one interval.
Definition bitmatrix.hpp:1779
const PointType & origin() const
Lower-left cell of the window.
Definition bitmatrix.hpp:454
BitMatrix & latticeRotate90(int k=1)
Rotates the cells as lattice points.
Definition bitmatrix.hpp:1391
void flip(NumberType x, NumberType y)
Flips the cell; a cell outside the window is silently dropped.
Definition bitmatrix.hpp:609
std::size_t xorCount(const BitMatrix &other) const
Number of cells set in exactly one matrix.
Definition bitmatrix.hpp:1243
std::strong_ordering operator<=>(const BitMatrix &other) const
Orders matrices lexicographically by (origin, width, height, bits).
Definition bitmatrix.hpp:1143
std::size_t andCount(const BitMatrix &other) const
Number of cells set in both matrices.
Definition bitmatrix.hpp:1229
void flip(const PointType &cell)
Flips the cell; a cell outside the window is silently dropped.
Definition bitmatrix.hpp:618
std::vector< RectangleType > cells() const
The set cells as the unit squares they cover, in row-major order.
Definition bitmatrix.hpp:820
void reset(NumberType x, NumberType y)
Clears the cell; a cell outside the window is silently dropped.
Definition bitmatrix.hpp:573
std::int64_t eulerNumber(GridAdjacency adjacency=GridAdjacency::edge) const
Euler characteristic of the covered region: components minus holes.
Definition bitmatrix.hpp:1630
BitMatrix(const PolygonSet< OtherPointType, TLabel > &set)
Rasterizes a rectilinear set given over another coordinate type.
Definition bitmatrix.hpp:398
bool contains(const BitMatrix &other) const
Whether the covered region contains the other one.
Definition bitmatrix.hpp:1173
bool interiorContains(const BitMatrix &other) const
Whether the interior of the covered region contains the other one.
Definition bitmatrix.hpp:1182
BitMatrix operator|(const BitMatrix &other) const
The cells either matrix has, over the hull of the windows.
Definition bitmatrix.hpp:1100
BitMatrix(const PolygonWithHoles< PointType, TLabel > ®ion)
Rasterizes a rectilinear region, one bit per covered cell.
Definition bitmatrix.hpp:302
BitMatrix & operator+=(const PointType &vector)
Translates the cells by a vector.
Definition bitmatrix.hpp:1269
std::size_t componentCount(GridAdjacency adjacency=GridAdjacency::edge) const
Number of connected groups of cells.
Definition bitmatrix.hpp:1578
auto latticeView() const
Returns a lazy view of the set cells as lattice points.
Definition bitmatrix.hpp:780
BitMatrix & operator-=(const PointType &vector)
Translates the cells by the opposite of a vector.
Definition bitmatrix.hpp:1275
std::vector< BitMatrix > connectedComponents(GridAdjacency adjacency=GridAdjacency::edge) const
Returns one matrix per connected group of cells, each trimmed.
Definition bitmatrix.hpp:1550
BitMatrix transposed() const
Returns the reflection of the covered region across the diagonal.
Definition bitmatrix.hpp:1313
Rectangle< PointType > RectangleType
Rectangle over PointType, the type of a window.
Definition bitmatrix.hpp:229
bool empty() const
Whether no cell is set, which an empty window always is.
Definition bitmatrix.hpp:658
ResultNumber perimeter() const
Length of the boundary of the covered region.
Definition bitmatrix.hpp:697
std::size_t holeCount(GridAdjacency adjacency=GridAdjacency::edge) const
Number of holes the set cells enclose.
Definition bitmatrix.hpp:1617
BitMatrix(const RectangleType &box)
Creates an empty matrix over the cells a rectangle covers.
Definition bitmatrix.hpp:279
Iterator const_iterator
Iteration is read-only, so both iterator types are Iterator.
Definition bitmatrix.hpp:243
void reset(Range &&points)
Clears one cell per point of a range; cells outside the window are dropped.
Definition bitmatrix.hpp:602
bool intersects(const BitMatrix &other) const
Whether the two covered regions share a point.
Definition bitmatrix.hpp:1203
BitMatrix()=default
Creates a matrix whose window is empty, so no cell can be set.
bool operator==(const BitMatrix &other) const
Whether the two matrices have the same window and the same cells.
Definition bitmatrix.hpp:1130
bool sameWindow(const BitMatrix &other) const
Whether two matrices cover the same window.
Definition bitmatrix.hpp:484
BitMatrix & operator|=(const BitMatrix &other)
Adds every cell of the other matrix that this window holds.
Definition bitmatrix.hpp:1081
Rectangle< Point< ResultNumber > > fbox() const
A floating-point bounding box of the covered region.
Definition bitmatrix.hpp:900
BitMatrix operator+(const PointType &vector) const
Returns the same cells translated by a vector.
Definition bitmatrix.hpp:1261
BitMatrix boundary(GridAdjacency adjacency=GridAdjacency::edge) const
Returns the set cells with at least one neighbor that is not set.
Definition bitmatrix.hpp:1536
BitMatrix latticeClosing(const BitMatrix &other) const
Returns the closing, a lattice sum with other followed by a lattice erosion.
Definition bitmatrix.hpp:1511
std::size_t orCount(const BitMatrix &other) const
Number of cells set in either matrix.
Definition bitmatrix.hpp:1238
BitMatrix latticeReflectedY() const
Returns the reflection of the cells as lattice points, {(-x, y)}.
Definition bitmatrix.hpp:1362
BitMatrix trimmed() const
Returns the same cells over the smallest window holding them.
Definition bitmatrix.hpp:505
BitMatrix latticeReflected() const
Returns the reflection of the cells as lattice points, {-c}.
Definition bitmatrix.hpp:1352
std::vector< RectangleType > rectangles() const
The covered region as maximal horizontal runs of cells.
Definition bitmatrix.hpp:836
TPointType PointType
Point type naming a cell by its lower-left corner.
Definition bitmatrix.hpp:225
BitMatrix translated(const PointType &vector) const
Returns the same cells translated by a vector.
Definition bitmatrix.hpp:1254
bool get(NumberType x, NumberType y) const
Whether the cell is set; cells outside the window are not.
Definition bitmatrix.hpp:511
BitMatrix operator&(const BitMatrix &other) const
The cells both matrices have, over the overlap of the windows.
Definition bitmatrix.hpp:1093
BitMatrix(const PolygonSet< PointType, TLabel > &set)
Rasterizes a rectilinear set of regions, one bit per covered cell.
Definition bitmatrix.hpp:377
bool samePointSet(const BitMatrix &other) const
Whether the two matrices cover the same region.
Definition bitmatrix.hpp:1162
auto cellsView() const
Returns a lazy view of the set cells as the unit squares they cover.
Definition bitmatrix.hpp:791
Iterator end() const
End of the iteration over the set lattice points.
Definition bitmatrix.hpp:768
void set(Range &&points)
Sets one cell per point of a range; cells outside the window are dropped.
Definition bitmatrix.hpp:566
BitMatrix operator-() const
Returns the reflection of the covered region through the origin.
Definition bitmatrix.hpp:1293
bool emptyWindow() const
Whether the window itself is degenerate, so no cell can be set.
Definition bitmatrix.hpp:472
Stores drawable objects and exports them as an SVG image.
Definition canvas.hpp:128
Any concrete geometry type or the runtime Shape wrapper.
Definition forward.hpp:328
Definition arrangement.hpp:67
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
constexpr bool is_Rational_v
Definition rational.hpp:37
GridAdjacency
Which grid cells count as neighbors.
Definition bitmatrix.hpp:37
@ edge
Definition bitmatrix.hpp:37
@ vertex
Definition bitmatrix.hpp:37
BitMatrix(PointType, int, int) -> BitMatrix< PointType >
PolygonSet() -> PolygonSet< Point<>, NoLabel >
Definition polygonset.hpp:1699
typename rational_int< T >::type rational_int_t
Definition rational.hpp:61
PolygonWithHoles() -> PolygonWithHoles< Point<>, NoLabel >
Definition polygonwithholes.hpp:3093
BitMatrix< PointType > outerRaster(const ShapeType &shape, const Rectangle< PointType > &window)
Rasterizes a shape into the cells it meets: its outer approximation.
Definition bitmatrix.hpp:2752
BitMatrix< PointType > operator+(const PointType &vector, const BitMatrix< PointType > &matrix)
Returns the same cells translated by a vector.
Definition bitmatrix.hpp:2660
CanvasCommand fill(std::string value)
Creates a command that changes the current fill color.
Definition canvas.hpp:98
Polygon() -> Polygon< Point<>, NoLabel >
Definition polygon.hpp:3200
BitMatrix< PointType > innerRaster(const ShapeType &shape, const Rectangle< PointType > &window)
Rasterizes a shape into the cells it covers: its inner approximation.
Definition bitmatrix.hpp:2768
Enumeration of polyominoes as Pangolin polygons.
Closed convex polygon stored by its vertices.
Definition convex.hpp:170
Two-dimensional point with optional label payload.
Definition point.hpp:129
Set of closed regions with pairwise disjoint interiors.
Definition polygonset.hpp:165
auto asBitMatrix() const
Rasterizes this set into a BitMatrix, one bit per covered cell.
Definition bitmatrix.hpp:2712
Closed region bounded by one outer simple polygon minus disjoint polygonal holes.
Definition polygonwithholes.hpp:89
auto asBitMatrix() const
Rasterizes this region into a BitMatrix, one bit per covered cell.
Definition bitmatrix.hpp:2705
Closed simple polygon stored by its vertices.
Definition polygon.hpp:59
auto asBitMatrix() const
Rasterizes this polygon into a BitMatrix, one bit per covered cell.
Definition bitmatrix.hpp:2698
Axis-aligned rectangle stored by minimum and maximum corners.
Definition rectangle.hpp:75
constexpr const PointType & min() const
Returns the minimum corner (min x, min y).
Definition rectangle.hpp:347
constexpr const PointType & max() const
Returns the maximum corner (max x, max y).
Definition rectangle.hpp:359