Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
orientation.hpp
Go to the documentation of this file.
1#pragma once
2
4
12
13#include <compare>
14#include <type_traits>
15#include <vector>
16
17
18namespace pgl {
19
20namespace detail {
21
32template <class... Numbers>
33using sign_coordinate_t = detail::promoted_number_t<std::common_type_t<Numbers...>>;
34
35template <class AX, class BX, class CX>
36using orientation_coordinate_t = sign_coordinate_t<AX, BX, CX>;
37template <class AX, class BX>
38using dot_coordinate_t = sign_coordinate_t<AX, BX>;
39
46template <class... Numbers>
47using incircle_coordinate_t = detail::promoted_number_t<sign_coordinate_t<Numbers...>>;
48
56template <class Ordering>
57constexpr int signOf(const Ordering& order) {
58 return order > 0 ? 1 : (order < 0 ? -1 : 0);
59}
60
70struct Approximate {
71 double value = 0.0;
72 double error = 0.0;
73};
74
83struct ApproximatePoint {
84 Approximate x;
85 Approximate y;
86};
87
94constexpr double approximateAbs(double value) { return value < 0.0 ? -value : value; }
95
105inline constexpr double approximateRoundoff = 0x1p-52;
106
113inline constexpr double approximateNarrowing = 0x1p-52;
114
128inline constexpr double approximateConversion = 0x1p-45;
129
133inline constexpr double approximateMargin = 1.0 + 0x1p-20;
134
138constexpr Approximate operator+(const Approximate& a, const Approximate& b) {
139 const double value = a.value + b.value;
140 return {value, a.error + b.error + approximateRoundoff * approximateAbs(value)};
141}
142
146constexpr Approximate operator-(const Approximate& a, const Approximate& b) {
147 const double value = a.value - b.value;
148 return {value, a.error + b.error + approximateRoundoff * approximateAbs(value)};
149}
150
157constexpr Approximate operator*(const Approximate& a, const Approximate& b) {
158 const double value = a.value * b.value;
159 return {value,
160 approximateAbs(a.value) * b.error + approximateAbs(b.value) * a.error +
161 a.error * b.error + approximateRoundoff * approximateAbs(value)};
162}
163
178template <class Int>
179constexpr Approximate approximate(const pgl::Rational<Int>& value) {
180 const double quotient = static_cast<double>(value);
181 return {quotient, approximateConversion * approximateAbs(quotient)};
182}
183
187template <class Number>
188constexpr Approximate approximate(const Number& value) {
189 if constexpr (std::is_floating_point_v<Number>) {
190 if constexpr (numeric_limits<Number>::digits <= numeric_limits<double>::digits &&
191 numeric_limits<Number>::max_exponent <= numeric_limits<double>::max_exponent) {
192 return {static_cast<double>(value), 0.0}; // float, double: exact
193 } else {
194 const double narrowed = static_cast<double>(value); // long double
195 return {narrowed, approximateNarrowing * approximateAbs(narrowed)};
196 }
197 } else if constexpr (numeric_limits<Number>::is_integer &&
198 numeric_limits<Number>::digits <= numeric_limits<double>::digits) {
199 return {static_cast<double>(value), 0.0}; // int and narrower: exact
200 } else if constexpr (extended_integral<Number>) {
201 const double narrowed = static_cast<double>(value); // int64_t, int128
202 return {narrowed, approximateNarrowing * approximateAbs(narrowed)};
203 } else {
204 const double narrowed = static_cast<double>(value); // BigInt and the like
205 return {narrowed, approximateConversion * approximateAbs(narrowed)};
206 }
207}
208
210template <class PointType>
211constexpr ApproximatePoint approximatePoint(const PointType& point) {
212 return {approximate(point.x()), approximate(point.y())};
213}
214
229constexpr std::partial_ordering approximateSign(const Approximate& quantity) {
230 const double bound = quantity.error * approximateMargin + 0x1p-1000;
231 if (quantity.value > bound) {
232 return std::partial_ordering::greater;
233 }
234 if (quantity.value < -bound) {
235 return std::partial_ordering::less;
236 }
237 return std::partial_ordering::unordered;
238}
239
261template <class Coordinate>
262inline constexpr bool filtersSign = arbitraryPrecision<Coordinate>;
263
269constexpr std::partial_ordering crossFilter(
270 const Approximate& ax, const Approximate& ay,
271 const Approximate& bx, const Approximate& by) {
272 return approximateSign(ax * by - ay * bx);
273}
274
281constexpr std::partial_ordering orientationFilter(
282 const ApproximatePoint& a, const ApproximatePoint& b, const ApproximatePoint& c) {
283 return crossFilter(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
284}
285
287template <class ANumber, class ALabel, class BNumber, class BLabel, class CNumber, class CLabel>
288constexpr std::partial_ordering exactOrientationSign(
289 const Point<ANumber, ALabel>& a,
290 const Point<BNumber, BLabel>& b,
291 const Point<CNumber, CLabel>& c) {
292 using Coordinate = orientation_coordinate_t<ANumber, BNumber, CNumber>;
293
294 const auto abx = asNumber<Coordinate>(b.x()) - asNumber<Coordinate>(a.x());
295 const auto aby = asNumber<Coordinate>(b.y()) - asNumber<Coordinate>(a.y());
296 const auto acx = asNumber<Coordinate>(c.x()) - asNumber<Coordinate>(a.x());
297 const auto acy = asNumber<Coordinate>(c.y()) - asNumber<Coordinate>(a.y());
298
299 return threeWay(abx * acy, aby * acx);
300}
301
322template <class PointType, bool Filters>
323struct FilteredPoint {
324 const PointType* point;
325 ApproximatePoint approximation;
326};
327
328template <class PointType>
329struct FilteredPoint<PointType, false> {
330 const PointType* point;
331};
332
341template <class Coordinate, class PointType>
342constexpr FilteredPoint<PointType, filtersSign<Coordinate>> filtered(const PointType& point) {
343 if constexpr (filtersSign<Coordinate>) {
344 return {&point, approximatePoint(point)};
345 } else {
346 return {&point};
347 }
348}
349
353template <class Coordinate, class PointType>
354constexpr FilteredPoint<PointType, filtersSign<Coordinate>> filtered(const PointType&&) = delete;
355
365template <class PointType>
366constexpr ApproximatePoint approximationOf(const FilteredPoint<PointType, true>& point) {
367 return point.approximation;
368}
369
370template <class PointType>
371constexpr ApproximatePoint approximationOf(const FilteredPoint<PointType, false>& point) {
372 return approximatePoint(*point.point);
373}
374
385template <class APoint, class BPoint, class CPoint, bool Filters>
386class DeferredOrientationSign {
387public:
388 constexpr DeferredOrientationSign(const APoint& a, const BPoint& b, const CPoint& c,
389 std::partial_ordering proved)
390 : a_(&a), b_(&b), c_(&c), proved_(proved) {}
391
393 [[nodiscard]] constexpr bool decided() const {
394 return proved_ != std::partial_ordering::unordered;
395 }
396
398 [[nodiscard]] constexpr std::partial_ordering value() const {
399 return decided() ? proved_ : exactOrientationSign(*a_, *b_, *c_);
400 }
401
402private:
403 const APoint* a_;
404 const BPoint* b_;
405 const CPoint* c_;
406 std::partial_ordering proved_;
407};
408
410template <class APoint, class BPoint, class CPoint>
411class DeferredOrientationSign<APoint, BPoint, CPoint, false> {
412public:
413 constexpr DeferredOrientationSign(const APoint& a, const BPoint& b, const CPoint& c)
414 : a_(&a), b_(&b), c_(&c) {}
415
416 [[nodiscard]] constexpr bool decided() const { return false; }
417
418 [[nodiscard]] constexpr std::partial_ordering value() const {
419 return exactOrientationSign(*a_, *b_, *c_);
420 }
421
422private:
423 const APoint* a_;
424 const BPoint* b_;
425 const CPoint* c_;
426};
427
433template <class APoint, bool AFilters, class BPoint, bool BFilters, class CPoint, bool CFilters>
434constexpr auto orientationSignOf(
435 const FilteredPoint<APoint, AFilters>& a,
436 const FilteredPoint<BPoint, BFilters>& b,
437 const FilteredPoint<CPoint, CFilters>& c) {
438 // The gate is this predicate's own, read at the type its exact fallback
439 // would evaluate in — never the operands', which say only whether an
440 // approximation was already at hand. Deciding it operand-side would both
441 // filter where @ref orientationSign does not (an int64_t coordinate keeps
442 // approximations for its in-circle tests, whose determinant needs them,
443 // but not for these, whose fits in an int128) and fail to filter where it
444 // does (`int` vertices against a rational query point).
445 using Coordinate = orientation_coordinate_t<typename APoint::NumberType,
446 typename BPoint::NumberType,
447 typename CPoint::NumberType>;
448 if constexpr (filtersSign<Coordinate>) {
449 return DeferredOrientationSign<APoint, BPoint, CPoint, true>{
450 *a.point, *b.point, *c.point,
451 orientationFilter(approximationOf(a), approximationOf(b), approximationOf(c))};
452 } else {
453 return DeferredOrientationSign<APoint, BPoint, CPoint, false>{
454 *a.point, *b.point, *c.point};
455 }
456}
457
459template <class... Signs>
460constexpr bool allDecided(const Signs&... signs) {
461 return (signs.decided() && ...);
462}
463
485template <class Coordinate, class PointType>
486constexpr FilteredPoint<PointType, filtersSign<Coordinate>> filtered(
487 const PointType& point,
488 const std::vector<ApproximatePoint>& approximations,
489 std::size_t index) {
490 if constexpr (filtersSign<Coordinate>) {
491 return {&point, approximations[index]};
492 } else {
493 return {&point};
494 }
495}
496
502constexpr std::partial_ordering dotFilter(
503 const Approximate& ax, const Approximate& ay,
504 const Approximate& bx, const Approximate& by) {
505 return approximateSign(ax * bx + ay * by);
506}
507} // namespace detail
508
517template <class ANumber, class ALabel, class BNumber, class BLabel, class CNumber, class CLabel>
519 const Point<ANumber, ALabel>& a,
520 const Point<BNumber, BLabel>& b,
521 const Point<CNumber, CLabel>& c) {
522 using Coordinate = detail::orientation_coordinate_t<ANumber, BNumber, CNumber>;
523
524 const auto abx = detail::asNumber<Coordinate>(b.x()) - detail::asNumber<Coordinate>(a.x());
525 const auto aby = detail::asNumber<Coordinate>(b.y()) - detail::asNumber<Coordinate>(a.y());
526 const auto acx = detail::asNumber<Coordinate>(c.x()) - detail::asNumber<Coordinate>(a.x());
527 const auto acy = detail::asNumber<Coordinate>(c.y()) - detail::asNumber<Coordinate>(a.y());
528
529 return abx * acy - aby * acx;
530}
531
543template <class ANumber, class ALabel, class BNumber, class BLabel, class CNumber, class CLabel>
544constexpr std::partial_ordering orientationSign(
545 const Point<ANumber, ALabel> &a,
546 const Point<BNumber, BLabel> &b,
547 const Point<CNumber, CLabel> &c) {
548 using Coordinate = detail::orientation_coordinate_t<ANumber, BNumber, CNumber>;
549
550 // A double evaluation that carries its own error bound settles the sign for
551 // all but the near-degenerate inputs, sparing the exact arithmetic below.
552 // It only reports a sign it has proved, so this is a shortcut, not an
553 // approximation; see @ref detail::approximateSign.
554 if constexpr (detail::filtersSign<Coordinate>) {
555 const detail::Approximate ax = detail::approximate(a.x());
556 const detail::Approximate ay = detail::approximate(a.y());
557 const std::partial_ordering filtered = detail::crossFilter(
558 detail::approximate(b.x()) - ax, detail::approximate(b.y()) - ay,
559 detail::approximate(c.x()) - ax, detail::approximate(c.y()) - ay);
560 if (filtered != std::partial_ordering::unordered) {
561 return filtered;
562 }
563 }
564
565 return detail::exactOrientationSign(a, b, c);
566}
567
582template <class UNumber, class ULabel, class VNumber, class VLabel>
583constexpr std::partial_ordering crossSign(
584 const Point<UNumber, ULabel>& u,
585 const Point<VNumber, VLabel>& v) {
586 using Coordinate = detail::sign_coordinate_t<UNumber, VNumber>;
587
588 if constexpr (detail::filtersSign<Coordinate>) { // see orientationSign
589 const std::partial_ordering filtered = detail::crossFilter(
590 detail::approximate(u.x()), detail::approximate(u.y()),
591 detail::approximate(v.x()), detail::approximate(v.y()));
592 if (filtered != std::partial_ordering::unordered) {
593 return filtered;
594 }
595 }
596
597 return detail::threeWay(detail::asNumber<Coordinate>(u.x()) * detail::asNumber<Coordinate>(v.y()),
598 detail::asNumber<Coordinate>(u.y()) * detail::asNumber<Coordinate>(v.x()));
599}
600
614template <class ANumber, class ALabel, class BNumber, class BLabel,
615 class PNumber, class PLabel, class QNumber, class QLabel>
616constexpr std::partial_ordering crossSign(
617 const Point<ANumber, ALabel>& a,
618 const Point<BNumber, BLabel>& b,
619 const Point<PNumber, PLabel>& p,
620 const Point<QNumber, QLabel>& q) {
621 using Coordinate = detail::sign_coordinate_t<ANumber, BNumber, PNumber, QNumber>;
622
623 if constexpr (detail::filtersSign<Coordinate>) { // see orientationSign
624 const std::partial_ordering filtered = detail::crossFilter(
625 detail::approximate(b.x()) - detail::approximate(a.x()),
626 detail::approximate(b.y()) - detail::approximate(a.y()),
627 detail::approximate(q.x()) - detail::approximate(p.x()),
628 detail::approximate(q.y()) - detail::approximate(p.y()));
629 if (filtered != std::partial_ordering::unordered) {
630 return filtered;
631 }
632 }
633
634 const auto abx = detail::asNumber<Coordinate>(b.x()) - detail::asNumber<Coordinate>(a.x());
635 const auto aby = detail::asNumber<Coordinate>(b.y()) - detail::asNumber<Coordinate>(a.y());
636 const auto pqx = detail::asNumber<Coordinate>(q.x()) - detail::asNumber<Coordinate>(p.x());
637 const auto pqy = detail::asNumber<Coordinate>(q.y()) - detail::asNumber<Coordinate>(p.y());
638
639 return detail::threeWay(abx * pqy, aby * pqx);
640}
641
650template <class ANumber, class ALabel, class BNumber, class BLabel, class CNumber, class CLabel>
651constexpr bool collinear(
652 const Point<ANumber, ALabel>& a,
653 const Point<BNumber, BLabel>& b,
654 const Point<CNumber, CLabel>& c) {
655 return orientationSign(a, b, c) == 0;
656}
657
672template <class ANumber, class ALabel, class BNumber, class BLabel>
673constexpr bool sameDirection(
674 const Point<ANumber, ALabel>& a1,
675 const Point<ANumber, ALabel>& a2,
676 const Point<BNumber, BLabel>& b1,
677 const Point<BNumber, BLabel>& b2) {
678 return crossSign(a1, a2, b1, b2) == 0;
679}
680
687template <class ANumber, class ALabel, class BNumber, class BLabel>
688constexpr std::partial_ordering dotSign(
689 const Point<ANumber, ALabel>& a,
690 const Point<BNumber, BLabel>& b) {
691 using Coordinate = detail::sign_coordinate_t<ANumber, BNumber>;
692
693 if constexpr (detail::filtersSign<Coordinate>) { // see orientationSign
694 const std::partial_ordering filtered = detail::dotFilter(
695 detail::approximate(a.x()), detail::approximate(a.y()),
696 detail::approximate(b.x()), detail::approximate(b.y()));
697 if (filtered != std::partial_ordering::unordered) {
698 return filtered;
699 }
700 }
701
702 const auto x = detail::asNumber<Coordinate>(a.x()) * detail::asNumber<Coordinate>(b.x());
703 const auto y = detail::asNumber<Coordinate>(a.y()) * detail::asNumber<Coordinate>(b.y());
704
705 return detail::threeWay(x, -y);
706}
707
722template <class ANumber, class ALabel, class BNumber, class BLabel,
723 class PNumber, class PLabel, class QNumber, class QLabel>
724constexpr std::partial_ordering dotSign(
725 const Point<ANumber, ALabel>& a,
726 const Point<BNumber, BLabel>& b,
727 const Point<PNumber, PLabel>& p,
728 const Point<QNumber, QLabel>& q) {
729 using Coordinate = detail::sign_coordinate_t<ANumber, BNumber, PNumber, QNumber>;
730
731 if constexpr (detail::filtersSign<Coordinate>) { // see orientationSign
732 const std::partial_ordering filtered = detail::dotFilter(
733 detail::approximate(b.x()) - detail::approximate(a.x()),
734 detail::approximate(b.y()) - detail::approximate(a.y()),
735 detail::approximate(q.x()) - detail::approximate(p.x()),
736 detail::approximate(q.y()) - detail::approximate(p.y()));
737 if (filtered != std::partial_ordering::unordered) {
738 return filtered;
739 }
740 }
741
742 const auto abx = detail::asNumber<Coordinate>(b.x()) - detail::asNumber<Coordinate>(a.x());
743 const auto aby = detail::asNumber<Coordinate>(b.y()) - detail::asNumber<Coordinate>(a.y());
744 const auto pqx = detail::asNumber<Coordinate>(q.x()) - detail::asNumber<Coordinate>(p.x());
745 const auto pqy = detail::asNumber<Coordinate>(q.y()) - detail::asNumber<Coordinate>(p.y());
746
747 return detail::threeWay(abx * pqx, -(aby * pqy));
748}
749
750namespace detail {
751
764constexpr std::partial_ordering inCircleFilter(
765 const ApproximatePoint& a,
766 const ApproximatePoint& b,
767 const ApproximatePoint& c,
768 const ApproximatePoint& d) {
769 const Approximate adx = a.x - d.x;
770 const Approximate ady = a.y - d.y;
771 const Approximate bdx = b.x - d.x;
772 const Approximate bdy = b.y - d.y;
773 const Approximate cdx = c.x - d.x;
774 const Approximate cdy = c.y - d.y;
775 const Approximate abdet = adx * bdy - bdx * ady;
776 const Approximate bcdet = bdx * cdy - cdx * bdy;
777 const Approximate cadet = cdx * ady - adx * cdy;
778 const Approximate alift = adx * adx + ady * ady;
779 const Approximate blift = bdx * bdx + bdy * bdy;
780 const Approximate clift = cdx * cdx + cdy * cdy;
781 return approximateSign(alift * bcdet + blift * cadet + clift * abdet);
782}
783
785template <class ANumber, class ALabel, class BNumber, class BLabel, class CNumber, class CLabel, class DNumber, class DLabel>
786constexpr std::partial_ordering exactInCircleSign(
787 const Point<ANumber, ALabel>& a,
788 const Point<BNumber, BLabel>& b,
789 const Point<CNumber, CLabel>& c,
790 const Point<DNumber, DLabel>& d) {
791 using Coordinate = incircle_coordinate_t<ANumber, BNumber, CNumber, DNumber>;
792
793 const auto adx = asNumber<Coordinate>(a.x()) - asNumber<Coordinate>(d.x());
794 const auto ady = asNumber<Coordinate>(a.y()) - asNumber<Coordinate>(d.y());
795 const auto bdx = asNumber<Coordinate>(b.x()) - asNumber<Coordinate>(d.x());
796 const auto bdy = asNumber<Coordinate>(b.y()) - asNumber<Coordinate>(d.y());
797 const auto cdx = asNumber<Coordinate>(c.x()) - asNumber<Coordinate>(d.x());
798 const auto cdy = asNumber<Coordinate>(c.y()) - asNumber<Coordinate>(d.y());
799 const auto abdet = adx * bdy - bdx * ady;
800 const auto bcdet = bdx * cdy - cdx * bdy;
801 const auto cadet = cdx * ady - adx * cdy;
802 const auto alift = adx * adx + ady * ady;
803 const auto blift = bdx * bdx + bdy * bdy;
804 const auto clift = cdx * cdx + cdy * cdy;
805 return threeWay(alift * bcdet + blift * cadet, -clift * abdet);
806}
807
817template <class APoint, bool AFilters, class BPoint, bool BFilters,
818 class CPoint, bool CFilters, class DPoint, bool DFilters>
819constexpr std::partial_ordering inCircleSignOf(
820 const FilteredPoint<APoint, AFilters>& a,
821 const FilteredPoint<BPoint, BFilters>& b,
822 const FilteredPoint<CPoint, CFilters>& c,
823 const FilteredPoint<DPoint, DFilters>& d) {
824 using Coordinate = incircle_coordinate_t<typename APoint::NumberType,
825 typename BPoint::NumberType,
826 typename CPoint::NumberType,
827 typename DPoint::NumberType>;
828 if constexpr (filtersSign<Coordinate>) { // see orientationSignOf
829 const std::partial_ordering filtered = inCircleFilter(
830 approximationOf(a), approximationOf(b), approximationOf(c), approximationOf(d));
831 if (filtered != std::partial_ordering::unordered) {
832 return filtered;
833 }
834 }
835 return exactInCircleSign(*a.point, *b.point, *c.point, *d.point);
836}
837
838} // namespace detail
839
856template <class ANumber, class ALabel, class BNumber, class BLabel, class CNumber, class CLabel, class DNumber, class DLabel>
857constexpr auto inCircleDeterminant(
858 const Point<ANumber, ALabel>& a,
859 const Point<BNumber, BLabel>& b,
860 const Point<CNumber, CLabel>& c,
861 const Point<DNumber, DLabel>& d) {
862 using Coordinate = detail::incircle_coordinate_t<ANumber, BNumber, CNumber, DNumber>;
863
864 const auto adx = detail::asNumber<Coordinate>(a.x()) - detail::asNumber<Coordinate>(d.x());
865 const auto ady = detail::asNumber<Coordinate>(a.y()) - detail::asNumber<Coordinate>(d.y());
866 const auto bdx = detail::asNumber<Coordinate>(b.x()) - detail::asNumber<Coordinate>(d.x());
867 const auto bdy = detail::asNumber<Coordinate>(b.y()) - detail::asNumber<Coordinate>(d.y());
868 const auto cdx = detail::asNumber<Coordinate>(c.x()) - detail::asNumber<Coordinate>(d.x());
869 const auto cdy = detail::asNumber<Coordinate>(c.y()) - detail::asNumber<Coordinate>(d.y());
870 const auto abdet = adx * bdy - bdx * ady;
871 const auto bcdet = bdx * cdy - cdx * bdy;
872 const auto cadet = cdx * ady - adx * cdy;
873 const auto alift = adx * adx + ady * ady;
874 const auto blift = bdx * bdx + bdy * bdy;
875 const auto clift = cdx * cdx + cdy * cdy;
876 return alift * bcdet + blift * cadet + clift * abdet;
877}
878
893template <class ANumber, class ALabel, class BNumber, class BLabel, class CNumber, class CLabel, class DNumber, class DLabel>
894constexpr std::partial_ordering inCircleSign(
895 const Point<ANumber, ALabel>& a,
896 const Point<BNumber, BLabel>& b,
897 const Point<CNumber, CLabel>& c,
898 const Point<DNumber, DLabel>& d) {
899 using Coordinate = detail::incircle_coordinate_t<ANumber, BNumber, CNumber, DNumber>;
900
901 // A double evaluation that carries its own error bound settles the sign for
902 // all but the near-degenerate inputs, sparing the exact arithmetic below.
903 // It only reports a sign it has proved, so this is a shortcut, not an
904 // approximation; see @ref detail::inCircleFilter. A caller running this
905 // repeatedly over one point array should keep the conversions — see
906 // @ref detail::filtered — and call @ref detail::inCircleSignOf instead.
907 if constexpr (detail::filtersSign<Coordinate>) {
908 const std::partial_ordering filtered = detail::inCircleFilter(
909 detail::approximatePoint(a), detail::approximatePoint(b),
910 detail::approximatePoint(c), detail::approximatePoint(d));
911 if (filtered != std::partial_ordering::unordered) {
912 return filtered;
913 }
914 }
915
916 return detail::exactInCircleSign(a, b, c, d);
917}
918
919} // namespace pgl
Public declaration of pgl::EmptyShape.
Definition arrangement.hpp:67
constexpr std::partial_ordering inCircleSign(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c, const Point< DNumber, DLabel > &d)
Classifies a point with respect to the circumcircle of three others.
Definition orientation.hpp:894
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
constexpr auto inCircleDeterminant(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c, const Point< DNumber, DLabel > &d)
Returns the signed in-circle determinant of a query point.
Definition orientation.hpp:857
Point() -> Point< int >
constexpr bool sameDirection(const Point< ANumber, ALabel > &a1, const Point< ANumber, ALabel > &a2, const Point< BNumber, BLabel > &b1, const Point< BNumber, BLabel > &b2)
Tests whether the directions a1 -> a2 and b1 -> b2 are parallel.
Definition orientation.hpp:673
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
constexpr std::partial_ordering orientationSign(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Classifies the orientation of three points.
Definition orientation.hpp:544
constexpr std::partial_ordering crossSign(const Point< UNumber, ULabel > &u, const Point< VNumber, VLabel > &v)
Classifies the turn from one vector to another.
Definition orientation.hpp:583
Rational(T) -> Rational< T >
constexpr bool collinear(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Tests whether three points are collinear.
Definition orientation.hpp:651
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
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