Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
measures.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <optional>
5#include <type_traits>
6
9
17
18
19namespace pgl {
20
21// -----------------------------------------------------------------------------
22// Point
23
24template <class LeftNumber, class LeftLabel, class RightNumber, class RightLabel>
26 return left.x() * right.x() + left.y() * right.y();
27}
28
29// -----------------------------------------------------------------------------
30// Segment
31
32template <class PointType, class LabelType>
33template <class ResultNumber>
34constexpr ResultNumber Segment<PointType, LabelType>::area() const {
35 return ResultNumber{};
36}
37
38template <class PointType, class LabelType>
42
43template <class PointType, class LabelType>
45 return min().template squaredDistance<NumberType>(max());
46}
47
48template <class PointType, class LabelType>
49template <class ApproximateNumber>
50ApproximateNumber Segment<PointType, LabelType>::length() const {
51 return min().template distance<ApproximateNumber>(max());
52}
53
54template <class PointType, class LabelType>
56 return min().distanceL1(max());
57}
58
59template <class PointType, class LabelType>
61 return min().distanceLInf(max());
62}
63
64template <class PointType, class LabelType>
65template <class ResultNumber>
66constexpr ResultNumber Segment<PointType, LabelType>::slope() const {
67 const auto dy = detail::asNumber<ResultNumber>(max().y()) - detail::asNumber<ResultNumber>(min().y());
68 const auto dx = detail::asNumber<ResultNumber>(max().x()) - detail::asNumber<ResultNumber>(min().x());
69 return dy / dx;
70}
71
72template <class PointType, class LabelType>
76
77template <class PointType, class LabelType>
78template <class ResultNumber>
81 (detail::asNumber<ResultNumber>(min().x()) + detail::asNumber<ResultNumber>(max().x())) / static_cast<ResultNumber>(2),
82 (detail::asNumber<ResultNumber>(min().y()) + detail::asNumber<ResultNumber>(max().y())) / static_cast<ResultNumber>(2)
83 );
84}
85
86template <class PointType, class LabelType>
87template <class ResultNumber>
91
92template <class PointType, class LabelType>
93template <class OtherShape>
94constexpr bool Segment<PointType, LabelType>::pointInsideInteriorContainedIn(const OtherShape& shape) const {
95 const auto witness = pointInside<NumberType>();
96 if (interiorContains(witness)) {
97 return shape.interiorContains(witness);
98 }
99 // Integer truncation rounded the midpoint onto an endpoint; scaling by 2
100 // makes it exact without changing the containment relation.
101 return (shape * 2).interiorContains((*this * 2).template pointInside<NumberType>());
102}
103
104// -----------------------------------------------------------------------------
105// OrientedSegment
106
107template <class PointType, class LabelType>
108template <class ResultNumber>
109constexpr ResultNumber OrientedSegment<PointType, LabelType>::area() const {
110 return ResultNumber{};
111}
112
113template <class PointType, class LabelType>
117
118template <class PointType, class LabelType>
122
123template <class PointType, class LabelType>
124template <class ApproximateNumber>
126 return source().template distance<ApproximateNumber>(target());
127}
128
129template <class PointType, class LabelType>
131 return source().distanceL1(target());
132}
133
134template <class PointType, class LabelType>
136 return source().distanceLInf(target());
137}
138
139template <class PointType, class LabelType>
140template <class ResultNumber>
141constexpr ResultNumber OrientedSegment<PointType, LabelType>::slope() const {
142 const auto dy = detail::asNumber<ResultNumber>(target().y()) - detail::asNumber<ResultNumber>(source().y());
143 const auto dx = detail::asNumber<ResultNumber>(target().x()) - detail::asNumber<ResultNumber>(source().x());
144 return dy / dx;
145}
146
147template <class PointType, class LabelType>
149 return static_cast<Segment<PointType>>(*this);
150}
151
152template <class PointType, class LabelType>
153template <class ResultNumber>
155 return Point<ResultNumber>(
156 (detail::asNumber<ResultNumber>(source().x()) + detail::asNumber<ResultNumber>(target().x())) / static_cast<ResultNumber>(2),
157 (detail::asNumber<ResultNumber>(source().y()) + detail::asNumber<ResultNumber>(target().y())) / static_cast<ResultNumber>(2));
158}
159
160template <class PointType, class LabelType>
161template <class ResultNumber>
165
166template <class PointType, class LabelType>
167template <class OtherShape>
169 // Orientation does not affect the point set, so defer to the segment.
170 return asSegment().pointInsideInteriorContainedIn(shape);
171}
172
173// -----------------------------------------------------------------------------
174// Line
175
176template <class PointType, class LabelType>
177template <class ResultNumber>
178constexpr ResultNumber Line<PointType, LabelType>::area() const {
179 return ResultNumber{};
180}
181
182template <class PointType, class LabelType>
186
187template <class PointType, class LabelType>
188template <class ResultNumber>
189constexpr ResultNumber Line<PointType, LabelType>::slope() const {
190 const auto dy = detail::asNumber<ResultNumber>(max().y()) - detail::asNumber<ResultNumber>(min().y());
191 const auto dx = detail::asNumber<ResultNumber>(max().x()) - detail::asNumber<ResultNumber>(min().x());
192 return dy / dx;
193}
194
195template <class PointType, class LabelType>
196template <class ResultNumber>
200
201template <class PointType, class LabelType>
202template <class OtherShape>
203constexpr bool Line<PointType, LabelType>::pointInsideInteriorContainedIn(const OtherShape& shape) const {
204 // pointInside() lies exactly on the line, so it is a valid interior witness.
205 return shape.interiorContains(pointInside<NumberType>());
206}
207
208// -----------------------------------------------------------------------------
209// OrientedLine
210
211template <class PointType, class LabelType>
212template <class ResultNumber>
213constexpr ResultNumber OrientedLine<PointType, LabelType>::area() const {
214 return ResultNumber{};
215}
216
217template <class PointType, class LabelType>
221
222template <class PointType, class LabelType>
223template <class ResultNumber>
224constexpr ResultNumber OrientedLine<PointType, LabelType>::slope() const {
225 const auto dy = detail::asNumber<ResultNumber>(target().y()) - detail::asNumber<ResultNumber>(source().y());
226 const auto dx = detail::asNumber<ResultNumber>(target().x()) - detail::asNumber<ResultNumber>(source().x());
227 return dy / dx;
228}
229
230template <class PointType, class LabelType>
231template <class ResultNumber>
235
236template <class PointType, class LabelType>
237template <class OtherShape>
238constexpr bool OrientedLine<PointType, LabelType>::pointInsideInteriorContainedIn(const OtherShape& shape) const {
239 // Orientation does not affect the point set, so defer to the line.
240 return asLine().pointInsideInteriorContainedIn(shape);
241}
242
243// -----------------------------------------------------------------------------
244// Ray
245
246template <class PointType, class LabelType>
247template <class ResultNumber>
248constexpr ResultNumber Ray<PointType, LabelType>::area() const {
249 return ResultNumber{};
250}
251
252template <class PointType, class LabelType>
256
257template <class PointType, class LabelType>
258template <class ResultNumber>
259constexpr ResultNumber Ray<PointType, LabelType>::slope() const {
260 const auto dy = detail::asNumber<ResultNumber>(target().y()) - detail::asNumber<ResultNumber>(source().y());
261 const auto dx = detail::asNumber<ResultNumber>(target().x()) - detail::asNumber<ResultNumber>(source().x());
262 return dy / dx;
263}
264
265template <class PointType, class LabelType>
266template <class ResultNumber>
270
271template <class PointType, class LabelType>
272template <class OtherShape>
273constexpr bool Ray<PointType, LabelType>::pointInsideInteriorContainedIn(const OtherShape& shape) const {
274 // pointInside() is an exact ray point past the source, so it is a valid
275 // interior witness.
276 return shape.interiorContains(pointInside<NumberType>());
277}
278
279// -----------------------------------------------------------------------------
280// Rectangle
281
282template <class PointType, class LabelType>
283template <class ResultNumber>
284constexpr ResultNumber Rectangle<PointType, LabelType>::area() const {
285 // width() and height() already report 0 for the empty rectangle, so its
286 // inverted corners never reach the product.
287 return static_cast<ResultNumber>(width()) * static_cast<ResultNumber>(height());
288}
289
290template <class PointType, class LabelType>
292 const auto rectangle_area = area();
293 return rectangle_area + rectangle_area;
294}
295
296template <class PointType, class LabelType>
298 assert(!empty());
299 return Segment<PointType>(min(), max());
300}
301
302template <class PointType, class LabelType>
303template <class ResultNumber>
305 assert(!empty());
306 return Point<ResultNumber>(
307 (detail::asNumber<ResultNumber>(min().x()) + detail::asNumber<ResultNumber>(max().x())) / static_cast<ResultNumber>(2),
308 (detail::asNumber<ResultNumber>(min().y()) + detail::asNumber<ResultNumber>(max().y())) / static_cast<ResultNumber>(2));
309}
310
311template <class PointType, class LabelType>
312template <class ResultNumber>
316
317template <class PointType, class LabelType>
319 assert(!empty());
320 return Disk<PointType, NoLabel>(min(), bottomRight(), max()); //Choosen arbitrarly
321}
322
323template <class PointType, class LabelType>
324template <class ResultNumber>
328
329template <class PointType, class LabelType>
330template <class ResultNumber>
334
335template <class PointType, class LabelType>
336template <class OtherShape>
337constexpr bool Rectangle<PointType, LabelType>::pointInsideInteriorContainedIn(const OtherShape& shape) const {
338 const auto witness = pointInside<NumberType>();
339 if (interiorContains(witness)) {
340 return shape.interiorContains(witness);
341 }
342 // Integer truncation rounded the midpoint onto the boundary; scaling by 2
343 // makes it exact without changing the containment relation.
344 return (shape * 2).interiorContains((*this * 2).template pointInside<NumberType>());
345}
346
347// -----------------------------------------------------------------------------
348// Triangle
349
350template <class PointType, class LabelType>
352 return static_cast<NumberType>(detail::abs(orientationDeterminant(a(), b(), c())));
353}
354
355template <class PointType, class LabelType>
356template <class ResultNumber>
357constexpr ResultNumber Triangle<PointType, LabelType>::area() const {
358 const auto area2 = twiceArea();
359 return detail::asNumber<ResultNumber>(area2) / static_cast<ResultNumber>(2);
360}
361
362template <class PointType, class LabelType>
363template <class ResultNumber>
365 const ResultNumber three = static_cast<ResultNumber>(3);
366 return Point<ResultNumber>(
367 (detail::asNumber<ResultNumber>(a().x()) +
368 detail::asNumber<ResultNumber>(b().x()) +
369 detail::asNumber<ResultNumber>(c().x())) / three,
370 (detail::asNumber<ResultNumber>(a().y()) +
371 detail::asNumber<ResultNumber>(b().y()) +
372 detail::asNumber<ResultNumber>(c().y())) / three);
373}
374
375template <class PointType, class LabelType>
379
380template <class PointType, class LabelType>
382 const auto ab = a().template squaredDistance<NumberType>(b());
383 const auto bc = b().template squaredDistance<NumberType>(c());
384 const auto ca = c().template squaredDistance<NumberType>(a());
385
386 if (ab < bc) {
387 if (ca < bc) {
388 return Segment<PointType>(b(), c());
389 }
390 return Segment<PointType>(c(), a());
391 }
392
393 if (ca < ab) {
394 return Segment<PointType>(a(), b());
395 }
396 return Segment<PointType>(c(), a());
397}
398
399template <class PointType, class LabelType>
400template <class ResultNumber>
402 Point<ResultNumber> p = points_[0] + points_[1];
403 p = (p/2 + points_[2])/2;
404 return p;
405}
406
407template <class PointType, class LabelType>
408template <class OtherShape>
409constexpr bool Triangle<PointType, LabelType>::pointInsideInteriorContainedIn(const OtherShape& shape) const {
410 const auto witness = pointInside<NumberType>();
411 if (interiorContains(witness)) {
412 return shape.interiorContains(witness);
413 }
414 // pointInside() divides by 4; integer truncation rounded it onto the
415 // boundary, so scaling by 4 makes it exact without changing containment.
416 return (shape * 4).interiorContains((*this * 4).template pointInside<NumberType>());
417}
418
419template <class PointType, class LabelType>
421 if (isDegenerate()) {
422 return false;
423 }
424
425 const auto ab = b() - a();
426 const auto ac = c() - a();
427 const auto bc = c() - b();
428
429 // dotSign promotes the coordinates before multiplying; the plain dot
430 // product operator* does not, and overflows the coordinate type well
431 // inside its range (a dot product of 2^32 wraps an int to zero, reporting
432 // a right angle where there is none).
433 //
434 // Three difference vectors carry all three angles: the vectors this test
435 // used to build for b and c are just negations of these, and negating one
436 // operand of a dot product only flips its sign. ba·bc = -(ab·bc) and
437 // ca·cb = ac·bc, and a test against zero cannot tell a sign flip apart.
438 return dotSign(ab, ac) == 0 || dotSign(ab, bc) == 0 || dotSign(ac, bc) == 0;
439}
440
441template <class PointType, class LabelType>
443 if (isDegenerate()) {
444 return false;
445 }
446
447 const auto ab = b() - a();
448 const auto ac = c() - a();
449 const auto bc = c() - b();
450
451 // Promoted like isRectangle above, and sharing its three difference
452 // vectors. Here the sign does matter: ba·bc = -(ab·bc), so the obtuse
453 // angle at b shows up as a *positive* ab·bc, while ca·cb = ac·bc keeps
454 // its sign.
455 return dotSign(ab, ac) < 0 || dotSign(ab, bc) > 0 || dotSign(ac, bc) < 0;
456}
457
458template <class PointType, class LabelType>
460 // Squared lengths need the promoted coordinate type for the same reason
461 // the dot products above do: |ab|^2 overflows an int coordinate at around
462 // 46341, and two sides that merely agree modulo the wrap would be reported
463 // as equal. The caller never names this type, so the choice is ours to get
464 // right rather than a precision the user asked for.
465 using Squared = detail::promoted_number_t<NumberType>;
466
467 const auto ab = a().template squaredDistance<Squared>(b());
468 const auto bc = b().template squaredDistance<Squared>(c());
469 const auto ca = c().template squaredDistance<Squared>(a());
470 return ab == bc || bc == ca || ca == ab;
471}
472
473// -----------------------------------------------------------------------------
474// Halfplane
475
476template <class PointType, class LabelType>
477template <class ResultNumber>
478constexpr ResultNumber Halfplane<PointType, LabelType>::slope() const {
479 const auto dy = detail::asNumber<ResultNumber>(target().y()) - detail::asNumber<ResultNumber>(source().y());
480 const auto dx = detail::asNumber<ResultNumber>(target().x()) - detail::asNumber<ResultNumber>(source().x());
481 return dy / dx;
482}
483
484template <class PointType, class LabelType>
485template <class ResultNumber>
487 const ResultNumber x = static_cast<ResultNumber>(source().x());
488 const ResultNumber y = static_cast<ResultNumber>(source().y());
489 const ResultNumber one = static_cast<ResultNumber>(1);
490
491 if (source().x() < target().x()) {
492 return {x, y + one};
493 }
494 if (source().x() > target().x()) {
495 return {x, y - one};
496 }
497 if (source().y() < target().y()) {
498 return {x - one, y};
499 }
500 return {x + one, y - one};
501}
502
503template <class PointType, class LabelType>
504template <class OtherShape>
505constexpr bool Halfplane<PointType, LabelType>::pointInsideInteriorContainedIn(const OtherShape& shape) const {
506 // pointInside() is an exact lattice point strictly inside the halfplane, so
507 // it is a valid interior witness.
509}
510
511
512// ---------------------------------------------------------------------------
513// Convex
514
515template <class PointType, class LabelType>
517 if (points_.size() < 3) {
518 return NumberType(0);
519 }
520 NumberType sum = 0;
521 for (std::size_t i = 0; i < points_.size(); ++i) {
522 const auto& p1 = points_[i];
523 const auto& p2 = points_[(i + 1) % points_.size()];
524 sum += p1.x() * p2.y() - p2.x() * p1.y();
525 }
526 return pgl::detail::abs(sum);
527}
528
529template <class PointType, class LabelType>
530template <class ResultNumber>
532 ResultNumber result = static_cast<ResultNumber>(twiceArea());
533 return result / ResultNumber(2);
534}
535
536template <class PointType, class LabelType>
537template <class ResultNumber>
539 if (points_.empty()) {
540 return Point<ResultNumber>();
541 }
542 if (points_.size() == 1) {
543 return Point<ResultNumber>(points_[0]);
544 }
545 auto area_twice = twiceArea();
546 if (points_.size() == 2 || area_twice == NumberType(0)) {
547 const Point<ResultNumber> p1 = static_cast<Point<ResultNumber>>(points_[0]);
548 const Point<ResultNumber> p2 = static_cast<Point<ResultNumber>>(points_[maxIndex()]);
549 return (p1 + p2) / ResultNumber(2) + static_cast<Point<ResultNumber>>(translation_);
550 }
551 ResultNumber cx = 0;
552 ResultNumber cy = 0;
553 for (std::size_t i = 0; i < points_.size(); ++i) {
554 const auto& p1 = points_[i];
555 const auto& p2 = points_[(i + 1) % points_.size()];
556 const auto cross = p1.x() * p2.y() - p2.x() * p1.y();
557 cx += (p1.x() + p2.x()) * cross;
558 cy += (p1.y() + p2.y()) * cross;
559 }
560 return Point<ResultNumber>(cx / (ResultNumber(3) * area_twice), cy / (ResultNumber(3) * area_twice))
561 + static_cast<Point<ResultNumber>>(translation_);
562}
563
564template <class PointType, class LabelType>
565template <class ResultNumber>
567 if (points_.empty()) {
568 return Point<ResultNumber>();
569 }
570 ResultNumber cx = 0;
571 ResultNumber cy = 0;
572 for (const auto& vertex : points_) {
573 cx += detail::asNumber<ResultNumber>(vertex.x());
574 cy += detail::asNumber<ResultNumber>(vertex.y());
575 }
576 return Point<ResultNumber>(cx / static_cast<ResultNumber>(points_.size()), cy / static_cast<ResultNumber>(points_.size()))
577 + static_cast<Point<ResultNumber>>(translation_);
578}
579
580template <class PointType, class LabelType>
581template <class ResultNumber>
583 if (points_.empty()) {
584 return Point<ResultNumber>();
585 }
586 if (points_.size() == 1) {
587 return Point<ResultNumber>(points_[0]) + static_cast<Point<ResultNumber>>(translation_);
588 }
589 if (points_.size() == 2) {
590 const Point<ResultNumber> p1(points_[0]);
591 const Point<ResultNumber> p2(points_[1]);
592 return (p1 + p2) / ResultNumber(2) + static_cast<Point<ResultNumber>>(translation_);
593 }
594 Triangle<PointType> triangle(points_[0], points_[1], points_[2]);
595 // Compute the inner triangle's interior point in ResultNumber too; using
596 // the (now integer) default would truncate and can fall outside the polygon.
597 return Point<ResultNumber>(triangle.template pointInside<ResultNumber>())
598 + static_cast<Point<ResultNumber>>(translation_);
599}
600
601template <class PointType, class LabelType>
602template <class OtherShape>
603constexpr bool Convex<PointType, LabelType>::pointInsideInteriorContainedIn(const OtherShape& shape) const {
604 const auto witness = pointInside<NumberType>();
605 if (interiorContains(witness)) {
606 return shape.interiorContains(witness);
607 }
608 // pointInside() divides by up to 4; integer truncation rounded it onto the
609 // boundary, so scaling by 4 makes it exact without changing containment.
610 return (shape * 4).interiorContains((*this * 4).template pointInside<NumberType>());
611}
612
613template <class PointType, class LabelType>
614constexpr std::vector<std::pair<std::size_t, std::size_t>>
616 const std::size_t n = size();
617 std::vector<std::pair<std::size_t, std::size_t>> pairs;
618 if (n < 2) {
619 return pairs;
620 }
621 if (n == 2) {
622 pairs.emplace_back(0, 1);
623 return pairs;
624 }
625
626 const auto next = [n](std::size_t v) { return v + 1 == n ? std::size_t{0} : v + 1; };
627
628 // Cross product of edge(i) = p[i+1]-p[i] with edge(j), in exact promoted
629 // arithmetic. As the caliper direction rotates CCW, the i-side caliper
630 // reaches the next edge normal before the j-side one exactly when this is
631 // negative; a zero means the two edges are parallel (a shared supporting
632 // direction touching both edges at once).
633 using Coord = detail::promoted_number_t<NumberType>;
634 const auto edgeCross = [this, n](std::size_t i, std::size_t j) {
635 const auto pi = (*this)[i];
636 const auto pi1 = (*this)[i + 1 == n ? std::size_t{0} : i + 1];
637 const auto pj = (*this)[j];
638 const auto pj1 = (*this)[j + 1 == n ? std::size_t{0} : j + 1];
639 const Coord eix = detail::asNumber<Coord>(pi1.x()) - detail::asNumber<Coord>(pi.x());
640 const Coord eiy = detail::asNumber<Coord>(pi1.y()) - detail::asNumber<Coord>(pi.y());
641 const Coord ejx = detail::asNumber<Coord>(pj1.x()) - detail::asNumber<Coord>(pj.x());
642 const Coord ejy = detail::asNumber<Coord>(pj1.y()) - detail::asNumber<Coord>(pj.y());
643 return eix * ejy - eiy * ejx;
644 };
645
646 // Start the calipers at the lexicographically smallest vertex (index 0, the
647 // min-x support) and the max-x support; these admit vertical parallel
648 // supporting lines, so they form an antipodal pair.
649 const std::size_t i0 = maxIndex();
650 const std::size_t j0 = 0;
651 std::size_t i = i0;
652 std::size_t j = j0;
653
654 const auto isStart = [i0](std::size_t a, std::size_t b) {
655 return (a == i0 && b == j0) || (a == j0 && b == i0);
656 };
657 const auto emit = [&pairs, &isStart](std::size_t a, std::size_t b) {
658 // The starting pair is emitted once up front; skip any later reappearance
659 // (it would otherwise close the sweep with a duplicate).
660 if (isStart(a, b)) {
661 return;
662 }
663 pairs.emplace_back(std::min(a, b), std::max(a, b));
664 };
665
666 pairs.emplace_back(std::min(i0, j0), std::max(i0, j0));
667
668 // Sweep the caliper direction through half a turn. Advancing whichever side
669 // reaches its next edge normal first walks every antipodal pair once.
670 while (true) {
671 const auto c = edgeCross(i, j);
672 if (c < 0) {
673 i = next(i);
674 } else if (c > 0) {
675 j = next(j);
676 } else {
677 // Parallel edges: vertices i, i+1 are co-extreme with j, j+1, so the
678 // three remaining cross pairs of this edge/edge contact are antipodal.
679 const std::size_t i2 = next(i);
680 const std::size_t j2 = next(j);
681 emit(i, j2);
682 emit(i2, j);
683 i = i2;
684 j = j2;
685 }
686 if (i == j0 && j == i0) {
687 break; // returned to the swapped start: sweep complete.
688 }
689 emit(i, j);
690 }
691
692 return pairs;
693}
694
695template <class PointType, class LabelType>
697 const std::size_t n = size();
698 if (n == 0) {
699 return Segment<PointType>();
700 }
701 if (n == 1) {
702 return Segment<PointType>((*this)[0], (*this)[0]);
703 }
704
705 // The farthest vertex pair is antipodal, so the diameter is the longest of
706 // the O(n) antipodal segments. Compare squared lengths for exactness.
707 const auto pairs = antipodalPairs();
708 Segment<PointType> best((*this)[pairs.front().first], (*this)[pairs.front().second]);
709 auto bestSquared = (*this)[pairs.front().first].template squaredDistance<NumberType>(
710 (*this)[pairs.front().second]);
711 for (const auto& [i, j] : pairs) {
712 const auto pi = (*this)[i];
713 const auto pj = (*this)[j];
714 const auto squared = pi.template squaredDistance<NumberType>(pj);
715 if (bestSquared < squared) {
716 bestSquared = squared;
717 best = Segment<PointType>(pi, pj);
718 }
719 }
720 return best;
721}
722
723template <class PointType, class LabelType>
726 const std::size_t n = size();
727 if (n < 3) {
728 // No area to enclose: the polygon (empty, a point, or a segment) is its
729 // own smallest enclosing rectangle.
731 }
732
733 // Every quantity below is a difference between vertices, so the promoted
734 // coordinate type only has to cover the extent of the polygon.
735 using Coord = detail::promoted_number_t<NumberType>;
736 // Comparing two candidate areas is one degree past that, so it runs in a
737 // type that grows to hold its values: their products are integers for
738 // integral coordinates and fractions for rational ones. Floating-point
739 // coordinates keep computing in the promoted floating-point type.
740 using Wide = std::conditional_t<
741 std::floating_point<NumberType>, Coord,
742 std::conditional_t<RationalConcept<NumberType>, ERational, BigInt>>;
743 struct Vec {
744 Coord x, y;
745 };
746
747 const auto next = [n](std::size_t v) { return v + 1 == n ? std::size_t{0} : v + 1; };
748 const auto difference = [this](std::size_t a, std::size_t b) {
749 const auto pa = (*this)[a];
750 const auto pb = (*this)[b];
751 return Vec{detail::asNumber<Coord>(pa.x()) - detail::asNumber<Coord>(pb.x()),
752 detail::asNumber<Coord>(pa.y()) - detail::asNumber<Coord>(pb.y())};
753 };
754 const auto edgeVector = [&](std::size_t i) { return difference(next(i), i); };
755 const auto dot = [](const Vec& u, const Vec& w) { return u.x * w.x + u.y * w.y; };
756 const auto cross = [](const Vec& u, const Vec& w) { return u.x * w.y - u.y * w.x; };
757
758 // The rectangle flush with edge i rests on three further supports: the
759 // extreme vertex along the edge direction, the farthest vertex from the edge
760 // line, and the extreme vertex against the edge direction. Around a CCW
761 // boundary they appear in exactly that order after i, and as the edge
762 // direction turns they only move forward, which is what makes the sweep
763 // linear. Each objective is cyclically unimodal along the boundary, so
764 // walking forward while the next vertex is strictly better lands on it.
765 std::size_t right = 0;
766 std::size_t top = 0;
767 std::size_t left = 0;
768 const auto advance = [&](std::size_t& support, auto better) {
769 while (better(difference(next(support), support))) {
770 support = next(support);
771 }
772 };
773 const auto advanceRight = [&](const Vec& u) {
774 advance(right, [&](const Vec& step) { return dot(u, step) > 0; });
775 };
776 const auto advanceTop = [&](const Vec& u) {
777 advance(top, [&](const Vec& step) { return cross(u, step) > 0; });
778 };
779 const auto advanceLeft = [&](const Vec& u) {
780 advance(left, [&](const Vec& step) { return dot(u, step) < 0; });
781 };
782
783 // Seed the three supports for edge 0, each starting where the previous one
784 // stopped, since that is where its own ascent begins.
785 {
786 const Vec u = edgeVector(0);
787 advanceRight(u);
788 top = right;
789 advanceTop(u);
790 left = top;
791 advanceLeft(u);
792 }
793
794 // A minimum-area enclosing rectangle has a side flush with a polygon edge,
795 // so the best of these n candidates is the answer. Measured in the frame of
796 // edge i, the rectangle spans [low, high] along u and [0, height] along u
797 // turned 90 degrees, all three scaled by |u|; its area is therefore
798 // (high - low) * height / (u * u), and two of those fractions are compared
799 // by cross-multiplication.
800 std::size_t bestEdge = 0, bestRight = 0, bestTop = 0, bestLeft = 0;
801 Wide bestWidth(0), bestHeight(0), bestSquaredLength(1);
802 for (std::size_t i = 0; i < n; ++i) {
803 const Vec u = edgeVector(i);
804 advanceRight(u);
805 advanceTop(u);
806 advanceLeft(u);
807
808 const Coord low = dot(u, difference(left, i));
809 const Coord high = dot(u, difference(right, i));
810 const Coord width = high - low;
811 const Coord height = cross(u, difference(top, i));
812 const Coord squaredLength = dot(u, u);
813
814 const Wide wideWidth = detail::asNumber<Wide>(width);
815 const Wide wideHeight = detail::asNumber<Wide>(height);
816 const Wide wideSquaredLength = detail::asNumber<Wide>(squaredLength);
817 if (i == 0 || wideWidth * wideHeight * bestSquaredLength <
818 bestWidth * bestHeight * wideSquaredLength) {
819 bestEdge = i;
820 bestRight = right;
821 bestTop = top;
822 bestLeft = left;
823 bestWidth = wideWidth;
824 bestHeight = wideHeight;
825 bestSquaredLength = wideSquaredLength;
826 }
827 }
828
829 // The four supporting lines, each through the support it touches and along
830 // the flush edge or that edge turned 90 degrees, oriented so that the
831 // rectangle lies to the left. Nothing here divides, which is why the region
832 // is exact in NumberType while its corners are not.
833 const PointType base = (*this)[bestEdge];
834 const PointType tip = (*this)[next(bestEdge)];
835 const PointType along = tip - base;
836 const PointType across = along.rotated90();
837 const PointType topSupport = (*this)[bestTop];
838 const PointType rightSupport = (*this)[bestRight];
839 const PointType leftSupport = (*this)[bestLeft];
841 Halfplane<PointType>(base, tip),
842 Halfplane<PointType>(rightSupport, rightSupport + across),
843 Halfplane<PointType>(topSupport, topSupport - along),
844 Halfplane<PointType>(leftSupport, leftSupport - across),
845 });
846}
847
848namespace detail {
849
864template <class PointType, class LabelType>
865constexpr std::pair<std::size_t, std::size_t>
866minimumWidthSupport(const Convex<PointType, LabelType>& convex) {
867 using NumberType = typename PointType::NumberType;
868 const std::size_t n = convex.size();
869
870 // Every quantity below is a difference between vertices, so the promoted
871 // coordinate type only has to cover the extent of the polygon.
872 using Coord = promoted_number_t<NumberType>;
873 // Comparing two candidate widths squares the supporting distance and
874 // cross-multiplies by the squared edge lengths, which is three degrees past
875 // that, so it runs in a type that grows to hold its values: their products
876 // are integers for integral coordinates and fractions for rational ones.
877 // Floating-point coordinates keep computing in the promoted floating-point
878 // type.
879 using Wide = std::conditional_t<
880 std::floating_point<NumberType>, Coord,
881 std::conditional_t<RationalConcept<NumberType>, ERational, BigInt>>;
882 struct Vec {
883 Coord x, y;
884 };
885
886 const auto next = [n](std::size_t v) { return v + 1 == n ? std::size_t{0} : v + 1; };
887 const auto difference = [&convex](std::size_t a, std::size_t b) {
888 const auto pa = convex[a];
889 const auto pb = convex[b];
890 return Vec{asNumber<Coord>(pa.x()) - asNumber<Coord>(pb.x()),
891 asNumber<Coord>(pa.y()) - asNumber<Coord>(pb.y())};
892 };
893 const auto edgeVector = [&](std::size_t i) { return difference(next(i), i); };
894 const auto dot = [](const Vec& u, const Vec& w) { return u.x * w.x + u.y * w.y; };
895 const auto cross = [](const Vec& u, const Vec& w) { return u.x * w.y - u.y * w.x; };
896
897 std::size_t top = 0;
898 const auto advance = [&](auto better) {
899 while (better(difference(next(top), top))) {
900 top = next(top);
901 }
902 };
903 const auto advanceTop = [&](const Vec& u) {
904 advance([&](const Vec& step) { return cross(u, step) > Coord(0); });
905 };
906
907 // Seed the support for edge 0. The supporting distance stays flat along the
908 // edge itself before it starts to rise, so the walk has to pass the extreme
909 // vertex along the edge direction first -- that is where its own ascent
910 // begins -- or it stalls on the flat step and never leaves vertex 0.
911 {
912 const Vec u = edgeVector(0);
913 advance([&](const Vec& step) { return dot(u, step) > Coord(0); });
914 advanceTop(u);
915 }
916
917 // A minimum-width slab has one supporting line flush with a polygon edge,
918 // so the best of these n candidates is the answer. Measured in the frame of
919 // edge i, the slab spans [0, height] along u turned 90 degrees, scaled by
920 // |u|; its width is therefore height / (u * u) raised to the half, and two
921 // of those are compared squared, by cross-multiplication.
922 std::size_t bestEdge = 0, bestTop = 0;
923 Wide bestSquaredHeight(0), bestSquaredLength(1);
924 bool found = false;
925 for (std::size_t i = 0; i < n; ++i) {
926 const Vec u = edgeVector(i);
927 advanceTop(u);
928
929 const Coord squaredLength = dot(u, u);
930 if (squaredLength == Coord(0)) {
931 // A repeated vertex leaves no direction to measure across, and its
932 // slab would divide by zero. Canonical hull vertices have no such
933 // edge; one reaching here came from a trusted construction.
934 continue;
935 }
936 const Coord height = cross(u, difference(top, i));
937
938 const Wide wideHeight = asNumber<Wide>(height);
939 const Wide wideSquaredHeight = wideHeight * wideHeight;
940 const Wide wideSquaredLength = asNumber<Wide>(squaredLength);
941 if (!found || wideSquaredHeight * bestSquaredLength <
942 bestSquaredHeight * wideSquaredLength) {
943 found = true;
944 bestEdge = i;
945 bestTop = top;
946 bestSquaredHeight = wideSquaredHeight;
947 bestSquaredLength = wideSquaredLength;
948 }
949 }
950 return {bestEdge, bestTop};
951}
952
965template <class Number, class PointType, class LabelType>
966constexpr std::pair<Number, Number>
967minimumWidthFraction(const Convex<PointType, LabelType>& convex, std::size_t edge, std::size_t top) {
968 const std::size_t n = convex.size();
969 const auto base = convex[edge];
970 const auto tip = convex[edge + 1 == n ? std::size_t{0} : edge + 1];
971 const auto support = convex[top];
972
973 const Number ux = asNumber<Number>(tip.x()) - asNumber<Number>(base.x());
974 const Number uy = asNumber<Number>(tip.y()) - asNumber<Number>(base.y());
975 const Number vx = asNumber<Number>(support.x()) - asNumber<Number>(base.x());
976 const Number vy = asNumber<Number>(support.y()) - asNumber<Number>(base.y());
977
978 const Number squaredLength = ux * ux + uy * uy;
979 if (squaredLength == Number(0)) {
980 // Every edge of the polygon is a repeated vertex, which only a trusted
981 // construction produces. It covers one point, whose width is zero;
982 // reporting the fraction as 0/1 keeps that the answer instead of a
983 // division by zero.
984 return {Number(0), Number(1)};
985 }
986 return {ux * vy - uy * vx, squaredLength};
987}
988
989} // namespace detail
990
991template <class PointType, class LabelType>
994 const std::size_t n = size();
995 if (n < 3) {
996 // No width to minimize: the polygon (empty, a point, or a segment) is
997 // its own narrowest slab, of width zero.
999 }
1000
1001 const auto [bestEdge, bestTop] = detail::minimumWidthSupport(*this);
1002
1003 // The two parallel supporting lines, one along the flush edge and one
1004 // through the opposite support along that same edge vector, oriented so
1005 // that the slab lies to the left of both. Nothing here divides, which is
1006 // why the region is exact in NumberType while its width is not.
1007 const PointType base = (*this)[bestEdge];
1008 const PointType tip = (*this)[bestEdge + 1 == n ? std::size_t{0} : bestEdge + 1];
1009 const PointType along = tip - base;
1010 const PointType topSupport = (*this)[bestTop];
1012 Halfplane<PointType>(base, tip),
1013 Halfplane<PointType>(topSupport, topSupport - along),
1014 });
1015}
1016
1017template <class PointType, class LabelType>
1018template <class ResultNumber>
1020 if (size() < 3) {
1021 return ResultNumber{};
1022 }
1023
1024 const auto [bestEdge, bestTop] = detail::minimumWidthSupport(*this);
1025 const auto [height, squaredLength] =
1026 detail::minimumWidthFraction<ResultNumber>(*this, bestEdge, bestTop);
1027 return height * height / squaredLength;
1028}
1029
1030template <class PointType, class LabelType>
1031template <class ApproximateNumber>
1033 if (size() < 3) {
1034 return ApproximateNumber{};
1035 }
1036
1037 const auto [bestEdge, bestTop] = detail::minimumWidthSupport(*this);
1038 const auto [height, squaredLength] =
1039 detail::minimumWidthFraction<ApproximateNumber>(*this, bestEdge, bestTop);
1040 // One square root, of the edge length rather than of the squared width, so
1041 // the division's operands are the exactly computed ones.
1042 return height / std::sqrt(squaredLength);
1043}
1044
1045// -----------------------------------------------------------------------------
1046// Polygon
1047
1048template <class PointType, class LabelType>
1049template <class ResultNumber>
1051 const std::size_t n = size();
1052 if (n < 3) {
1053 // Degenerate polygon (UB per the library contract); fall back to a
1054 // representative point rather than reading past the end.
1056 }
1057
1058 // In canonical form the lexicographically smallest vertex is stored first,
1059 // so p0 is a convex vertex; a and b are its boundary neighbours, and the
1060 // triangle (a, p0, b) is counterclockwise and lies locally inside.
1061 const PointType p0 = (*this)[0];
1062 const Triangle<PointType> t(p0,get(1),get(-1));
1063
1064 if (t.isDegenerate()) {
1065 // Degenerate input polygon: all its vertices have the same x coordinate
1066 return p0;
1067 }
1068
1069 // Leftmost (lexicographically smallest) vertex inside the closed triangle
1070 // (a, p0, b), skipping the three corners at indices 0, 1 and n-1. Because
1071 // p0 is the global lex-min vertex, the diagonal from p0 to this vertex
1072 // stays inside the polygon.
1073 std::optional<PointType> q;
1074 for (std::size_t i = 2; i + 1 < n; ++i) {
1075 const PointType v = (*this)[i];
1076 if (t.interiorContains(v)) {
1077 if (!q.has_value() || v < *q) {
1078 q = v;
1079 }
1080 }
1081 }
1082
1083 if (q.has_value()) {
1084 // p0 q is a diagonal; its midpoint is strictly interior (divides by 2).
1085 return (Point<ResultNumber>(p0) + Point<ResultNumber>(*q)) / ResultNumber(2);
1086 }
1087
1088 // The triangle is an ear: return its interior point (divides by 4).
1089 return t.template pointInside<ResultNumber>();
1090}
1091
1092template <class PointType, class LabelType>
1093template <class OtherShape>
1094constexpr bool Polygon<PointType, LabelType>::pointInsideInteriorContainedIn(const OtherShape& shape) const {
1095 const auto witness = pointInside<NumberType>();
1096 if (interiorContains(witness)) {
1097 return shape.interiorContains(witness);
1098 }
1099 // pointInside() divides by up to 4; integer truncation rounded it onto the
1100 // boundary, so scaling by 4 makes it exact without changing containment.
1101 return (shape * 4).interiorContains((*this * 4).template pointInside<NumberType>());
1102}
1103
1104// -----------------------------------------------------------------------------
1105// PolygonWithHoles
1106
1107template <class PointType, class LabelType>
1108template <class ResultNumber>
1110 const std::size_t n = vertexCount();
1111 if (n == 0) {
1112 return Point<ResultNumber>();
1113 }
1114 ResultNumber cx{};
1115 ResultNumber cy{};
1116 const auto accumulate = [&](const PolygonType& ring) {
1117 for (const auto& vertex : ring) {
1118 cx += detail::asNumber<ResultNumber>(vertex.x());
1119 cy += detail::asNumber<ResultNumber>(vertex.y());
1120 }
1121 };
1122 accumulate(outer_);
1123 for (const auto& hole : holes_) {
1124 accumulate(hole);
1125 }
1126 return Point<ResultNumber>(cx / detail::asNumber<ResultNumber>(n), cy / detail::asNumber<ResultNumber>(n));
1127}
1128
1129template <class PointType, class LabelType>
1130template <class ResultNumber>
1132 if (empty()) {
1133 return Point<ResultNumber>();
1134 }
1135 const NumberType netTwiceArea = twiceArea();
1136 if (netTwiceArea == NumberType(0)) {
1137 // No area to weight by (a collapsed outer ring, or holes cancelling it
1138 // out); fall back to the vertex centroid as Polygon does.
1140 }
1141
1142 // Each ring contributes its own area-weighted centroid, holes negatively.
1143 // Weighting by twice the area keeps the single division to the very end.
1144 ResultNumber cx{};
1145 ResultNumber cy{};
1146 const auto accumulate = [&](const PolygonType& ring, int sign) {
1147 const auto ringTwiceArea = static_cast<ResultNumber>(ring.twiceArea());
1148 const auto ringCentroid = ring.template centroid<ResultNumber>();
1149 const ResultNumber weight = ringTwiceArea * detail::asNumber<ResultNumber>(sign);
1150 cx += ringCentroid.x() * weight;
1151 cy += ringCentroid.y() * weight;
1152 };
1153 accumulate(outer_, 1);
1154 for (const auto& hole : holes_) {
1155 accumulate(hole, -1);
1156 }
1157 const auto denominator = static_cast<ResultNumber>(netTwiceArea);
1158 return Point<ResultNumber>(cx / denominator, cy / denominator);
1159}
1160
1161// -----------------------------------------------------------------------------
1162// PolygonSet
1163
1164template <class PointType, class LabelType>
1165template <class ResultNumber>
1167 const std::size_t n = vertexCount();
1168 if (n == 0) {
1169 return Point<ResultNumber>();
1170 }
1171 ResultNumber cx{};
1172 ResultNumber cy{};
1173 for (const auto& component : components_) {
1174 const auto componentCentroid = component.template verticesCentroid<ResultNumber>();
1175 const auto weight = static_cast<ResultNumber>(component.vertexCount());
1176 cx += componentCentroid.x() * weight;
1177 cy += componentCentroid.y() * weight;
1178 }
1179 return Point<ResultNumber>(cx / detail::asNumber<ResultNumber>(n), cy / detail::asNumber<ResultNumber>(n));
1180}
1181
1182template <class PointType, class LabelType>
1183template <class ResultNumber>
1185 if (empty()) {
1186 return Point<ResultNumber>();
1187 }
1188 const NumberType netTwiceArea = twiceArea();
1189 if (netTwiceArea == NumberType(0)) {
1190 // No area to weight by; fall back to the vertex centroid as Polygon and
1191 // PolygonWithHoles do.
1193 }
1194
1195 // The components have disjoint interiors, so the set's centroid is their
1196 // area-weighted mean. Weighting by twice the area keeps the single division
1197 // to the very end.
1198 ResultNumber cx{};
1199 ResultNumber cy{};
1200 for (const auto& component : components_) {
1201 const auto weight = static_cast<ResultNumber>(component.twiceArea());
1202 const auto componentCentroid = component.template centroid<ResultNumber>();
1203 cx += componentCentroid.x() * weight;
1204 cy += componentCentroid.y() * weight;
1205 }
1206 const auto denominator = static_cast<ResultNumber>(netTwiceArea);
1207 return Point<ResultNumber>(cx / denominator, cy / denominator);
1208}
1209
1210// -----------------------------------------------------------------------------
1211// MonotoneChain
1212
1213template <class PointType, class LabelType, class Storage>
1214template <class ApproximateNumber>
1216 ApproximateNumber total{};
1217 for (std::size_t i = 1; i < points_.size(); ++i) {
1218 total += points_[i - 1].template distance<ApproximateNumber>(points_[i]);
1219 }
1220 return total;
1221}
1222
1223template <class PointType, class LabelType, class Storage>
1225 decltype(std::declval<PointType>().distanceL1(std::declval<PointType>())) total{};
1226 for (std::size_t i = 1; i < points_.size(); ++i) {
1227 total += points_[i - 1].distanceL1(points_[i]);
1228 }
1229 return total;
1230}
1231
1232template <class PointType, class LabelType, class Storage>
1234 decltype(std::declval<PointType>().distanceLInf(std::declval<PointType>())) total{};
1235 for (std::size_t i = 1; i < points_.size(); ++i) {
1236 total += points_[i - 1].distanceLInf(points_[i]);
1237 }
1238 return total;
1239}
1240
1241template <class PointType, class LabelType, class Storage>
1242template <class ResultNumber>
1244 if (size() < 2) {
1245 // Degenerate case
1246 return size() == 0 ? PointType() : (*this)[0];
1247 }
1248
1249 return Segment<PointType>((*this)[0], (*this)[1]).template pointInside<ResultNumber>();
1250}
1251
1252// -----------------------------------------------------------------------------
1253// Polyline
1254
1255template <class PointType, class LabelType>
1256template <class ApproximateNumber>
1258 ApproximateNumber total{};
1259 for (std::size_t i = 1; i < points_.size(); ++i) {
1260 total += points_[i - 1].template distance<ApproximateNumber>(points_[i]);
1261 }
1262 return total;
1263}
1264
1265template <class PointType, class LabelType>
1267 decltype(std::declval<PointType>().distanceL1(std::declval<PointType>())) total{};
1268 for (std::size_t i = 1; i < points_.size(); ++i) {
1269 total += points_[i - 1].distanceL1(points_[i]);
1270 }
1271 return total;
1272}
1273
1274template <class PointType, class LabelType>
1276 decltype(std::declval<PointType>().distanceLInf(std::declval<PointType>())) total{};
1277 for (std::size_t i = 1; i < points_.size(); ++i) {
1278 total += points_[i - 1].distanceLInf(points_[i]);
1279 }
1280 return total;
1281}
1282
1283template <class PointType, class LabelType>
1284template <class ResultNumber>
1286 if (size() < 2) {
1287 // Degenerate case
1288 return size() == 0 ? PointType() : (*this)[0];
1289 }
1290
1291 return Segment<PointType>((*this)[0], (*this)[1]).template pointInside<ResultNumber>();
1292}
1293
1294template <class PointType, class LabelType, class Storage>
1295template <class OtherShape>
1297 const auto witness = pointInside<NumberType>();
1298 if (interiorContains(witness)) {
1299 return shape.interiorContains(witness);
1300 }
1301 // pointInside() is the first edge's midpoint (divides by 2); integer
1302 // truncation rounded it onto a vertex, so scaling by 2 makes it exact
1303 // without changing the containment relation.
1304 return (shape * 2).interiorContains((*this * 2).template pointInside<NumberType>());
1305}
1306
1307
1308// -----------------------------------------------------------------------------
1309// HalfplaneIntersection
1310
1311template <class PointType, class LabelType>
1312template <class ResultNumber>
1314 if (empty_) {
1315 return ResultNumber{};
1316 }
1317 if (!isBounded()) {
1318 throw std::logic_error("HalfplaneIntersection::twiceArea requires a bounded region");
1319 }
1320 return static_cast<ResultNumber>(asConvex<ResultNumber>().twiceArea());
1321}
1322
1323template <class PointType, class LabelType>
1324template <class ResultNumber>
1326 if (empty_) {
1327 return ResultNumber{};
1328 }
1329 if (!isBounded()) {
1330 throw std::logic_error("HalfplaneIntersection::area requires a bounded region");
1331 }
1332 return asConvex<ResultNumber>().template area<ResultNumber>();
1333}
1334
1335template <class PointType, class LabelType>
1336template <class ResultNumber>
1338 if (empty_) {
1339 return Point<ResultNumber>();
1340 }
1341 if (!isBounded()) {
1342 throw std::logic_error("HalfplaneIntersection::centroid requires a bounded region");
1343 }
1344 const auto c = asConvex<ResultNumber>().template centroid<ResultNumber>();
1345 return Point<ResultNumber>(c.x(), c.y());
1346}
1347
1348template <class PointType, class LabelType>
1349template <class ResultNumber>
1351 if (empty_ || halfplanes_.empty()) {
1352 return Point<ResultNumber>(); // the whole plane's representative is the origin
1353 }
1354 if (halfplanes_.size() == 1) {
1355 return halfplanes_[0].template pointInside<ResultNumber>();
1356 }
1357 if (isBounded()) {
1359 }
1360 // Unbounded: clip a box around a finite anchor point of the region, then
1361 // take that bounded piece's interior representative. A vertex, or (for a
1362 // slab) any point of the first boundary line, anchors the box on the
1363 // region.
1364 NumberType anchorX{};
1365 NumberType anchorY{};
1366 bool anchored = false;
1367 for (std::size_t i = 0; i < size(); ++i) {
1368 if (vertexExists(i)) {
1369 const auto v = vertex<double>(i);
1370 anchorX = static_cast<NumberType>(v.x() < 0 ? -std::floor(-v.x()) : std::floor(v.x()));
1371 anchorY = static_cast<NumberType>(v.y() < 0 ? -std::floor(-v.y()) : std::floor(v.y()));
1372 anchored = true;
1373 break;
1374 }
1375 }
1376 if (!anchored) {
1377 anchorX = static_cast<NumberType>(halfplanes_[0].source().x());
1378 anchorY = static_cast<NumberType>(halfplanes_[0].source().y());
1379 }
1380 const NumberType margin(4);
1381 const PointType lo(anchorX - margin, anchorY - margin);
1382 const PointType hi(anchorX + margin, anchorY + margin);
1383 const PointType lohi(lo.x(), hi.y());
1384 const PointType hilo(hi.x(), lo.y());
1385 HalfplaneIntersection clipped(*this);
1386 clipped.insert(HalfplaneType(lo, hilo));
1387 clipped.insert(HalfplaneType(hilo, hi));
1388 clipped.insert(HalfplaneType(hi, lohi));
1389 clipped.insert(HalfplaneType(lohi, lo));
1390 if (clipped.empty()) {
1391 // The anchor rounding missed the region; fall back to a boundary point.
1392 return Point<ResultNumber>(detail::asNumber<ResultNumber>(halfplanes_[0].source().x()),
1393 detail::asNumber<ResultNumber>(halfplanes_[0].source().y()));
1394 }
1395 return clipped.template asConvex<ResultNumber>().template pointInside<ResultNumber>();
1396}
1397
1398template <class PointType, class LabelType>
1399template <class OtherShape>
1401 const OtherShape& shape) const {
1402 // Exact witness: rational unless the coordinates are already floating point
1403 // (region_exact_number_t is not yet visible at this include position).
1404 using Exact = std::conditional_t<std::is_floating_point_v<NumberType>, double, Rational<BigInt>>;
1405 return shape.interiorContains(pointInside<Exact>());
1406}
1407
1408} // namespace pgl
Arbitrary precision signed integer.
Definition bigint.hpp:157
Exact rational number class template.
Definition rational.hpp:106
Minkowski sums of two shapes, and the operator+ that spells them.
Definition arrangement.hpp:67
HalfplaneIntersection() -> HalfplaneIntersection< Point<>, NoLabel >
Definition halfplaneintersection.hpp:2308
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
@ edge
Definition bitmatrix.hpp:37
@ vertex
Definition bitmatrix.hpp:37
Point() -> Point< int >
Rational< BigInt > ERational
Exact, overflow-free result used when integral coordinates require fractions.
Definition rational.hpp:1151
constexpr std::partial_ordering dotSign(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b)
Tells if the angle between two vectors is acute, right, or obtuse.
Definition orientation.hpp:688
Convex() -> Convex< Point<>, NoLabel >
Definition convex.hpp:3311
constexpr auto orientationDeterminant(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Returns the signed orientation determinant of three points.
Definition orientation.hpp:518
Disk() -> Disk< Point<>, NoLabel >
Deduces a default disk with Point<> boundary points and no label.
Definition disk.hpp:1691
constexpr auto operator*(const Transformation< Number > &transformation, const ShapeT &shape)
Applies a transformation to any supported shape.
Definition transformations.hpp:2200
Exact low-level orientation and incircle predicates.
Closed convex polygon stored by its vertices.
Definition convex.hpp:170
constexpr auto twiceArea() const
Computes twice the area of the convex polygon.
Definition measures.hpp:516
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the convex polygon.
Definition measures.hpp:582
constexpr Point< ResultNumber > verticesCentroid() const
Computes the centroid of the vertex set.
Definition measures.hpp:566
constexpr HalfplaneIntersection< PointType > asHalfplaneIntersection() const
Returns the convex polygon as a half-plane intersection.
Definition convex.hpp:672
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:791
auto difference(const Shape< OtherPoint > &other) const
Returns the regularized set difference of the two shapes (A ∖ B), re-dispatching through the wrapper'...
Definition convex.hpp:2538
PointType::NumberType NumberType
Definition convex.hpp:172
constexpr size_t maxIndex() const
Returns the index of the maximum vertex (rightmost and highest in case of ties).
Definition predicates.hpp:1022
constexpr auto area() const
Computes the area of the convex polygon.
Definition measures.hpp:531
constexpr bool interiorContains(const EmptyShape< EmptyPoint > &) const
Tests whether this shape's interior contains the other shape (A∖∂A ⊇ B).
Definition convex.hpp:1340
constexpr ResultNumber squaredMinimumWidth() const
Returns the squared minimum width of the convex polygon.
Definition measures.hpp:1019
ApproximateNumber minimumWidth() const
Returns the minimum width of the convex polygon.
Definition measures.hpp:1032
constexpr HalfplaneIntersection< PointType > smallestEnclosingSlab() const
Returns the narrowest slab containing the convex polygon.
Definition measures.hpp:993
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:603
constexpr Point< ResultNumber > centroid() const
Computes the centroid of the convex polygon.
Definition measures.hpp:538
constexpr Segment< PointType > diameter() const
Returns a segment realizing the diameter (the farthest vertex pair).
Definition measures.hpp:696
constexpr std::vector< std::pair< std::size_t, std::size_t > > antipodalPairs() const
Returns every antipodal vertex-index pair, via rotating calipers.
Definition measures.hpp:615
size_t size() const
Returns the number of vertices in the convex polygon.
Definition convex.hpp:840
constexpr HalfplaneIntersection< PointType > smallestEnclosingRectangle() const
Returns the smallest-area rectangle containing the convex polygon.
Definition measures.hpp:725
PointType_ PointType
Definition convex.hpp:171
Closed Euclidean disk stored by boundary points plus optional disk label.
Definition disk.hpp:66
Intersection of closed half-planes; convex but possibly unbounded or empty.
Definition halfplaneintersection.hpp:244
Halfplane< PointType > HalfplaneType
Definition halfplaneintersection.hpp:248
constexpr bool empty() const
Returns whether the region is the empty set.
Definition halfplaneintersection.hpp:649
constexpr bool vertexExists(std::size_t i) const
Returns whether the half-plane pair (i, i+1) (cyclically) defines a vertex of the region.
Definition halfplaneintersection.hpp:854
constexpr auto area() const
Returns the area of the region.
Definition measures.hpp:1325
constexpr bool isBounded() const
Returns whether the region is bounded.
Definition halfplaneintersection.hpp:811
PointType_ PointType
Definition halfplaneintersection.hpp:245
PointType::NumberType NumberType
Definition halfplaneintersection.hpp:246
constexpr Convex< Point< ResultNumber, typename PointType::LabelType > > asConvex() const
Returns the region as a convex polygon.
Definition halfplaneintersection.hpp:955
constexpr std::size_t size() const
Returns the number of stored (non-redundant) half-planes.
Definition halfplaneintersection.hpp:596
friend struct HalfplaneIntersection
Definition halfplaneintersection.hpp:2308
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether an exact interior witness of this region lies in the interior of the given shape.
Definition measures.hpp:1400
constexpr auto twiceArea() const
Returns twice the area of the region.
Definition measures.hpp:1313
constexpr bool insert(const OtherHalfplane &other)
Intersects the region with one more half-plane.
Definition halfplaneintersection.hpp:509
constexpr Point< ResultNumber > centroid() const
Returns the centroid of the region.
Definition measures.hpp:1337
constexpr Point< ResultNumber > pointInside() const
Returns a representative point of the region: a point of its interior when the region is full-dimensi...
Definition measures.hpp:1350
Closed half-plane defined by an oriented boundary line.
Definition halfplane.hpp:51
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:505
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the halfplane.
Definition measures.hpp:486
constexpr const PointType & target() const
Returns the target boundary point.
Definition halfplane.hpp:193
constexpr const PointType & source() const
Returns the source boundary point.
Definition halfplane.hpp:181
constexpr ResultNumber slope() const
Returns the slope of the boundary line.
Definition measures.hpp:478
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the line.
Definition measures.hpp:197
constexpr const PointType & max() const
Returns the largest stored defining point.
Definition line.hpp:189
constexpr const PointType & min() const
Returns the smallest stored defining point.
Definition line.hpp:180
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:203
PointType::NumberType NumberType
Definition line.hpp:54
constexpr ResultNumber slope() const
Returns the slope of the line.
Definition measures.hpp:189
constexpr ResultNumber area() const
Returns the area of the line.
Definition measures.hpp:178
constexpr NumberType twiceArea() const
Returns twice the area of the line.
Definition measures.hpp:183
PointType_ PointType
Definition monotonechain.hpp:147
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:1296
constexpr std::size_t size() const
Returns the number of vertices in the chain.
Definition monotonechain.hpp:393
constexpr auto lengthLInf() const
Computes the Chebyshev (LInf) length of the chain.
Definition measures.hpp:1233
constexpr bool interiorContains(const OtherPoint &point) const
Tests whether this shape's interior contains the other shape (A∖∂A ⊇ B).
Definition interiorcontains.hpp:1371
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the chain.
Definition measures.hpp:1243
ApproximateNumber length() const
Computes the Euclidean length of the chain (the sum of its edge lengths).
Definition measures.hpp:1215
constexpr auto lengthL1() const
Computes the Manhattan (L1) length of the chain.
Definition measures.hpp:1224
constexpr ResultNumber slope() const
Returns the slope of the line.
Definition measures.hpp:224
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the oriented line.
Definition measures.hpp:232
constexpr NumberType twiceArea() const
Returns twice the area of the line.
Definition measures.hpp:218
constexpr const PointType & target() const
Returns the target defining point.
Definition orientedline.hpp:195
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:238
constexpr ResultNumber area() const
Returns the area of the line.
Definition measures.hpp:213
constexpr Line< PointType > asLine() const
Returns the line without orientation.
Definition orientedline.hpp:321
PointType::NumberType NumberType
Definition orientedline.hpp:55
constexpr const PointType & source() const
Returns the source defining point.
Definition orientedline.hpp:183
ApproximateNumber length() const
Returns the Euclidean length.
Definition measures.hpp:125
PointType::NumberType NumberType
Definition orientedsegment.hpp:46
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:168
constexpr auto lengthL1() const
Returns the Manhattan length.
Definition measures.hpp:130
constexpr ResultNumber slope() const
Returns the slope of the segment.
Definition measures.hpp:141
constexpr const PointType & source() const
Returns the source endpoint.
Definition orientedsegment.hpp:178
constexpr auto lengthLInf() const
Returns the Chebyshev length.
Definition measures.hpp:135
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the segment.
Definition measures.hpp:162
constexpr ResultNumber area() const
Returns the area of the segment.
Definition measures.hpp:109
constexpr const PointType & target() const
Returns the target endpoint.
Definition orientedsegment.hpp:190
constexpr Segment< PointType > asSegment() const
Returns the segment without orientation.
Definition orientedsegment.hpp:322
constexpr NumberType twiceArea() const
Returns twice the area of the segment.
Definition measures.hpp:114
constexpr auto squaredLength() const
Returns the squared Euclidean length.
Definition measures.hpp:119
constexpr Point< ResultNumber > midpoint() const
Returns the midpoint of the segment.
Definition measures.hpp:154
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:178
constexpr Segment< PointType > diameter() const
Returns an unordered segment defining the diameter.
Definition measures.hpp:148
Two-dimensional point with optional label payload.
Definition point.hpp:129
constexpr const NumberType & x() const
Returns the x coordinate.
Definition point.hpp:193
constexpr const NumberType & y() const
Returns the y coordinate.
Definition point.hpp:205
constexpr bool interiorContains(const EmptyShape< EmptyPoint > &) const
Tests whether this shape's interior contains the other shape (A∖∂A ⊇ B).
Definition point.hpp:471
constexpr Point< ResultNumber > centroid() const
Computes the area-weighted centroid of the set.
Definition measures.hpp:1184
constexpr ResultNumber twiceArea() const
Computes twice the area of the set.
Definition polygonset.hpp:625
typename PointType::NumberType NumberType
Definition polygonset.hpp:167
constexpr Point< ResultNumber > verticesCentroid() const
Computes the centroid of the vertex set over every ring of every component.
Definition measures.hpp:1166
constexpr std::size_t vertexCount() const
Returns the total number of vertices over every ring of every component.
Definition polygonset.hpp:377
constexpr bool empty() const
Tests whether the set has no components at all.
Definition polygonset.hpp:485
constexpr const ComponentType & component(std::size_t index) const
Accesses a component by index.
Definition polygonset.hpp:271
constexpr ResultNumber twiceArea() const
Computes twice the area of the region.
Definition polygonwithholes.hpp:577
constexpr const PolygonType & hole(std::size_t index) const
Accesses a hole by index.
Definition polygonwithholes.hpp:196
typename PointType::NumberType NumberType
Definition polygonwithholes.hpp:91
constexpr Point< ResultNumber > verticesCentroid() const
Computes the centroid of the vertex set over all rings.
Definition measures.hpp:1109
constexpr Point< ResultNumber > centroid() const
Computes the area-weighted centroid of the region.
Definition measures.hpp:1131
Polygon< PointType > PolygonType
Definition polygonwithholes.hpp:93
constexpr bool empty() const
Tests whether the region has no outer boundary at all.
Definition polygonwithholes.hpp:430
constexpr std::size_t vertexCount() const
Returns the total number of vertices over all rings.
Definition polygonwithholes.hpp:279
constexpr Point< ResultNumber > pointInside() const
Returns a point strictly inside the (simple) polygon.
Definition measures.hpp:1050
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:1094
constexpr bool interiorContains(const EmptyShape< EmptyPoint > &) const
Tests whether this shape's interior contains the other shape (A∖∂A ⊇ B).
Definition polygon.hpp:1537
constexpr std::size_t size() const
Returns the number of vertices in the polygon.
Definition polygon.hpp:259
constexpr ResultNumber twiceArea() const
Computes twice the (unsigned) area of the polygon via the shoelace formula.
Definition polygon.hpp:273
PointType_ PointType
Definition polygon.hpp:60
constexpr PointType get(std::ptrdiff_t index) const
Cyclic access: same as operator[] but index is taken modulo size(); negative indices wrap from the en...
Definition polygon.hpp:169
constexpr Point< ResultNumber > verticesCentroid() const
Computes the centroid of the vertex set (the average of the vertices).
Definition polygon.hpp:892
constexpr auto lengthLInf() const
Computes the Chebyshev (LInf) length of the polyline.
Definition measures.hpp:1275
PointType_ PointType
Definition polyline.hpp:70
ApproximateNumber length() const
Computes the Euclidean length of the polyline (the sum of its edge lengths).
Definition measures.hpp:1257
constexpr auto lengthL1() const
Computes the Manhattan (L1) length of the polyline.
Definition measures.hpp:1266
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the polyline.
Definition measures.hpp:1285
constexpr std::size_t size() const
Returns the number of vertices in the polyline.
Definition polyline.hpp:388
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:273
constexpr ResultNumber area() const
Returns the area of the ray.
Definition measures.hpp:248
PointType::NumberType NumberType
Definition ray.hpp:53
constexpr const PointType & target() const
Returns the second stored point defining the direction.
Definition ray.hpp:193
constexpr ResultNumber slope() const
Returns the slope of the supporting line.
Definition measures.hpp:259
constexpr NumberType twiceArea() const
Returns twice the area of the ray.
Definition measures.hpp:253
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the ray.
Definition measures.hpp:267
constexpr const PointType & source() const
Returns the source point of the ray.
Definition ray.hpp:181
constexpr ResultNumber area() const
Returns the rectangle area.
Definition measures.hpp:284
constexpr Point< ResultNumber > midpoint() const
Returns the midpoint of the rectangle.
Definition measures.hpp:304
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the rectangle.
Definition measures.hpp:331
constexpr auto height() const
Returns the rectangle height.
Definition rectangle.hpp:378
constexpr Point< ResultNumber > centroid() const
Returns the centroid of the rectangle.
Definition measures.hpp:313
constexpr const PointType & min() const
Returns the minimum corner (min x, min y).
Definition rectangle.hpp:347
constexpr bool empty() const
Returns whether the rectangle is the empty set of points.
Definition rectangle.hpp:290
constexpr Point< ResultNumber > center() const
Returns the center of the rectangle.
Definition measures.hpp:325
constexpr auto twiceArea() const
Returns twice the rectangle area.
Definition measures.hpp:291
constexpr bool interiorContains(const EmptyShape< EmptyPoint > &) const
Tests whether this shape's interior contains the other shape (A∖∂A ⊇ B).
Definition rectangle.hpp:862
constexpr Disk< PointType, NoLabel > circumcircle() const
Returns the circumcircle of the rectangle.
Definition measures.hpp:318
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:337
constexpr const PointType & max() const
Returns the maximum corner (max x, max y).
Definition rectangle.hpp:359
constexpr auto width() const
Returns the rectangle width.
Definition rectangle.hpp:368
constexpr Segment< PointType > diameter() const
Returns a segment defining a diameter.
Definition measures.hpp:297
Unoriented closed segment between two endpoints plus optional segment label.
Definition segment.hpp:58
constexpr auto lengthLInf() const
Returns the Chebyshev length.
Definition measures.hpp:60
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:94
constexpr auto lengthL1() const
Returns the Manhattan length.
Definition measures.hpp:55
constexpr ResultNumber slope() const
Returns the slope of the segment.
Definition measures.hpp:66
constexpr Point< ResultNumber > midpoint() const
Returns the midpoint of the segment.
Definition measures.hpp:79
constexpr const PointType & max() const
Returns the largest stored endpoint.
Definition segment.hpp:199
ApproximateNumber length() const
Returns the Euclidean length.
Definition measures.hpp:50
constexpr const PointType & min() const
Returns the smallest stored endpoint.
Definition segment.hpp:190
PointType::NumberType NumberType
Definition segment.hpp:60
constexpr ResultNumber area() const
Returns the area of the segment.
Definition measures.hpp:34
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:100
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the segment.
Definition measures.hpp:88
constexpr NumberType twiceArea() const
Returns twice the area of the segment.
Definition measures.hpp:39
constexpr Segment diameter() const
Returns a segment defining the diameter.
Definition measures.hpp:73
constexpr auto squaredLength() const
Returns the squared Euclidean length.
Definition measures.hpp:44
constexpr Segment()=default
Creates the degenerate segment (0,0)--(0,0).
constexpr bool interiorContains(const EmptyShape< EmptyPoint > &) const
Tests whether this shape's interior contains the other shape (A∖∂A ⊇ B).
Definition segment.hpp:736
Closed triangle stored by three vertices.
Definition triangle.hpp:53
constexpr Disk< PointType, NoLabel > circumcircle() const
Returns the circumcircle of the triangle.
Definition measures.hpp:376
constexpr const PointType & b() const
Returns the second vertex.
Definition triangle.hpp:217
constexpr bool isIsosceles() const
Tests whether two sides have the same length.
Definition measures.hpp:459
constexpr const PointType & a() const
Returns the first vertex.
Definition triangle.hpp:208
constexpr bool isRectangle() const
Tests whether the triangle has a right angle.
Definition measures.hpp:420
constexpr Segment< PointType > diameter() const
Returns a segment defining the diameter.
Definition measures.hpp:381
constexpr bool pointInsideInteriorContainedIn(const OtherShape &shape) const
Tests whether some point in this shape's relative interior lies in the strict interior of shape.
Definition measures.hpp:409
constexpr auto squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance to the given shape.
Definition distance.hpp:670
constexpr bool isObtuse() const
Tests whether the triangle has an obtuse angle.
Definition measures.hpp:442
constexpr ResultNumber area() const
Returns the non-negative area of the triangle.
Definition measures.hpp:357
constexpr NumberType twiceArea() const
Returns twice the area of the triangle.
Definition measures.hpp:351
constexpr bool isDegenerate() const
Tests whether the three vertices are collinear.
Definition predicates.hpp:223
constexpr Point< ResultNumber > pointInside() const
Returns a point inside the triangle.
Definition measures.hpp:401
PointType::NumberType NumberType
Definition triangle.hpp:57
constexpr const PointType & c() const
Returns the third vertex.
Definition triangle.hpp:226
constexpr bool interiorContains(const EmptyShape< EmptyPoint > &) const
Tests whether this shape's interior contains the other shape (A∖∂A ⊇ B).
Definition triangle.hpp:806
constexpr Point< ResultNumber > centroid() const
Returns the arithmetic centroid.
Definition measures.hpp:364