24#include <unordered_set>
32template <
class Number>
33[[nodiscard]]
bool isWholeCoordinate(
const Number& value) {
35 return value.isInteger();
36 }
else if constexpr (std::is_floating_point_v<Number>) {
37 return std::isfinite(value) && value == std::floor(value);
50template <
class Int,
class Integer>
51[[nodiscard]]
bool latticeFits(
const Integer& value) {
52 if constexpr (std::same_as<Int, Integer>) {
54 }
else if constexpr (numeric_limits<Integer>::is_specialized
55 && numeric_limits<Integer>::digits < numeric_limits<Int>::digits) {
58 return representableAs<Int>(value);
68template <
class Int,
class Integer>
69void requireLatticeFits(
const Integer& value) {
70 if (!latticeFits<Int>(value)) {
71 throw std::logic_error(
"pgl::latticePoints: a lattice point does not fit the result type");
76template <
class Int,
class Integer>
77[[nodiscard]] Int latticeNarrow(
const Integer& value) {
78 requireLatticeFits<Int>(value);
79 return narrowTo<Int>(value);
83template <
class Int,
class Float>
84[[nodiscard]] Int latticeFromFloat(
const Float& value) {
85 if (!std::isfinite(value)) {
86 throw std::logic_error(
"pgl::latticePoints: a coordinate is not finite");
88 if constexpr (numeric_limits<Int>::is_bounded) {
91 const Float low =
static_cast<Float
>(numeric_limits<Int>::min());
92 if (value < low || value >= -low) {
93 throw std::logic_error(
"pgl::latticePoints: a lattice point does not fit the result type");
95 return static_cast<Int
>(value);
102template <
class Int,
class Number>
103[[nodiscard]] Int latticeFloor(
const Number& value) {
107 const Number reduced = value.simplified();
109 const Integer n = reduced.numerator();
110 const Integer d = reduced.denominator();
111 return latticeNarrow<Int>(n >= Integer(0) ? n / d : -((-n + d - Integer(1)) / d));
112 }
else if constexpr (std::is_floating_point_v<Number>) {
113 return latticeFromFloat<Int>(std::floor(value));
115 return latticeNarrow<Int>(value);
120template <
class Int,
class Number>
121[[nodiscard]] Int latticeCeil(
const Number& value) {
123 const Number reduced = value.simplified();
125 const Integer n = reduced.numerator();
126 const Integer d = reduced.denominator();
127 return latticeNarrow<Int>(n > Integer(0) ? (n + d - Integer(1)) / d : -((-n) / d));
128 }
else if constexpr (std::is_floating_point_v<Number>) {
129 return latticeFromFloat<Int>(std::ceil(value));
131 return latticeNarrow<Int>(value);
142template <
class Number>
143[[nodiscard]] std::array<BigInt, 2> exactFraction(
const Number& value) {
145 const Number reduced = value.simplified();
146 return {BigInt(reduced.numerator()), BigInt(reduced.denominator())};
147 }
else if constexpr (std::is_floating_point_v<Number>) {
148 if (!std::isfinite(value)) {
149 throw std::logic_error(
"pgl::latticePoints: a coordinate is not finite");
154 const Number fraction = std::frexp(value, &exponent);
155 const int digits = numeric_limits<Number>::digits;
156 const BigInt significand(std::ldexp(fraction, digits));
157 const int shift = exponent - digits;
159 return {significand * pow2(shift), BigInt(1)};
161 return {significand, pow2(-shift)};
163 return {BigInt(value), BigInt(1)};
168template <
class Integer>
169[[nodiscard]] std::size_t latticeCount(
const Integer& first,
const Integer& last) {
172 constexpr std::int64_t limit =
173 static_cast<std::int64_t
>(std::numeric_limits<std::size_t>::max() / 2);
174 const Integer count = last - first + Integer(1);
175 if (!latticeFits<std::int64_t>(count) || narrowTo<std::int64_t>(count) > limit) {
176 throw std::length_error(
"pgl::latticePoints: too many lattice points");
178 return static_cast<std::size_t
>(narrowTo<std::int64_t>(count));
182template <
class Int,
class Number>
183[[nodiscard]] Int strictlyBelow(
const Number& value) {
184 const Int below = latticeFloor<Int>(value);
185 return isWholeCoordinate(value) ? Int(below - Int(1)) : below;
189template <
class Number,
class Int>
190[[nodiscard]] Number coordinateAt(
const Int& index) {
191 if constexpr (std::same_as<Number, Int>) {
195 }
else if constexpr (std::is_floating_point_v<Number>) {
196 return static_cast<Number
>(narrowTo<std::int64_t>(index));
198 return narrowTo<Number>(index);
211template <
class ResultNumber,
class SegmentType>
212[[nodiscard]] ResultNumber crossingFloor(
const SegmentType&
edge,
const ResultNumber& column) {
213 using Number =
typename SegmentType::NumberType;
214 const auto& lower =
edge.min();
215 const auto& upper =
edge.max();
216 if constexpr (extended_integral<Number> || std::same_as<Number, BigInt>) {
217 using Wide = promoted_number_t<Number>;
218 const Wide run = Wide(upper.x()) - Wide(lower.x());
219 const Wide rise = Wide(upper.y()) - Wide(lower.y());
220 const Wide offset = (narrowTo<Wide>(column) - Wide(lower.x())) * rise;
221 const Wide quotient = offset >= Wide(0) ? offset / run
222 : -((-offset + run - Wide(1)) / run);
223 return latticeNarrow<ResultNumber>(Wide(lower.y()) + quotient);
225 const Number crossing =
226 lower.y() + (coordinateAt<Number>(column) - lower.x()) * (upper.y() - lower.y())
227 / (upper.x() - lower.x());
228 return latticeFloor<ResultNumber>(crossing);
248template <
class ResultPo
int,
class EdgeRange>
249[[nodiscard]] std::vector<ResultPoint> regionLatticePoints(
const EdgeRange& edges) {
250 using ResultNumber =
typename ResultPoint::NumberType;
251 using EdgeType = std::ranges::range_value_t<EdgeRange>;
260 std::vector<Crossed> crossed;
261 std::vector<ResultPoint> boundary;
262 for (
const EdgeType&
edge : edges) {
263 const std::vector<ResultPoint> own =
edge.template latticePoints<ResultNumber>();
264 boundary.insert(boundary.end(), own.begin(), own.end());
265 if (
edge.isVertical()) {
270 const ResultNumber first = latticeCeil<ResultNumber>(
edge.min().x());
271 const ResultNumber last = strictlyBelow<ResultNumber>(
edge.max().x());
275 crossed.push_back(Crossed{
edge, first, last});
277 std::sort(boundary.begin(), boundary.end());
278 boundary.erase(std::unique(boundary.begin(), boundary.end()), boundary.end());
279 if (crossed.empty()) {
282 std::sort(crossed.begin(), crossed.end(),
283 [](
const Crossed& left,
const Crossed& right) { return left.first < right.first; });
285 std::vector<ResultPoint> inside;
286 std::vector<const Crossed*> active;
287 std::vector<ResultNumber> crossings;
288 std::size_t pending = 0;
289 ResultNumber column = crossed.front().first;
290 while (pending < crossed.size() || !active.empty()) {
291 if (active.empty() && pending < crossed.size() && column < crossed[pending].first) {
292 column = crossed[pending].first;
294 while (pending < crossed.size() && !(column < crossed[pending].first)) {
295 active.push_back(&crossed[pending++]);
297 std::erase_if(active, [&](
const Crossed*
edge) {
return edge->last < column; });
298 if (active.empty()) {
302 for (
const Crossed*
edge : active) {
303 crossings.push_back(crossingFloor<ResultNumber>(
edge->edge, column));
305 std::sort(crossings.begin(), crossings.end());
306 for (std::size_t i = 0; i + 1 < crossings.size(); i += 2) {
307 for (ResultNumber row = crossings[i] + ResultNumber(1); !(crossings[i + 1] < row); ++row) {
308 inside.push_back(ResultPoint(column, row));
314 std::vector<ResultPoint> points;
315 points.reserve(inside.size() + boundary.size());
316 std::set_union(inside.begin(), inside.end(), boundary.begin(), boundary.end(),
317 std::back_inserter(points));
322template <
class Region,
class SegmentVector>
323void appendRegionEdges(
const Region& region, SegmentVector& edges) {
324 for (
const auto&
edge : region.outer().edgesView()) {
325 edges.push_back(
edge);
327 for (
const auto& hole : region.holes()) {
328 for (
const auto&
edge : hole.edgesView()) {
329 edges.push_back(
edge);
341template <
class ResultPo
int,
class Chain>
342[[nodiscard]] std::vector<ResultPoint> chainLatticePoints(
const Chain& chain) {
343 using ResultNumber =
typename ResultPoint::NumberType;
344 if (chain.size() == 1) {
347 .template latticePoints<ResultNumber>();
349 std::vector<ResultPoint> points;
350 std::unordered_set<ResultPoint> reached;
351 for (
const auto&
edge : chain.orientedEdgesView()) {
352 for (
const ResultPoint& point :
edge.template latticePoints<ResultNumber>()) {
353 if (reached.insert(point).second) {
354 points.push_back(point);
369template <
class Po
intType,
class LabelType>
370template <
class ResultNumber>
371 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
377 using Step = detail::promoted_number_t<ResultNumber>;
379 std::vector<ResultPoint> points;
388 ResultNumber firstX{}, firstY{};
389 Step stepX{}, stepY{};
390 std::size_t count = 0;
400 if (!detail::isWholeCoordinate(fixed)) {
403 const ResultNumber constant = detail::latticeFloor<ResultNumber>(fixed);
404 const ResultNumber lowest = detail::latticeCeil<ResultNumber>(low);
405 const ResultNumber highest = detail::latticeFloor<ResultNumber>(high);
406 if (highest < lowest) {
409 count = detail::latticeCount(Step(lowest), Step(highest));
410 firstX = vertical ? constant : lowest;
411 firstY = vertical ? lowest : constant;
412 stepX = vertical ? Step(0) : Step(1);
413 stepY = vertical ? Step(1) : Step(0);
414 }
else if constexpr (detail::extended_integral<NumberType> || std::same_as<NumberType, BigInt>) {
418 using Wide = detail::promoted_number_t<NumberType>;
419 const Wide deltaX = Wide(x2) - Wide(x1);
420 const Wide deltaY = Wide(y2) - Wide(y1);
421 const Wide steps = detail::gcd(detail::abs(deltaX), detail::abs(deltaY));
422 firstX = detail::latticeNarrow<ResultNumber>(x1);
423 firstY = detail::latticeNarrow<ResultNumber>(y1);
424 detail::requireLatticeFits<ResultNumber>(x2);
425 detail::requireLatticeFits<ResultNumber>(y2);
426 count = detail::latticeCount(Wide(0), steps);
427 stepX = detail::narrowTo<Step>(deltaX / steps);
428 stepY = detail::narrowTo<Step>(deltaY / steps);
433 const auto line = [&] {
439 const std::array<BigInt, 2> fx = detail::exactFraction(
x);
440 const std::array<BigInt, 2> fy = detail::exactFraction(
y);
444 .template integralLine<BigInt>();
450 const BigInt baseX = line->source().x();
451 const BigInt baseY = line->source().y();
452 const BigInt directionX = line->target().x() - baseX;
453 const BigInt directionY = line->target().y() - baseY;
458 const auto boundIndex = [&](
const NumberType&
x,
bool upwards) {
459 const std::array<BigInt, 2> fraction = detail::exactFraction(
x);
460 const BigInt numerator = fraction[0] - baseX * fraction[1];
461 const BigInt denominator = fraction[1] * directionX;
463 return numerator >
BigInt(0)
464 ? (numerator + denominator -
BigInt(1)) / denominator
465 : -((-numerator) / denominator);
467 return numerator >=
BigInt(0)
468 ? numerator / denominator
469 : -((-numerator + denominator -
BigInt(1)) / denominator);
471 const BigInt lowest = boundIndex(x1,
true);
472 const BigInt highest = boundIndex(x2,
false);
473 if (highest < lowest) {
476 firstX = detail::latticeNarrow<ResultNumber>(baseX + lowest * directionX);
477 firstY = detail::latticeNarrow<ResultNumber>(baseY + lowest * directionY);
478 detail::requireLatticeFits<ResultNumber>(baseX + highest * directionX);
479 detail::requireLatticeFits<ResultNumber>(baseY + highest * directionY);
480 count = detail::latticeCount(lowest, highest);
484 stepX = detail::narrowTo<Step>(directionX);
485 stepY = detail::narrowTo<Step>(directionY);
489 points.reserve(count);
490 Step
x(firstX),
y(firstY);
491 for (std::size_t i = 0; i < count; ++i) {
492 points.push_back(ResultPoint(detail::narrowTo<ResultNumber>(
x),
493 detail::narrowTo<ResultNumber>(
y)));
504template <
class Po
intType,
class LabelType>
505template <
class ResultNumber>
506 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
511 std::reverse(points.begin(), points.end());
519template <
class Po
intType,
class LabelType,
class Storage>
520template <
class ResultNumber>
521 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
524 return detail::chainLatticePoints<Point<ResultNumber, typename PointType::LabelType>>(*this);
530template <
class Po
intType,
class LabelType>
531template <
class ResultNumber>
532 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
535 return detail::chainLatticePoints<Point<ResultNumber, typename PointType::LabelType>>(*this);
542template <
class Po
intType,
class LabelType>
543template <
class ResultNumber>
544 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
548 std::vector<ResultPoint> points;
549 const ResultNumber firstX = detail::latticeCeil<ResultNumber>(
min().
x());
550 const ResultNumber lastX = detail::latticeFloor<ResultNumber>(
max().
x());
551 const ResultNumber firstY = detail::latticeCeil<ResultNumber>(
min().
y());
552 const ResultNumber lastY = detail::latticeFloor<ResultNumber>(
max().
y());
553 if (lastX < firstX || lastY < firstY) {
560 using Step = detail::promoted_number_t<ResultNumber>;
561 const std::size_t columns = detail::latticeCount(Step(firstX), Step(lastX));
562 const std::size_t rows = detail::latticeCount(Step(firstY), Step(lastY));
563 if (columns > std::numeric_limits<std::size_t>::max() / rows) {
564 throw std::length_error(
"pgl::latticePoints: too many lattice points");
566 points.reserve(columns * rows);
567 for (Step
x = Step(firstX); !(Step(lastX) <
x); ++
x) {
568 for (Step
y = Step(firstY); !(Step(lastY) <
y); ++
y) {
569 points.push_back(ResultPoint(detail::narrowTo<ResultNumber>(
x),
570 detail::narrowTo<ResultNumber>(
y)));
579template <
class Po
intType,
class LabelType>
580template <
class ResultNumber>
581 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
584 return detail::regionLatticePoints<Point<ResultNumber, typename PointType::LabelType>>(
edges());
590template <
class Po
intType,
class LabelType>
591template <
class ResultNumber>
592 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
596 if (
const std::optional<PointType> single =
getIfPoint()) {
600 std::vector<ResultPoint> points;
602 const ResultNumber firstX = detail::latticeCeil<ResultNumber>(box.
min().x());
603 const ResultNumber lastX = detail::latticeFloor<ResultNumber>(box.
max().x());
604 if (lastX < firstX) {
611 const ResultNumber middle =
615 using Step = detail::promoted_number_t<ResultNumber>;
616 for (Step column = Step(firstX); !(Step(lastX) < column); ++column) {
617 const ResultNumber
x = detail::narrowTo<ResultNumber>(column);
618 ResultNumber seed = middle;
619 bool inside =
contains(ResultPoint(
x, seed));
620 for (
int step = -1; !inside && step <= 1; step += 2) {
621 seed = middle + ResultNumber(step);
627 ResultNumber low = seed;
628 ResultNumber high = seed;
629 while (
contains(ResultPoint(
x, low - ResultNumber(1)))) {
632 while (
contains(ResultPoint(
x, high + ResultNumber(1)))) {
635 for (ResultNumber
y = low; !(high <
y); ++
y) {
636 points.push_back(ResultPoint(
x,
y));
645template <
class Po
intType,
class LabelType>
646template <
class ResultNumber>
647 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
650 return detail::regionLatticePoints<Point<ResultNumber, typename PointType::LabelType>>(
657template <
class Po
intType,
class LabelType>
658template <
class ResultNumber>
659 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
662 return detail::regionLatticePoints<Point<ResultNumber, typename PointType::LabelType>>(
669template <
class Po
intType,
class LabelType>
670template <
class ResultNumber>
671 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
683template <
class Po
intType,
class LabelType>
684template <
class ResultNumber>
685 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
691 std::vector<Segment<PointType>>
edges;
692 detail::appendRegionEdges(*
this,
edges);
693 return detail::regionLatticePoints<Point<ResultNumber, typename PointType::LabelType>>(
edges);
699template <
class Po
intType,
class LabelType>
700template <
class ResultNumber>
701 requires(detail::extended_integral<ResultNumber> || std::same_as<ResultNumber, BigInt>)
707 std::vector<Segment<PointType>>
edges;
711 return detail::regionLatticePoints<Point<ResultNumber, typename PointType::LabelType>>(
edges);
Arbitrary precision signed integer.
Definition bigint.hpp:157
Exact rational number class template.
Definition rational.hpp:106
Hash support for Pangolin value types.
Definition arrangement.hpp:67
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
constexpr bool is_Rational_v
Definition rational.hpp:37
@ edge
Definition bitmatrix.hpp:37
typename DivisionResult< Number >::type division_result_t
Convenience alias for DivisionResult.
Definition rational.hpp:1175
typename rational_int< T >::type rational_int_t
Definition rational.hpp:61
Segment() -> Segment< Point<>, NoLabel >
OrientedLine() -> OrientedLine< Point<>, NoLabel >
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the convex polygon contains.
Definition lattice.hpp:649
constexpr auto edgesView() const
Returns a lazy view over the edges, materializing each Segment on the fly instead of allocating a vec...
Definition convex.hpp:575
constexpr Point< ResultNumber, PointLabelType > center() const
Returns the center (circumcenter of the three boundary points) in an explicitly chosen coordinate typ...
Definition disk.hpp:284
constexpr std::optional< PointType > getIfPoint() const
Returns the point the disk collapses to, if it does.
Definition disk.hpp:372
constexpr bool contains(const OtherPoint &other) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:1015
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the disk contains.
Definition lattice.hpp:594
constexpr Rectangle< PointType > bbox() const
Returns an axis-aligned bounding box in the coordinate type.
Definition disk.hpp:457
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the region contains.
Definition lattice.hpp:673
constexpr Convex< Point< ResultNumber, typename PointType::LabelType > > asConvex() const
Returns the region as a convex polygon.
Definition halfplaneintersection.hpp:955
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the chain contains.
Definition lattice.hpp:523
constexpr const PointType & source() const
Returns the source endpoint.
Definition orientedsegment.hpp:178
constexpr const PointType & target() const
Returns the target endpoint.
Definition orientedsegment.hpp:190
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the oriented segment contains.
Definition lattice.hpp:508
Two-dimensional point with optional label payload.
Definition point.hpp:129
constexpr std::vector< EdgeType > edges() const
Returns the boundary edges of every ring of every component.
Definition polygonset.hpp:420
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the set contains.
Definition lattice.hpp:703
constexpr const std::vector< ComponentType > & components() const
Returns the components in canonical order.
Definition polygonset.hpp:277
constexpr const ComponentType & component(std::size_t index) const
Accesses a component by index.
Definition polygonset.hpp:271
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the region contains.
Definition lattice.hpp:687
constexpr std::vector< EdgeType > edges() const
Returns the boundary edges of every ring, outer boundary first.
Definition polygonwithholes.hpp:343
constexpr auto edgesView() const
Returns a lazy view over the edges, materializing each Segment on the fly instead of allocating a vec...
Definition polygon.hpp:782
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the polygon contains.
Definition lattice.hpp:661
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the polyline contains.
Definition lattice.hpp:534
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
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the rectangle contains.
Definition lattice.hpp:546
Unoriented closed segment between two endpoints plus optional segment label.
Definition segment.hpp:58
constexpr bool isHorizontal() const
Returns whether the segment is horizontal.
Definition predicates.hpp:82
constexpr const PointType & max() const
Returns the largest stored endpoint.
Definition segment.hpp:199
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the segment contains.
Definition lattice.hpp:373
constexpr const PointType & min() const
Returns the smallest stored endpoint.
Definition segment.hpp:190
PointType::NumberType NumberType
Definition segment.hpp:60
constexpr bool isVertical() const
Returns whether the segment is vertical.
Definition predicates.hpp:77
std::vector< Point< ResultNumber, typename PointType::LabelType > > latticePoints() const
Returns the integer points the triangle contains.
Definition lattice.hpp:583
constexpr std::array< Segment< PointType >, 3 > edges() const
Returns the three unoriented boundary edges.
Definition bounding.hpp:240