Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
point.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
43# ifndef POINT_H
44# define POINT_H
45
46# include <cmath>
47
48# include <cstddef>
49# include <limits>
50# include <array>
51# include <functional>
52
53# include <iomanip>
54# include <string>
55
56# include <ahAssert.H>
57# include <ahUtils.H>
58# include <ah-errors.H>
59# include <utility>
60
61# include <gmpfrxx.h>
62
69# if defined(_LIBCPP_VERSION)
76inline std::ostream & operator<<(std::ostream & o, mpq_srcptr q)
77{
78 o << mpq_get_d(q);
79 return o;
80}
81
88inline std::ostream & operator<<(std::ostream & o, mpz_srcptr z)
89{
90 o << mpz_get_d(z);
91 return o;
92}
93
100inline std::ostream & operator<<(std::ostream & o, mpf_srcptr f)
101{
102 o << mpf_get_d(f);
103 return o;
104}
105# endif
106
107namespace Aleph
108{
116
122 inline double geom_number_to_double(const Geom_Number & n)
123 {
124 return n.get_d();
125 }
126
127 // TODO: provide helpers for rotating special figures (e.g., ellipses) by
128 // rotating the Cartesian axes instead of recomputing points from scratch.
129
130
131 // Legacy double-precision constants kept for backward compatibility.
132 constexpr double PI = 3.1415926535897932384626433832795028841971693993751;
133 constexpr double PI_2 = PI / 2.0;
134 constexpr double PI_4 = PI / 4.0;
135
140 inline const Geom_Number &geom_pi()
141 {
142 static const Geom_Number pi = acos(mpfr_class(-1));
143 return pi;
144 }
145
146
147 class Point;
148 class Polar_Point;
149 class Segment;
150 class Triangle;
151 class Ellipse;
152
153
162 inline Geom_Number
163 area_of_parallelogram(const Point & a, const Point & b, const Point & c);
164
165
168 const Geom_Number & y)
169 {
170 return hypot(mpfr_class(x), mpfr_class(y));
171 }
172
174 [[deprecated("Use euclidean_distance() instead")]]
175 inline Geom_Number pitag(const Geom_Number & x, const Geom_Number & y)
176 {
177 return euclidean_distance(x, y);
178 }
179
182 {
183 return atan(mpfr_class(m));
184 }
185
187 inline Geom_Number arctan2(const Geom_Number & m, const Geom_Number & n)
188 {
189 return atan2(mpfr_class(m), mpfr_class(n));
190 }
191
193 inline Geom_Number sinus(const Geom_Number & x)
194 {
195 return sin(mpfr_class(x));
196 }
197
200 {
201 return cos(mpfr_class(x));
202 }
203
206 {
207 return sqrt(mpfr_class(x));
208 }
209
214 {
215 Geom_Object() = default;
216
217 virtual ~Geom_Object() = default;
218 };
219
228 class Point : public Geom_Object
229 {
230 friend class Segment;
231 friend class Triangle;
232 friend class Polar_Point;
233
236
237 public:
242 Point() : Geom_Object(), x_(0), y_(0)
243 { /* empty */
244 }
245
251 Point(const Geom_Number & x, const Geom_Number & y)
252 : Geom_Object(), x_(x), y_(y)
253 {
254 // empty
255 }
256
261 inline Point(const Polar_Point & pp);
262
268 [[nodiscard]] bool operator ==(const Point & point) const noexcept
269 {
270 return x_ == point.x_ and y_ == point.y_;
271 }
272
278 [[nodiscard]] bool operator !=(const Point & point) const noexcept
279 {
280 return not (*this == point);
281 }
282
288 [[nodiscard]] bool operator <(const Point & point) const noexcept
289 {
290 return x_ < point.x_ or (x_ == point.x_ and y_ < point.y_);
291 }
292
298 [[nodiscard]] Point operator +(const Point & p) const
299 {
300 return {x_ + p.x_, y_ + p.y_};
301 }
302
309 {
310 x_ += p.x_;
311 y_ += p.y_;
312
313 return *this;
314 }
315
321 Point operator -(const Point & p) const
322 {
323 return {x_ - p.x_, y_ - p.y_};
324 }
325
332 {
333 x_ -= p.x_;
334 y_ -= p.y_;
335
336 return *this;
337 }
338
344 {
345 return {-x_, -y_};
346 }
347
354 {
355 return {x_ * s, y_ * s};
356 }
357
364 {
365 return {x_ / s, y_ / s};
366 }
367
373 [[nodiscard]] Geom_Number dot(const Point & p) const
374 {
375 return x_ * p.x_ + y_ * p.y_;
376 }
377
383 [[nodiscard]] Geom_Number cross(const Point & p) const
384 {
385 return x_ * p.y_ - y_ * p.x_;
386 }
387
394 {
395 return x_ * x_ + y_ * y_;
396 }
397
403 {
404 return euclidean_distance(x_, y_);
405 }
406
413 {
414 const Geom_Number n = norm();
415 ah_domain_error_if(n == 0) << "Cannot normalize the zero vector";
416 return *this / n;
417 }
418
425 {
426 const Geom_Number c = cosinus(angle);
427 const Geom_Number s = sinus(angle);
428 return {x_ * c - y_ * s, x_ * s + y_ * c};
429 }
430
437 [[nodiscard]] Point lerp(const Point & other, const Geom_Number & t) const
438 {
439 const Geom_Number one_minus_t = Geom_Number(1) - t;
440 return *this * one_minus_t + other * t;
441 }
442
448 [[nodiscard]] Point midpoint(const Point & other) const
449 {
450 return lerp(other, Geom_Number(1, 2));
451 }
452
458 {
459 return x_;
460 }
461
467 {
468 return y_;
469 }
470
477 [[nodiscard]] bool is_colinear_with(const Point & p1, const Point & p2) const
478 {
479 return area_of_parallelogram(*this, p1, p2) == 0;
480 }
481
487 [[nodiscard]] inline bool is_colinear_with(const Segment & s) const;
488
495 [[nodiscard]] bool is_left_of(const Point & p1, const Point & p2) const
496 {
497 return area_of_parallelogram(p1, p2, *this) > 0;
498 }
499
501 [[deprecated("Use is_left_of() instead")]]
502 [[nodiscard]] bool is_to_left_from(const Point & p1, const Point & p2) const
503 {
504 return is_left_of(p1, p2);
505 }
506
513 [[nodiscard]] bool is_to_right_from(const Point & p1, const Point & p2) const
514 {
515 return area_of_parallelogram(p1, p2, *this) < 0;
516 }
517
524 [[nodiscard]] bool is_to_left_on_from(const Point & p1, const Point & p2) const
525 {
526 return not is_to_right_from(p1, p2);
527 }
528
535 [[nodiscard]] bool is_right_on_of(const Point & p1, const Point & p2) const
536 {
537 return not is_left_of(p1, p2);
538 }
539
541 [[deprecated("Use is_right_on_of() instead")]]
542 [[nodiscard]] bool is_to_right_on_from(const Point & p1, const Point & p2) const
543 {
544 return is_right_on_of(p1, p2);
545 }
546
553 [[nodiscard]] bool is_clockwise_with(const Point & p1, const Point & p2) const
554 {
555 return area_of_parallelogram(*this, p1, p2) < 0;
556 }
557
563 [[nodiscard]] inline bool is_left_of(const Segment & s) const;
564
570 [[nodiscard]] inline bool is_right_of(const Segment & s) const;
571
573 [[deprecated("Use is_left_of() instead")]]
574 [[nodiscard]] bool is_to_left_from(const Segment & s) const
575 {
576 return is_left_of(s);
577 }
578
580 [[deprecated("Use is_right_of() instead")]]
581 [[nodiscard]] bool is_to_right_from(const Segment & s) const
582 {
583 return is_right_of(s);
584 }
585
591 [[nodiscard]] inline bool is_clockwise_with(const Segment & s) const;
592
599 [[nodiscard]] bool is_between(const Point & p1, const Point & p2) const
600 {
601 if (not this->is_colinear_with(p1, p2))
602 return false;
603
604 if (p1.get_x() == p2.get_x())
605 return (p1.get_y() <= this->get_y() and this->get_y() <= p2.get_y())
606 or (p1.get_y() >= this->get_y() and (this->get_y() >= p2.get_y()));
607
608 return (p1.get_x() <= this->get_x() and (this->get_x() <= p2.get_x()))
609 or (p1.get_x() >= this->get_x() and (this->get_x() >= p2.get_x()));
610 }
611
618 [[nodiscard]] const Point &nearest_point(const Point & p1, const Point & p2) const
619 {
620 return this->distance_squared_to(p1) < this->distance_squared_to(p2) ? p1 : p2;
621 }
622
629 [[nodiscard]] inline bool is_inside(const Segment & s) const;
630
637 [[nodiscard]] inline bool is_inside(const Ellipse & e) const;
638
645 [[nodiscard]] inline bool intersects_with(const Ellipse & e) const;
646
651 [[nodiscard]] std::string to_string() const
652 {
653 return "(" + std::to_string(geom_number_to_double(x_)) + "," +
654 std::to_string(geom_number_to_double(y_)) + ")";
655 }
656
661 operator std::string() const { return to_string(); }
662
668 [[nodiscard]] inline Geom_Number distance_squared_to(const Point & that) const;
669
675 [[nodiscard]] inline Geom_Number distance_to(const Point & p) const;
676
678 [[deprecated("Use distance_to() instead")]]
680 {
681 return distance_to(p);
682 }
683
688 [[nodiscard]] const Point &highest_point() const { return *this; }
689
694 [[nodiscard]] const Point &lowest_point() const { return *this; }
695
700 [[nodiscard]] const Point &leftmost_point() const { return *this; }
701
706 [[nodiscard]] const Point &rightmost_point() const { return *this; }
707 };
708
710 [[nodiscard]] inline Point operator*(const Geom_Number & s, const Point & p)
711 {
712 return p * s;
713 }
714
715
726 {
727 friend class Point;
728
729 Geom_Number r_ = 0; // distance from origin
730 Geom_Number theta_ = 0; // angle in radians from positive x-axis
731
732 public:
737 [[nodiscard]] const Geom_Number &get_r() const { return r_; }
738
743 [[nodiscard]] const Geom_Number &get_theta() const { return theta_; }
744
751 : r_(r), theta_(theta)
752 {
753 // empty
754 }
755
760 explicit Polar_Point(const Point & p)
761 {
762 const Geom_Number & x = p.get_x();
763 const Geom_Number & y = p.get_y();
764
765 r_ = euclidean_distance(x, y);
766 theta_ = arctan2(y, x);
767 }
768
771
778 {
779 const Point cartesian(*this);
780 const bool east = cartesian.get_x() >= 0;
781 const bool north = cartesian.get_y() >= 0;
782
783 if (east and north)
784 return First;
785 if (not east and north)
786 return Second;
787 if (not east and not north)
788 return Third;
789 return Fourth;
790 }
791
796 [[nodiscard]] std::string to_string() const
797 {
798 return "[" + std::to_string(geom_number_to_double(r_)) + "," +
799 std::to_string(geom_number_to_double(theta_)) + "]";
800 }
801
806 Polar_Point() = default;
807 };
808
809
810 inline
812 : x_(pp.r_ * cosinus(pp.theta_)), y_(pp.r_ * sinus(pp.theta_))
813 {
814 // empty
815 }
816
817
826 class Segment : public Geom_Object
827 {
828 friend class Point;
829 friend class Triangle;
830
832
837 [[nodiscard]] double compute_slope() const
838 {
839 if (tgt_.get_x() == src_.get_x())
840 {
841 if (src_.get_y() < tgt_.get_y())
842 return std::numeric_limits<double>::max();
843 return -std::numeric_limits<double>::max();
844 }
845
846 const Geom_Number slope_ = (tgt_.get_y() - src_.get_y()) / (tgt_.get_x() - src_.get_x());
847
848 return slope_.get_d();
849 }
850
851 public:
859 [[nodiscard]] bool operator ==(const Segment & s) const noexcept
860 {
861 return (src_ == s.src_ and tgt_ == s.tgt_) or
862 (src_ == s.tgt_ and tgt_ == s.src_);
863 }
864
870 [[nodiscard]] bool operator !=(const Segment & s) const noexcept
871 {
872 return not (*this == s);
873 }
874
880 {
881 return src_.get_y() > tgt_.get_y() ? src_ : tgt_;
882 }
883
889 {
890 return src_.get_y() < tgt_.get_y() ? src_ : tgt_;
891 }
892
898 {
899 return src_.get_x() < tgt_.get_x() ? src_ : tgt_;
900 }
901
907 {
908 return src_.get_x() > tgt_.get_x() ? src_ : tgt_;
909 }
910
916
922
926 Segment() = default;
927
934 : Geom_Object(), src_(std::move(src)), tgt_(std::move(tgt))
935 {
936 // empty
937 }
938
939 private:
947 static
948 Point compute_tgt_point(const Point & src, // Point of origin
949 const Geom_Number & m, // slope
950 const Geom_Number & d) // segment length
951 {
952 const Geom_Number den2 = 1 + m * m;
953
955
956 const Geom_Number x = src.get_x() + d / den;
957
958 const Geom_Number y = src.get_y() + d * m / den;
959
960 return {x, y};
961 }
962
963 public:
972 Segment(Point src, const Geom_Number & m, const Geom_Number & l)
973 : Geom_Object(), src_(std::move(src)), tgt_(compute_tgt_point(src_, m, l))
974 {
975 // empty
976 }
977
983 Segment(const Segment & sg, const Geom_Number & dist)
984 {
985 const Segment perp = sg.mid_perpendicular(dist);
986
987 const Point mid_point = sg.mid_point();
988
989 const Point diff_point = mid_point - perp.get_src_point();
990
993 }
994
999 [[nodiscard]] double slope() const
1000 {
1001 return compute_slope();
1002 }
1003
1010 {
1011 ah_domain_error_if(tgt_.get_x() == src_.get_x()) << "Vertical segment has undefined slope";
1012 return (tgt_.get_y() - src_.get_y()) / (tgt_.get_x() - src_.get_x());
1013 }
1014
1026 {
1027 const Geom_Number x1 = tgt_.get_x() - src_.get_x();
1028 const Geom_Number x2 = s.tgt_.get_x() - s.src_.get_x();
1029 const Geom_Number y1 = tgt_.get_y() - src_.get_y();
1030 const Geom_Number y2 = s.tgt_.get_y() - s.src_.get_y();
1031 const Geom_Number dot = x1 * x2 + y1 * y2;
1032 const Geom_Number det = x1 * y2 - y1 * x2;
1033
1034 const Geom_Number angle = arctan2(det, dot);
1035
1036 if (angle < 0)
1037 return geom_number_to_double(angle + 2 * geom_pi());
1038
1039 return angle.get_d();
1040 }
1041
1047 {
1048 const Segment x_axis(Point(0, 0), Point(1, 0));
1049 return x_axis.counterclockwise_angle_with(*this);
1050 }
1051
1057 {
1058 return euclidean_distance(tgt_.get_x() - src_.get_x(), tgt_.get_y() - src_.get_y());
1059 }
1060
1062 [[deprecated("Use length() instead")]]
1063 [[nodiscard]] Geom_Number size() const { return length(); }
1064
1070 [[nodiscard]] bool is_colinear_with(const Point & p) const
1071 {
1072 return p.is_colinear_with(src_, tgt_);
1073 }
1074
1081 [[nodiscard]] bool is_left_of(const Point & p) const
1082 {
1083 return p.is_right_of(*this);
1084 }
1085
1092 [[nodiscard]] bool is_right_of(const Point & p) const
1093 {
1094 return p.is_left_of(*this);
1095 }
1096
1098 [[deprecated("Use is_left_of() instead")]]
1099 [[nodiscard]] bool is_to_left_from(const Point & p) const
1100 {
1101 return is_left_of(p);
1102 }
1103
1105 [[deprecated("Use is_right_of() instead")]]
1106 [[nodiscard]] bool is_to_right_from(const Point & p) const
1107 {
1108 return is_right_of(p);
1109 }
1110
1116 {
1117 const Geom_Number x = (src_.get_x() + tgt_.get_x()) / 2;
1118 const Geom_Number y = (src_.get_y() + tgt_.get_y()) / 2;
1119
1120 return {x, y};
1121 }
1122
1128 {
1129 return {tgt_, src_};
1130 }
1131
1137 [[nodiscard]] Point at(const Geom_Number & t) const
1138 {
1139 return src_.lerp(tgt_, t);
1140 }
1141
1147 [[nodiscard]] Point project(const Point & p) const
1148 {
1149 const Point dir = tgt_ - src_;
1150 const Geom_Number len2 = dir.dot(dir);
1151
1152 if (len2 == 0)
1153 return src_;
1154
1155 Geom_Number t = (p - src_).dot(dir) / len2;
1156 if (t < 0)
1157 t = 0;
1158 else if (t > 1)
1159 t = 1;
1160
1161 return at(t);
1162 }
1163
1170 {
1171 return p.distance_to(project(p));
1172 }
1173
1174
1180 [[nodiscard]] const Point &nearest_point(const Point & p) const
1181 {
1183 }
1184
1191 {
1192 if (src_ == tgt_)
1193 return *this;
1194
1195 const Point dir = tgt_ - src_;
1196 const Geom_Number len2 = dir.dot(dir);
1197 ah_domain_error_if(len2 == 0) << "Cannot get perpendicular of zero-length segment";
1198
1199 const Geom_Number t = (p - src_).dot(dir) / len2;
1200 const Point foot = src_.lerp(tgt_, t);
1201
1202 // The perpendicular segment goes from the foot to p.
1203 return {foot, p};
1204 }
1205
1213 {
1214 const Point mid = mid_point();
1215 const Point dir = (tgt_ - src_).normalize();
1216 const Point perp_dir(-dir.get_y(), dir.get_x()); // Rotated by +90 degrees
1217
1218 return {mid - perp_dir * dist, mid + perp_dir * dist};
1219 }
1220
1229 {
1230 // Each pair of endpoints must lie strictly on opposite sides of the
1231 // other segment's supporting line. Using the product of signed areas
1232 // guarantees that shared endpoints (area == 0) are never treated as a
1233 // proper crossing.
1234 const auto d1 = area_of_parallelogram(s.src_, s.tgt_, src_);
1235 const auto d2 = area_of_parallelogram(s.src_, s.tgt_, tgt_);
1236 const auto d3 = area_of_parallelogram(src_, tgt_, s.src_);
1237 const auto d4 = area_of_parallelogram(src_, tgt_, s.tgt_);
1238 return (d1 * d2 < 0) and (d3 * d4 < 0);
1239 }
1240
1246 [[nodiscard]] bool contains(const Point & p) const
1247 {
1248 return p.is_between(src_, tgt_);
1249 }
1250
1252 [[deprecated("Use contains() instead")]]
1253 [[nodiscard]] bool contains_to(const Point & p) const { return contains(p); }
1254
1260 [[nodiscard]] bool contains(const Segment & s) const
1261 {
1263 }
1264
1266 [[deprecated("Use contains() instead")]]
1267 [[nodiscard]] bool contains_to(const Segment & s) const { return contains(s); }
1268
1274 [[nodiscard]] bool intersects_with(const Segment & s) const
1275 {
1276 if (this->intersects_properly_with(s))
1277 return true;
1278
1279 return this->contains(s.src_) or this->contains(s.tgt_) or
1280 s.contains(this->src_) or s.contains(this->tgt_);
1281 }
1282
1288 [[nodiscard]] inline bool intersects_with(const Triangle & t) const;
1289
1295 [[nodiscard]] inline bool intersects_with(const Ellipse & e) const;
1296
1302 [[nodiscard]] bool is_parallel_with(const Segment & s) const
1303 {
1304 return (tgt_ - src_).cross(s.tgt_ - s.src_) == 0;
1305 }
1306
1314 {
1315 const Point v1 = tgt_ - src_;
1316 const Point v2 = s.tgt_ - s.src_;
1317 const Geom_Number cross = v1.cross(v2);
1318
1319 ah_domain_error_if(cross == 0) << "Segments are parallel";
1320
1321 const Geom_Number t = (s.src_ - src_).cross(v2) / cross;
1322
1323 return src_ + v1 * t;
1324 }
1325
1327 enum Sense { E, NE, N, NW, W, SW, S, SE };
1328
1334 {
1335 const Point dir = tgt_ - src_;
1336 if (dir.get_x() > 0) // headed east?
1337 {
1338 if (dir.get_y() > 0) // and north?
1339 return NE;
1340 if (dir.get_y() < 0) // and south?
1341 return SE;
1342 return E;
1343 }
1344 if (dir.get_x() < 0) // headed west?
1345 {
1346 if (dir.get_y() > 0) // and north?
1347 return NW;
1348 if (dir.get_y() < 0) // and south?
1349 return SW;
1350 return W;
1351 }
1352
1353 // Vertical segment
1354 return dir.get_y() > 0 ? N : S;
1355 }
1356
1361 void enlarge_src(const Geom_Number & dist)
1362 {
1363 if (src_ == tgt_) return;
1364 src_ -= (tgt_ - src_).normalize() * dist;
1365 }
1366
1371 void enlarge_tgt(const Geom_Number & dist)
1372 {
1373 if (src_ == tgt_) return;
1374 tgt_ += (tgt_ - src_).normalize() * dist;
1375 }
1376
1381 [[nodiscard]] std::string to_string() const
1382 {
1383 return src_.to_string() + tgt_.to_string();
1384 }
1385
1389 operator std::string() const
1390 {
1391 return to_string();
1392 }
1393
1399 {
1400 if (angle == 0)
1401 return;
1402 tgt_ = src_ + (tgt_ - src_).rotate(angle);
1403 }
1404
1411 [[nodiscard]] inline Segment intersection_with(const Triangle & t) const;
1412
1419 [[nodiscard]] inline Segment intersection_with(const Ellipse & e) const;
1420 };
1421
1422 // Return true if this point lies inside segment s.
1423 inline bool Point::is_inside(const Segment & s) const
1424 {
1425 return s.contains(*this);
1426 }
1427
1428 // Return true if this point is colinear with segment s.
1429 inline bool Point::is_colinear_with(const Segment & s) const
1430 {
1431 return this->is_colinear_with(s.src_, s.tgt_);
1432 }
1433
1434 // Return true if this point is to the left of the segment s.
1435 inline bool Point::is_left_of(const Segment & s) const
1436 {
1437 return this->is_left_of(s.src_, s.tgt_);
1438 }
1439
1440 // Return true if this point is to the right of the segment s.
1441 inline bool Point::is_right_of(const Segment & s) const
1442 {
1443 return area_of_parallelogram(s.src_, s.tgt_, *this) < 0;
1444 }
1445
1446 // Return true if the sequence (this, segment) is clockwise.
1447 inline bool Point::is_clockwise_with(const Segment & s) const
1448 {
1449 return this->is_clockwise_with(s.src_, s.tgt_);
1450 }
1451
1452
1453 // Return the squared Euclidean distance to that.
1455 {
1456 const Geom_Number dx = this->x_ - that.x_;
1457 const Geom_Number dy = this->y_ - that.y_;
1458 return dx * dx + dy * dy;
1459 }
1460
1461
1462 // Return the Euclidean distance to p.
1463 inline Geom_Number Point::distance_to(const Point & p) const
1464 {
1465 return euclidean_distance(p.x_ - x_, p.y_ - y_);
1466 }
1467
1468
1477 class Triangle : public Geom_Object
1478 {
1479 friend class Point;
1480 friend class Segment;
1481
1483
1484 Geom_Number area_; // signed area * 2
1485
1486 public:
1495 : p1_(std::move(p1)), p2_(std::move(p2)), p3_(std::move(p3))
1496 {
1497 // Note: area_ is twice the signed area
1499 ah_domain_error_if(area_ == 0) << "The three points of a triangle cannot be collinear";
1500 }
1501
1508 Triangle(Point p, const Segment & s)
1509 : p1_(std::move(p)), p2_(s.src_), p3_(s.tgt_)
1510 {
1512 ah_domain_error_if(area_ == 0) << "The three points of a triangle cannot be collinear";
1513 }
1514
1521 Triangle(const Segment & s, Point p)
1522 : p1_(s.src_), p2_(s.tgt_), p3_(std::move(p))
1523 {
1525 ah_domain_error_if(area_ == 0) << "The three points of a triangle cannot be collinear";
1526 }
1527
1533 {
1534 return abs(area_) / 2;
1535 }
1536
1541 [[nodiscard]] bool is_clockwise() const
1542 {
1543 return area_ < 0;
1544 }
1545
1550 [[nodiscard]] const Point &highest_point() const
1551 {
1552 const Point & max = p1_.get_y() > p2_.get_y() ? p1_ : p2_;
1553 return p3_.get_y() > max.get_y() ? p3_ : max;
1554 }
1555
1560 [[nodiscard]] const Point &lowest_point() const
1561 {
1562 const Point & min = p1_.get_y() < p2_.get_y() ? p1_ : p2_;
1563 return p3_.get_y() < min.get_y() ? p3_ : min;
1564 }
1565
1570 [[nodiscard]] const Point &leftmost_point() const
1571 {
1572 const Point & min = p1_.get_x() < p2_.get_x() ? p1_ : p2_;
1573 return p3_.get_x() < min.get_x() ? p3_ : min;
1574 }
1575
1581 {
1582 const Point & max = p1_.get_x() > p2_.get_x() ? p1_ : p2_;
1583 return p3_.get_x() > max.get_x() ? p3_ : max;
1584 }
1585
1587 [[nodiscard]] const Point &get_p1() const { return p1_; }
1589 [[nodiscard]] const Point &get_p2() const { return p2_; }
1591 [[nodiscard]] const Point &get_p3() const { return p3_; }
1592
1600 [[nodiscard]] bool operator==(const Triangle & t) const noexcept
1601 {
1602 const bool p1_in = p1_ == t.p1_ or p1_ == t.p2_ or p1_ == t.p3_;
1603 const bool p2_in = p2_ == t.p1_ or p2_ == t.p2_ or p2_ == t.p3_;
1604 const bool p3_in = p3_ == t.p1_ or p3_ == t.p2_ or p3_ == t.p3_;
1605 return p1_in and p2_in and p3_in;
1606 }
1607
1613 [[nodiscard]] bool operator!=(const Triangle & t) const noexcept
1614 {
1615 return not (*this == t);
1616 }
1617
1623 {
1624 return (p1_ + p2_ + p3_) / Geom_Number(3);
1625 }
1626
1632 {
1634 }
1635
1644 {
1645 const Geom_Number & x1 = p1_.get_x();
1646 const Geom_Number & y1 = p1_.get_y();
1647 const Geom_Number & x2 = p2_.get_x();
1648 const Geom_Number & y2 = p2_.get_y();
1649 const Geom_Number & x3 = p3_.get_x();
1650 const Geom_Number & y3 = p3_.get_y();
1651
1652 const Geom_Number d = Geom_Number(2) *
1653 (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
1654 ah_domain_error_if(d == 0) << "Cannot compute circumcenter of a degenerate triangle";
1655
1656 const Geom_Number x1sq_y1sq = x1 * x1 + y1 * y1;
1657 const Geom_Number x2sq_y2sq = x2 * x2 + y2 * y2;
1658 const Geom_Number x3sq_y3sq = x3 * x3 + y3 * y3;
1659
1660 const Geom_Number ux =
1661 (x1sq_y1sq * (y2 - y3) + x2sq_y2sq * (y3 - y1) +
1662 x3sq_y3sq * (y1 - y2)) / d;
1663
1664 const Geom_Number uy =
1665 (x1sq_y1sq * (x3 - x2) + x2sq_y2sq * (x1 - x3) +
1666 x3sq_y3sq * (x2 - x1)) / d;
1667
1668 return {ux, uy};
1669 }
1670
1679 {
1680 const Geom_Number a = p2_.distance_to(p3_);
1681 const Geom_Number b = p1_.distance_to(p3_);
1682 const Geom_Number c = p1_.distance_to(p2_);
1683 const Geom_Number sum = a + b + c;
1684 ah_domain_error_if(sum == 0) << "Cannot compute incenter of a degenerate triangle";
1685
1686 return (p1_ * a + p2_ * b + p3_ * c) / sum;
1687 }
1688
1693 [[nodiscard]] std::array<Segment, 3> edges() const
1694 {
1695 return {Segment(p1_, p2_), Segment(p2_, p3_), Segment(p3_, p1_)};
1696 }
1697
1706 [[nodiscard]] bool contains(const Point & p) const
1707 {
1708 if (is_clockwise())
1709 return p.is_to_right_from(p1_, p2_) and
1712 return p.is_left_of(p1_, p2_) and
1713 p.is_left_of(p2_, p3_) and
1714 p.is_left_of(p3_, p1_);
1715 }
1716
1724 {
1725 return s.intersection_with(*this);
1726 }
1727 };
1728
1737 {
1740
1741 public:
1747 [[nodiscard]] bool operator==(const Rectangle & r) const noexcept
1748 {
1749 return xmin_ == r.xmin_ and ymin_ == r.ymin_ and
1750 xmax_ == r.xmax_ and ymax_ == r.ymax_;
1751 }
1752
1758 [[nodiscard]] bool operator!=(const Rectangle & r) const noexcept
1759 {
1760 return not (*this == r);
1761 }
1762
1764 [[nodiscard]] const Geom_Number &get_xmin() const { return xmin_; }
1766 [[nodiscard]] const Geom_Number &get_ymin() const { return ymin_; }
1768 [[nodiscard]] const Geom_Number &get_xmax() const { return xmax_; }
1770 [[nodiscard]] const Geom_Number &get_ymax() const { return ymax_; }
1771
1775 Rectangle() : xmin_(0), ymin_(0), xmax_(0), ymax_(0)
1776 {
1777 // empty
1778 }
1779
1788 Rectangle(const Geom_Number & xmin, const Geom_Number & ymin,
1789 const Geom_Number & xmax, const Geom_Number & ymax)
1790 : xmin_(xmin), ymin_(ymin), xmax_(xmax), ymax_(ymax)
1791 {
1792 ah_range_error_if(xmax_ < xmin_ or ymax_ < ymin_) << "Invalid rectangle";
1793 }
1794
1803 void set_rect(const Geom_Number & xmin, const Geom_Number & ymin,
1804 const Geom_Number & xmax, const Geom_Number & ymax)
1805 {
1806 ah_range_error_if(xmax < xmin or ymax < ymin) << "Invalid rectangle";
1807
1808 xmin_ = xmin;
1809 ymin_ = ymin;
1810 xmax_ = xmax;
1811 ymax_ = ymax;
1812 }
1813
1820
1826 {
1827 return 2 * (width() + height());
1828 }
1829
1835 {
1836 return {(xmin_ + xmax_) / 2, (ymin_ + ymax_) / 2};
1837 }
1838
1844 [[nodiscard]] std::array<Point, 4> corners() const noexcept
1845 {
1846 return {
1847 Point(xmin_, ymin_),
1848 Point(xmax_, ymin_),
1849 Point(xmax_, ymax_),
1850 Point(xmin_, ymax_)
1851 };
1852 }
1853
1859 [[nodiscard]] bool intersects(const Rectangle & that) const noexcept
1860 {
1861 return this->xmax_ >= that.xmin_ and this->ymax_ >= that.ymin_ and
1862 that.xmax_ >= this->xmin_ and that.ymax_ >= this->ymin_;
1863 }
1864
1871 [[nodiscard]] Geom_Number distance_squared_to(const Point & p) const noexcept
1872 {
1873 Geom_Number dx = 0.0, dy = 0.0;
1874 if (p.get_x() < xmin_)
1875 dx = p.get_x() - xmin_;
1876 else if (p.get_x() > xmax_)
1877 dx = p.get_x() - xmax_;
1878
1879 if (p.get_y() < ymin_)
1880 dy = p.get_y() - ymin_;
1881 else if (p.get_y() > ymax_)
1882 dy = p.get_y() - ymax_;
1883
1884 return dx * dx + dy * dy;
1885 }
1886
1893 {
1895 }
1896
1902 [[nodiscard]] bool contains(const Point & p) const noexcept
1903 {
1904 return p.get_x() >= xmin_ and p.get_x() <= xmax_ and
1905 p.get_y() >= ymin_ and p.get_y() <= ymax_;
1906 }
1907
1912 [[nodiscard]] std::string to_string() const
1913 {
1914 return "(" + std::to_string(geom_number_to_double(xmin_)) + "," +
1915 std::to_string(geom_number_to_double(ymin_)) + ")-(" +
1916 std::to_string(geom_number_to_double(xmax_)) + "," +
1917 std::to_string(geom_number_to_double(ymax_)) + ")";
1918 }
1919 };
1920
1921 // Returns true if this segment intersects one or two edges of triangle @p t.
1922 inline bool Segment::intersects_with(const Triangle & t) const
1923 {
1924 return this->intersects_with(Segment(t.get_p1(), t.get_p2())) or
1925 this->intersects_with(Segment(t.get_p2(), t.get_p3())) or
1926 this->intersects_with(Segment(t.get_p3(), t.get_p1()));
1927 }
1928
1929
1930 // Returns the segment resulting from intersecting this segment with triangle @p t.
1931 // If an endpoint lies inside the triangle, the intersection degenerates to a
1932 // point, which can be used to test if a point belongs to @p t.
1934 {
1936 << "segment does not intersects with triangle";
1937
1938 std::array<Point, 6> points;
1939 size_t count = 0;
1940
1941 auto append_unique = [&points, &count](const Point & pt)
1942 {
1943 for (size_t i = 0; i < count; ++i)
1944 if (points[i] == pt)
1945 return;
1946 points[count++] = pt;
1947 };
1948
1949 auto collect_with_edge = [this, &append_unique](const Segment & edge)
1950 {
1951 if (not this->intersects_with(edge))
1952 return;
1953
1954 if (this->is_parallel_with(edge))
1955 {
1956 if (this->contains(edge.get_src_point()))
1957 append_unique(edge.get_src_point());
1958 if (this->contains(edge.get_tgt_point()))
1959 append_unique(edge.get_tgt_point());
1960 if (edge.contains(this->get_src_point()))
1962 if (edge.contains(this->get_tgt_point()))
1964 return;
1965 }
1966
1967 append_unique(this->intersection_with(edge));
1968 };
1969
1973
1975 << "no intersection point found despite intersection confirmed";
1976
1977 if (count == 1)
1978 return {points[0], points[0]};
1979
1980 // Keep the farthest pair in case collinear overlaps produced >2 points.
1981 size_t i_best = 0;
1982 size_t j_best = 1;
1983 Geom_Number best_d2 = points[0].distance_squared_to(points[1]);
1984 for (size_t i = 0; i < count; ++i)
1985 for (size_t j = i + 1; j < count; ++j)
1986 if (const Geom_Number d2 = points[i].distance_squared_to(points[j]); d2 > best_d2)
1987 {
1988 best_d2 = d2;
1989 i_best = i;
1990 j_best = j;
1991 }
1992
1993 return {points[i_best], points[j_best]};
1994 }
1995
1996
2005 class Ellipse : public Geom_Object
2006 {
2007 friend class Point;
2008
2009 /*
2010 The ellipse is defined relative to its center (xc, yc) by:
2011
2012 2 2
2013 (y - yc) (x - xc)
2014 --------- + --------- = 1
2015 2 2
2016 vr hr
2017 */
2018
2019 Point center_; // ellipse center
2020
2021 Geom_Number hr_; // horizontal radius (parameter a)
2022 Geom_Number vr_; // vertical radius (parameter b)
2023
2024 public:
2033 const Geom_Number & hr,
2034 const Geom_Number & vr)
2035 : center_(std::move(center)), hr_(hr), vr_(vr)
2036 {
2038 }
2039
2043 Ellipse(const Ellipse & e) = default;
2044
2045
2050 Ellipse() : center_(0, 0), hr_(1), vr_(1)
2051 {
2052 // empty
2053 }
2054
2060 [[nodiscard]] bool operator==(const Ellipse & e) const noexcept
2061 {
2062 return center_ == e.center_ and hr_ == e.hr_ and vr_ == e.vr_;
2063 }
2064
2070 [[nodiscard]] bool operator!=(const Ellipse & e) const noexcept
2071 {
2072 return not (*this == e);
2073 }
2074
2076 [[nodiscard]] const Point &get_center() const { return center_; }
2078 [[nodiscard]] const Geom_Number &get_hradius() const { return hr_; }
2080 [[nodiscard]] const Geom_Number &get_vradius() const { return vr_; }
2081
2087 {
2089 return geom_pi() * hr_ * vr_;
2090 }
2091
2098 {
2100 const Geom_Number ab_sum = hr_ + vr_;
2101 const Geom_Number h = (hr_ - vr_) * (hr_ - vr_) / (ab_sum * ab_sum);
2102 const Geom_Number correction =
2103 Geom_Number(1) + Geom_Number(3) * h /
2104 (Geom_Number(10) + square_root(Geom_Number(4) - Geom_Number(3) * h));
2105 return geom_pi() * ab_sum * correction;
2106 }
2107
2114 {
2116 return {
2117 center_.get_x() + hr_ * cosinus(angle),
2118 center_.get_y() + vr_ * sinus(angle)
2119 };
2120 }
2121
2126 [[nodiscard]] std::string to_string() const
2127 {
2128 return "Ellipse(center=" + center_.to_string() +
2129 ", hr=" + std::to_string(geom_number_to_double(hr_)) +
2130 ", vr=" + std::to_string(geom_number_to_double(vr_)) + ")";
2131 }
2132
2133 private:
2139 {
2140 ah_domain_error_if(hr_ <= 0 or vr_ <= 0) << "Ellipse radii must be > 0";
2141 }
2142
2146 [[nodiscard]] static bool in_unit_interval(const Geom_Number & t)
2147 {
2148 return t >= 0 and t <= 1;
2149 }
2150
2151 public:
2156 static bool is_clockwise() { return false; }
2157
2160 {
2161 return {center_.get_x(), center_.get_y() + vr_};
2162 }
2163
2166 {
2167 return {center_.get_x(), center_.get_y() - vr_};
2168 }
2169
2172 {
2173 return {center_.get_x() - hr_, center_.get_y()};
2174 }
2175
2178 {
2179 return {center_.get_x() + hr_, center_.get_y()};
2180 }
2181
2188 void
2190 {
2192
2193 const Geom_Number m2 = m * m;
2194 const Geom_Number hr2 = hr_ * hr_;
2195 const Geom_Number vr2 = vr_ * vr_;
2196 const Geom_Number y_intercept_sq = hr2 * m2 + vr2;
2197 ah_domain_error_if(y_intercept_sq < 0) << "No tangent for this slope";
2198
2199 const Geom_Number y_intercept = square_root(y_intercept_sq);
2200
2201 // Tangent points on origin-centered ellipse
2202 const Point t1_local(-hr2*m/y_intercept, vr2/y_intercept);
2203 const Point t2_local( hr2*m/y_intercept, -vr2/y_intercept);
2204
2205 // Translate to ellipse's actual center
2208
2209 // Create segments of arbitrary length through the tangent points
2210 const Geom_Number tangent_len = hr_ > vr_ ? hr_ : vr_;
2211 s1 = Segment(tangent_point1 - (Point(1, m).normalize() * tangent_len),
2212 tangent_point1 + (Point(1, m).normalize() * tangent_len));
2213 s2 = Segment(tangent_point2 - (Point(1, m).normalize() * tangent_len),
2214 tangent_point2 + (Point(1, m).normalize() * tangent_len));
2215 }
2216
2224 [[nodiscard]] bool intersects_with(const Segment & s) const
2225 {
2227
2228 const Point p0 = s.get_src_point() - center_;
2229 const Point p1 = s.get_tgt_point() - center_;
2230 const Geom_Number dx = p1.get_x() - p0.get_x();
2231 const Geom_Number dy = p1.get_y() - p0.get_y();
2232 const Geom_Number a2 = hr_ * hr_;
2233 const Geom_Number b2 = vr_ * vr_;
2234
2235 const Geom_Number A = (dx * dx) / a2 + (dy * dy) / b2;
2236 const Geom_Number B =
2237 Geom_Number(2) * (p0.get_x() * dx / a2 + p0.get_y() * dy / b2);
2238 const Geom_Number C =
2239 (p0.get_x() * p0.get_x()) / a2 + (p0.get_y() * p0.get_y()) / b2 -
2240 Geom_Number(1);
2241
2242 // For a segment, A can be 0 if it's a point.
2243 if (A == 0)
2244 return C <= 0; // Point is inside or on the ellipse
2245
2246 const Geom_Number discriminant = B * B - Geom_Number(4) * A * C;
2247 if (discriminant < 0)
2248 return false; // No real roots, no intersection
2249
2250 // Check if intersection points lie on the segment (t in [0,1])
2252 const Geom_Number denom = Geom_Number(2) * A;
2253 const Geom_Number t1 = (-B - root) / denom;
2254 const Geom_Number t2 = (-B + root) / denom;
2256 }
2257
2258 private:
2267 {
2269
2270 Geom_Number x2 = (p.get_x() - center_.get_x());
2271 x2 = x2 * x2;
2272
2273 Geom_Number y2 = (p.get_y() - center_.get_y());
2274 y2 = y2 * y2;
2275
2276 return x2 / (hr_ * hr_) + y2 / (vr_ * vr_);
2277 }
2278
2279 public:
2285 [[nodiscard]] bool contains(const Point & p) const
2286 {
2287 return compute_radius(p) <= 1;
2288 }
2289
2291 [[deprecated("Use contains() instead")]]
2292 [[nodiscard]] bool contains_to(const Point & p) const { return contains(p); }
2293
2299 [[nodiscard]] bool intersects_with(const Point & p) const
2300 {
2301 return compute_radius(p) == 1;
2302 }
2303
2312 {
2314
2315 const Point p0 = sg.get_src_point() - center_;
2316 const Point p1 = sg.get_tgt_point() - center_;
2317 const Geom_Number dx = p1.get_x() - p0.get_x();
2318 const Geom_Number dy = p1.get_y() - p0.get_y();
2319 const Geom_Number a2 = hr_ * hr_;
2320 const Geom_Number b2 = vr_ * vr_;
2321
2322 const Geom_Number A = dx * dx / a2 + dy * dy / b2;
2323 const Geom_Number B =
2324 Geom_Number(2) * (p0.get_x() * dx / a2 + p0.get_y() * dy / b2);
2325 const Geom_Number C =
2326 p0.get_x() * p0.get_x() / a2 + p0.get_y() * p0.get_y() / b2 - Geom_Number(1);
2327
2328 if (A == 0)
2329 {
2330 ah_domain_error_if(C > 0) << "No intersection (point outside)";
2331 return {sg.get_src_point(), sg.get_src_point()};
2332 }
2333
2334 const Geom_Number discriminant = B * B - Geom_Number(4) * A * C;
2335 ah_domain_error_if(discriminant < 0) << "No intersection (no real roots)";
2336
2338 const Geom_Number denom = Geom_Number(2) * A;
2339 const Geom_Number t1 = (-B - root) / denom;
2340 const Geom_Number t2 = (-B + root) / denom;
2341
2342 const bool in1 = in_unit_interval(t1);
2343 const bool in2 = in_unit_interval(t2);
2344 ah_domain_error_if(not in1 and not in2) << "Intersection points are outside the segment";
2345
2346 auto eval_at = [&](const Geom_Number & t)
2347 {
2348 return sg.at(t);
2349 };
2350
2351 if (in1 and in2)
2352 return {eval_at(t1), eval_at(t2)};
2353
2354 const Point touch = in1 ? eval_at(t1) : eval_at(t2);
2355 return {touch, touch};
2356 }
2357 };
2358
2359 // Return true if this point lies inside ellipse e.
2360 inline bool Point::is_inside(const Ellipse & e) const
2361 {
2362 return e.contains(*this);
2363 }
2364
2365 // Return true if this point lies exactly on ellipse e.
2366 inline bool Point::intersects_with(const Ellipse & e) const
2367 {
2368 return e.intersects_with(*this);
2369 }
2370
2371 // Return true if this segment intersects ellipse e.
2372 inline bool Segment::intersects_with(const Ellipse & e) const
2373 {
2374 return e.intersects_with(*this);
2375 }
2376
2377 // Return the segment defined by the two intersection points between this
2378 // segment and ellipse e.
2380 {
2381 return e.intersection_with(*this);
2382 }
2383
2384 // ============================================================================
2385 // Rotated Ellipse
2386 // ============================================================================
2387
2404 {
2406 Geom_Number a_; // semi-axis along the local x (before rotation)
2407 Geom_Number b_; // semi-axis along the local y (before rotation)
2408 Geom_Number cos_th_; // cosine of the rotation angle
2409 Geom_Number sin_th_; // sine of the rotation angle
2410
2416 [[nodiscard]] Point to_local(const Point & p) const
2417 {
2418 const Geom_Number dx = p.get_x() - center_.get_x();
2419 const Geom_Number dy = p.get_y() - center_.get_y();
2420 return {
2421 cos_th_ * dx + sin_th_ * dy,
2422 -sin_th_ * dx + cos_th_ * dy
2423 };
2424 }
2425
2431 [[nodiscard]] Point to_world(const Point & p) const
2432 {
2433 return {
2434 cos_th_ * p.get_x() - sin_th_ * p.get_y() + center_.get_x(),
2435 sin_th_ * p.get_x() + cos_th_ * p.get_y() + center_.get_y()
2436 };
2437 }
2438
2444 {
2445 ah_domain_error_if(a_ <= 0 or b_ <= 0) << "RotatedEllipse radii must be > 0";
2446 }
2447
2453 {
2454 const Geom_Number norm2 = cos_th_ * cos_th_ + sin_th_ * sin_th_;
2455 ah_domain_error_if(norm2 == 0) << "Rotation vector (cos,sin) cannot be (0,0)";
2456 if (norm2 != 1)
2457 {
2458 const Geom_Number norm = square_root(norm2);
2459 cos_th_ /= norm;
2460 sin_th_ /= norm;
2461 }
2462 }
2463
2464 public:
2474 const Geom_Number & a, const Geom_Number & b,
2475 const Geom_Number & cos_theta,
2476 const Geom_Number & sin_theta)
2477 : center_(std::move(center)), a_(a), b_(b),
2479 {
2482 }
2483
2491 const Geom_Number & a, const Geom_Number & b)
2492 : center_(std::move(center)), a_(a), b_(b), cos_th_(1), sin_th_(0)
2493 {
2495 }
2496
2502 : center_(0, 0), a_(1), b_(1), cos_th_(1), sin_th_(0)
2503 {
2504 // empty
2505 }
2506
2507 RotatedEllipse(const RotatedEllipse &) = default;
2508
2510
2516 [[nodiscard]] bool operator==(const RotatedEllipse & e) const noexcept
2517 {
2518 return center_ == e.center_ and a_ == e.a_ and b_ == e.b_ and
2519 cos_th_ == e.cos_th_ and sin_th_ == e.sin_th_;
2520 }
2521
2527 [[nodiscard]] bool operator!=(const RotatedEllipse & e) const noexcept
2528 {
2529 return not (*this == e);
2530 }
2531
2533 [[nodiscard]] const Point &get_center() const { return center_; }
2535 [[nodiscard]] const Geom_Number &get_a() const { return a_; }
2537 [[nodiscard]] const Geom_Number &get_b() const { return b_; }
2539 [[nodiscard]] const Geom_Number &get_cos() const { return cos_th_; }
2541 [[nodiscard]] const Geom_Number &get_sin() const { return sin_th_; }
2542
2548 {
2550 return geom_pi() * a_ * b_;
2551 }
2552
2560 {
2562
2563 const Point lp = to_local(p);
2564 return (lp.get_x() * lp.get_x()) / (a_ * a_) +
2565 (lp.get_y() * lp.get_y()) / (b_ * b_);
2566 }
2567
2573 [[nodiscard]] bool contains(const Point & p) const
2574 {
2575 return radius_value(p) <= 1;
2576 }
2577
2583 [[nodiscard]] bool strictly_contains(const Point & p) const
2584 {
2585 return radius_value(p) < 1;
2586 }
2587
2593 [[nodiscard]] bool on_boundary(const Point & p) const
2594 {
2595 return radius_value(p) == 1;
2596 }
2597
2605 const Geom_Number & sin_t) const
2606 {
2608 // Point on axis-aligned ellipse: (a cos t, b sin t).
2609 // Rotate and translate to the world.
2610 return to_world(Point(a_ * cos_t, b_ * sin_t));
2611 }
2612
2618 [[nodiscard]] Point sample(const Geom_Number & t) const
2619 {
2620 const Geom_Number angle = Geom_Number(2) * geom_pi() * t;
2621 return sample(cosinus(angle), sinus(angle));
2622 }
2623
2629 [[nodiscard]] bool intersects_with(const Segment & s) const
2630 {
2633 return Ellipse(Point(0, 0), a_, b_).intersects_with(local_s);
2634 }
2635
2643 {
2647 return {to_world(local_i.get_src_point()), to_world(local_i.get_tgt_point())};
2648 }
2649
2650 [[nodiscard]] std::string to_string() const
2651 {
2652 return "RotatedEllipse(center=" + center_.to_string() +
2653 ", a=" + std::to_string(geom_number_to_double(a_)) +
2654 ", b=" + std::to_string(geom_number_to_double(b_)) +
2655 ", cos=" + std::to_string(geom_number_to_double(cos_th_)) +
2656 ", sin=" + std::to_string(geom_number_to_double(sin_th_)) + ")";
2657 }
2658
2661 {
2663 };
2664
2672 {
2673 // TODO: This is incorrect. It returns the endpoints of the semi-axes,
2674 // which are only the extremal points if the ellipse is axis-aligned.
2675 // The correct calculation requires finding the points where the derivative
2676 // of the world coordinate is zero.
2677 return {
2682 };
2683 }
2684 };
2685
2686 /*****************************************************************
2687
2688 Fundamental text primitive
2689
2690 Utility to draw text strings on the plane. Offsets are not stored because
2691 callers can shift the reference point directly.
2692 */
2693
2700 inline size_t approximate_string_size(const std::string & str)
2701 {
2702 const char *ptr = str.c_str();
2703
2704 size_t _len = 0;
2705 for (int i = 0; true; /* empty */)
2706 {
2707 switch (ptr[i])
2708 {
2709 case '\\':
2710 // Skip all characters that compose the LaTeX command.
2711 for (++i; isalnum(ptr[i]) and ptr[i] != '\0'; /* nothing */)
2712 ++i;
2713 ++_len;
2714 break;
2715
2716 case '$':
2717 case '{':
2718 case '}':
2719 case '\n':
2720 ++i;
2721 break;
2722
2723 case '\0':
2724 return _len;
2725
2726 default:
2727 ++_len;
2728 ++i;
2729 break;
2730 }
2731 }
2732 }
2733
2738 class Text : public Geom_Object
2739 {
2741
2742 std::string str_;
2743
2744 size_t len_ = 0;
2745
2746 public:
2747 static constexpr double font_width_in_points = 0.8;
2748
2749 static constexpr double font_height_in_points = 1.2;
2750
2756 Text(Point p, const std::string & str)
2757 : p_(std::move(p)), str_(str), len_(approximate_string_size(str))
2758 {
2759 // empty
2760 }
2761
2763 Text() = default;
2764
2766 [[nodiscard]] const size_t &len() const { return len_; }
2767
2769 [[nodiscard]] const Point &get_point() const
2770 {
2771 return p_;
2772 }
2773
2775 [[nodiscard]] const std::string &get_str() const { return str_; }
2776
2779 {
2780 return p_;
2781 }
2782
2785 {
2786 return p_;
2787 }
2788
2791 {
2792 return p_;
2793 }
2794
2797 {
2798 return p_;
2799 }
2800 };
2801
2802 inline Geom_Number
2803 area_of_parallelogram(const Point & a, const Point & b, const Point & c)
2804 {
2805 return ((b.get_x() - a.get_x()) * (c.get_y() - a.get_y()) -
2806 (c.get_x() - a.get_x()) * (b.get_y() - a.get_y()));
2807 }
2808
2809
2811 enum class Orientation
2812 {
2813 CCW, // counter-clockwise
2814 CW, // clockwise
2815 COLLINEAR
2816 };
2817
2820 [[nodiscard]] inline Orientation
2821 orientation(const Point & a, const Point & b, const Point & c)
2822 {
2823 const Geom_Number area = area_of_parallelogram(a, b, c);
2824 if (area > 0) return Orientation::CCW;
2825 if (area < 0) return Orientation::CW;
2827 }
2828
2831
2834 [[nodiscard]] inline Geom_Number
2835 in_circle_determinant(const Point & a, const Point & b,
2836 const Point & c, const Point & p)
2837 {
2838 const Geom_Number adx = a.get_x() - p.get_x();
2839 const Geom_Number ady = a.get_y() - p.get_y();
2840 const Geom_Number bdx = b.get_x() - p.get_x();
2841 const Geom_Number bdy = b.get_y() - p.get_y();
2842 const Geom_Number cdx = c.get_x() - p.get_x();
2843 const Geom_Number cdy = c.get_y() - p.get_y();
2844
2845 const Geom_Number ad2 = adx * adx + ady * ady;
2846 const Geom_Number bd2 = bdx * bdx + bdy * bdy;
2847 const Geom_Number cd2 = cdx * cdx + cdy * cdy;
2848
2849 return ad2 * (bdx * cdy - bdy * cdx) -
2850 bd2 * (adx * cdy - ady * cdx) +
2851 cd2 * (adx * bdy - ady * bdx);
2852 }
2853
2856 [[nodiscard]] inline InCircleResult
2857 in_circle(const Point & a, const Point & b,
2858 const Point & c, const Point & p)
2859 {
2860 const Orientation o = orientation(a, b, c);
2863
2864 const Geom_Number det = in_circle_determinant(a, b, c, p);
2865 if (det == 0)
2867
2868 if (o == Orientation::CCW)
2870
2872 }
2873
2875 [[nodiscard]] inline bool
2876 on_segment(const Segment & s, const Point & p)
2877 {
2878 return s.contains(p);
2879 }
2880
2882 [[nodiscard]] inline bool
2884 {
2885 return s1.intersects_with(s2);
2886 }
2887
2889 [[nodiscard]] inline bool
2890 segments_intersect(const Point & a, const Point & b,
2891 const Point & c, const Point & d)
2892 {
2893 return Segment(a, b).intersects_with(Segment(c, d));
2894 }
2895
2899 [[nodiscard]] inline Point
2901 {
2902 ah_domain_error_if(not segments_intersect(s1, s2)) << "Segments do not intersect";
2903
2904 const Point & a = s1.get_src_point();
2905 const Point & b = s1.get_tgt_point();
2906 const Point & c = s2.get_src_point();
2907 const Point & d = s2.get_tgt_point();
2908
2909 // Degenerate-point cases may still have a unique intersection point.
2910 if (a == b and c == d)
2911 {
2912 ah_domain_error_if(a != c) << "Segments do not intersect";
2913 return a;
2914 }
2915
2916 if (a == b)
2917 return a;
2918
2919 if (c == d)
2920 return c;
2921
2922 if (not s1.is_parallel_with(s2))
2923 return s1.intersection_with(s2);
2924
2925 // Collinear/parallel intersections can still be unique (touching endpoint).
2927 bool has_unique_point = false;
2929 {
2931 {
2932 unique_point = p;
2933 has_unique_point = true;
2934 return;
2935 }
2936
2937 ah_domain_error_if(unique_point != p) << "No unique intersection point";
2938 };
2939
2940 if (on_segment(s2, a))
2942 if (on_segment(s2, b))
2944 if (on_segment(s1, c))
2946 if (on_segment(s1, d))
2948
2949 ah_domain_error_if(not has_unique_point) << "No unique intersection point";
2950
2951 return unique_point;
2952 }
2953
2955 [[nodiscard]] inline Geom_Number
2956 area_of_triangle(const Point & a, const Point & b, const Point & c)
2957 {
2958 return abs(area_of_parallelogram(a, b, c)) / 2;
2959 }
2960
2961 // ============================================================================
2962 // 3D Primitives
2963 // ============================================================================
2964
2973 {
2975
2976 public:
2980 Point3D() : x_(0), y_(0), z_(0) {}
2981
2988 Point3D(const Geom_Number & x, const Geom_Number & y, const Geom_Number & z)
2989 : x_(x), y_(y), z_(z) {}
2990
2991 Point3D(const Point3D &) = default;
2992
2993 Point3D &operator=(const Point3D &) = default;
2994
2996 [[nodiscard]] const Geom_Number &get_x() const { return x_; }
2998 [[nodiscard]] const Geom_Number &get_y() const { return y_; }
3000 [[nodiscard]] const Geom_Number &get_z() const { return z_; }
3001
3007 [[nodiscard]] bool operator==(const Point3D & p) const
3008 {
3009 return x_ == p.x_ and y_ == p.y_ and z_ == p.z_;
3010 }
3011
3017 [[nodiscard]] bool operator!=(const Point3D & p) const
3018 {
3019 return ! (*this == p);
3020 }
3021
3027 [[nodiscard]] Point3D operator+(const Point3D & p) const
3028 {
3029 return {x_ + p.x_, y_ + p.y_, z_ + p.z_};
3030 }
3031
3037 [[nodiscard]] Point3D operator-(const Point3D & p) const
3038 {
3039 return {x_ - p.x_, y_ - p.y_, z_ - p.z_};
3040 }
3041
3048 {
3049 x_ += p.x_;
3050 y_ += p.y_;
3051 z_ += p.z_;
3052 return *this;
3053 }
3054
3061 {
3062 x_ -= p.x_;
3063 y_ -= p.y_;
3064 z_ -= p.z_;
3065 return *this;
3066 }
3067
3073 {
3074 return {-x_, -y_, -z_};
3075 }
3076
3083 {
3084 return {x_ * s, y_ * s, z_ * s};
3085 }
3086
3093 {
3094 return {x_ / s, y_ / s, z_ / s};
3095 }
3096
3102 [[nodiscard]] Geom_Number dot(const Point3D & p) const
3103 {
3104 return x_ * p.x_ + y_ * p.y_ + z_ * p.z_;
3105 }
3106
3112 [[nodiscard]] Point3D cross(const Point3D & p) const
3113 {
3114 return {
3115 y_ * p.z_ - z_ * p.y_,
3116 z_ * p.x_ - x_ * p.z_,
3117 x_ * p.y_ - y_ * p.x_
3118 };
3119 }
3120
3127 {
3128 const Geom_Number dx = x_ - p.x_;
3129 const Geom_Number dy = y_ - p.y_;
3130 const Geom_Number dz = z_ - p.z_;
3131 return dx * dx + dy * dy + dz * dz;
3132 }
3133
3139 {
3140 return x_ * x_ + y_ * y_ + z_ * z_;
3141 }
3142
3148 {
3149 return square_root(norm_squared());
3150 }
3151
3158 {
3160 }
3161
3168 {
3169 const Geom_Number n = norm();
3170 ah_domain_error_if(n == 0) << "Cannot normalize zero Point3D";
3171 return *this / n;
3172 }
3173
3178 [[nodiscard]] Point to_2d() const { return {x_, y_}; }
3179
3185 [[nodiscard]] static Point3D from_2d(const Point & p)
3186 {
3187 return {p.get_x(), p.get_y(), Geom_Number(0)};
3188 }
3189
3196 [[nodiscard]] static Point3D from_2d(const Point & p, const Geom_Number & z)
3197 {
3198 return {p.get_x(), p.get_y(), z};
3199 }
3200 };
3201
3203 [[nodiscard]] inline Geom_Number
3204 scalar_triple_product(const Point3D & a, const Point3D & b, const Point3D & c)
3205 {
3206 return a.dot(b.cross(c));
3207 }
3208
3214 {
3216
3217 public:
3219 Segment3D() = default;
3220
3226 Segment3D(const Point3D & src, const Point3D & tgt) : src_(src), tgt_(tgt) {}
3227
3229 [[nodiscard]] const Point3D &get_src() const noexcept { return src_; }
3231 [[nodiscard]] const Point3D &get_tgt() const noexcept { return tgt_; }
3236
3241 [[nodiscard]] Point3D direction() const { return tgt_ - src_; }
3242
3248 {
3250 }
3251
3257 {
3258 return src_.distance_to(tgt_);
3259 }
3260
3266 [[nodiscard]] Point3D at(const Geom_Number & t) const
3267 {
3268 const Geom_Number s = Geom_Number(1) - t;
3269 return src_ * s + tgt_ * t;
3270 }
3271
3277 {
3278 return at(Geom_Number(1, 2));
3279 }
3280
3288 [[nodiscard]] bool operator==(const Segment3D & s) const
3289 {
3290 return (src_ == s.src_ and tgt_ == s.tgt_) or
3291 (src_ == s.tgt_ and tgt_ == s.src_);
3292 }
3293
3299 [[nodiscard]] bool operator!=(const Segment3D & s) const
3300 {
3301 return not (*this == s);
3302 }
3303
3309 [[nodiscard]] bool contains(const Point3D & p) const
3310 {
3311 const Point3D d = direction();
3312 const Point3D w = p - src_;
3313
3314 if (d == Point3D(0, 0, 0))
3315 return p == src_; // Segment is a point
3316
3317 // Check for collinearity using cross product
3318 if (d.cross(w) != Point3D(0, 0, 0))
3319 return false;
3320
3321 // Check if point is within the segment bounds using dot product
3322 const Geom_Number dot = w.dot(d);
3323 return dot >= 0 && dot <= d.dot(d);
3324 }
3325
3332 {
3333 const Point3D d = direction();
3334 const Geom_Number len2 = d.dot(d);
3335
3336 if (len2 == 0)
3337 return p.distance_to(src_); // Segment is a point
3338
3339 // Project p onto the line defined by the segment
3340 // t = ((p - src) . d) / |d|^2
3341 Geom_Number t = (p - src_).dot(d) / len2;
3342
3343 // Clamp t to the [0, 1] interval to stay on the segment
3344 if (t < 0)
3345 t = 0;
3346 else if (t > 1)
3347 t = 1;
3348
3349 const Point3D proj = at(t);
3350 return p.distance_to(proj);
3351 }
3352 };
3353
3359 {
3361
3362 public:
3364 Triangle3D() = default;
3365
3372 Triangle3D(const Point3D & p1, const Point3D & p2, const Point3D & p3)
3373 : p1_(p1), p2_(p2), p3_(p3) {}
3374
3376 [[nodiscard]] const Point3D &get_p1() const { return p1_; }
3378 [[nodiscard]] const Point3D &get_p2() const { return p2_; }
3380 [[nodiscard]] const Point3D &get_p3() const { return p3_; }
3381
3390 {
3391 return (p2_ - p1_).cross(p3_ - p1_);
3392 }
3393
3399 {
3400 return normal().norm_squared() / Geom_Number(2);
3401 }
3402
3408 {
3409 return (p1_ + p2_ + p3_) / Geom_Number(3);
3410 }
3411
3416 [[nodiscard]] bool is_degenerate() const
3417 {
3418 return normal().norm_squared() == 0;
3419 }
3420
3428 {
3430 };
3431
3439 {
3441 << "Barycentric coordinates undefined for degenerate triangle";
3442
3443 const Point3D v0 = p2_ - p1_, v1 = p3_ - p1_, v2 = p - p1_;
3444 const Geom_Number d00 = v0.dot(v0);
3445 const Geom_Number d01 = v0.dot(v1);
3446 const Geom_Number d11 = v1.dot(v1);
3447 const Geom_Number d20 = v2.dot(v0);
3448 const Geom_Number d21 = v2.dot(v1);
3449 const Geom_Number denom = d00 * d11 - d01 * d01;
3451 << "Barycentric coordinates undefined for degenerate triangle";
3452 const Geom_Number v = (d11 * d20 - d01 * d21) / denom;
3453 const Geom_Number w = (d00 * d21 - d01 * d20) / denom;
3454 return {Geom_Number(1) - v - w, v, w};
3455 }
3456 };
3457
3463 {
3465
3466 public:
3468 Tetrahedron() = default;
3469
3477 Tetrahedron(const Point3D & p1, const Point3D & p2,
3478 const Point3D & p3, const Point3D & p4)
3479 : p1_(p1), p2_(p2), p3_(p3), p4_(p4) {}
3480
3482 [[nodiscard]] const Point3D &get_p1() const { return p1_; }
3484 [[nodiscard]] const Point3D &get_p2() const { return p2_; }
3486 [[nodiscard]] const Point3D &get_p3() const { return p3_; }
3488 [[nodiscard]] const Point3D &get_p4() const { return p4_; }
3489
3498 {
3499 return scalar_triple_product(p2_ - p1_, p3_ - p1_, p4_ - p1_);
3500 }
3501
3507 {
3509 if (v < 0) v = -v;
3510 return v / Geom_Number(6);
3511 }
3512
3517 [[nodiscard]] bool is_degenerate() const
3518 {
3519 return signed_volume_x6() == 0;
3520 }
3521
3527 {
3528 return (p1_ + p2_ + p3_ + p4_) / Geom_Number(4);
3529 }
3530
3538 [[nodiscard]] bool contains(const Point3D & p) const
3539 {
3540 auto signed_volume_x6_of = [](const Point3D & a, const Point3D & b,
3541 const Point3D & c, const Point3D & d)
3542 {
3543 return scalar_triple_product(b - a, c - a, d - a);
3544 };
3545
3547 if (d0 == 0) return false; // Not contained in a degenerate tetrahedron
3548
3549 // Keep vertex order consistent with d0 by replacing one vertex at a time.
3550 const Geom_Number d1 = signed_volume_x6_of(p, p2_, p3_, p4_);
3551 const Geom_Number d2 = signed_volume_x6_of(p1_, p, p3_, p4_);
3552 const Geom_Number d3 = signed_volume_x6_of(p1_, p2_, p, p4_);
3553 const Geom_Number d4 = signed_volume_x6_of(p1_, p2_, p3_, p);
3554
3555 // Point is inside iff all sub-volumes have the same sign as d0.
3556 if (d0 > 0)
3557 return d1 >= 0 and d2 >= 0 and d3 >= 0 and d4 >= 0;
3558 return d1 <= 0 and d2 <= 0 and d3 <= 0 and d4 <= 0;
3559 }
3560
3564 struct Faces
3565 {
3567 };
3568
3574 {
3575 return {
3576 {
3577 Triangle3D(p1_, p2_, p3_),
3578 Triangle3D(p1_, p2_, p4_),
3579 Triangle3D(p1_, p3_, p4_),
3581 }
3582 };
3583 }
3584 };
3585
3586 // ============================================================================
3587 // Stream output operators for geometry classes
3588 // ============================================================================
3589
3590 inline std::ostream &operator<<(std::ostream & o, const Point & p)
3591 {
3592 o << "Point(" << p.get_x() << ", " << p.get_y() << ")";
3593 return o;
3594 }
3595
3596 inline std::ostream &operator<<(std::ostream & o, const Segment & s)
3597 {
3598 o << "Segment(" << s.get_src_point() << " -> " << s.get_tgt_point() << ")";
3599 return o;
3600 }
3601
3602 inline std::ostream &operator<<(std::ostream & o, const Triangle & t)
3603 {
3604 o << "Triangle(" << t.get_p1() << ", " << t.get_p2()
3605 << ", " << t.get_p3() << ")";
3606 return o;
3607 }
3608
3609 inline std::ostream &operator<<(std::ostream & o, const Rectangle & r)
3610 {
3611 o << "Rectangle" << r.to_string();
3612 return o;
3613 }
3614
3615 inline std::ostream &operator<<(std::ostream & o, const Ellipse & e)
3616 {
3617 o << e.to_string();
3618 return o;
3619 }
3620
3621 inline std::ostream &operator<<(std::ostream & o, const RotatedEllipse & e)
3622 {
3623 o << e.to_string();
3624 return o;
3625 }
3626
3627 inline std::ostream &operator<<(std::ostream & o, const Point3D & p)
3628 {
3629 o << "Point3D(" << p.get_x() << ", " << p.get_y()
3630 << ", " << p.get_z() << ")";
3631 return o;
3632 }
3633
3634 inline std::ostream &operator<<(std::ostream & o, const Segment3D & s)
3635 {
3636 o << "Segment3D(" << s.get_src() << " -> " << s.get_tgt() << ")";
3637 return o;
3638 }
3639
3640 inline std::ostream &operator<<(std::ostream & o, const Triangle3D & t)
3641 {
3642 o << "Triangle3D(" << t.get_p1() << ", " << t.get_p2()
3643 << ", " << t.get_p3() << ")";
3644 return o;
3645 }
3646
3647 inline std::ostream &operator<<(std::ostream & o, const Tetrahedron & t)
3648 {
3649 o << "Tetrahedron(" << t.get_p1() << ", " << t.get_p2()
3650 << ", " << t.get_p3() << ", " << t.get_p4() << ")";
3651 return o;
3652 }
3653} // namespace Aleph
3654
3655namespace std
3656{
3661 template <>
3662 struct hash<Aleph::Point>
3663 {
3664 std::size_t operator()(const Aleph::Point & p) const
3665 {
3666 const std::size_t hx = std::hash<std::string>{}(p.get_x().get_str());
3667 const std::size_t hy = std::hash<std::string>{}(p.get_y().get_str());
3668 return hx ^ (hy + 0x9e3779b97f4a7c15ULL + (hx << 6) + (hx >> 2));
3669 }
3670 };
3671} // namespace std
3672
3673// ============================================================================
3674// std::format support (C++20)
3675// ============================================================================
3676
3677#if __cplusplus >= 202002L && __has_include(<format>)
3678# include <format>
3679
3680# if defined(__cpp_lib_format)
3681
3683template <>
3684struct std::formatter<Aleph::Point> : std::formatter<std::string>
3685{
3686 auto format(const Aleph::Point & p, std::format_context & ctx) const
3687 {
3688 return std::formatter<std::string>::format(
3689 std::format("Point({}, {})",
3692 }
3693};
3694
3696template <>
3697struct std::formatter<Aleph::Segment> : std::formatter<std::string>
3698{
3699 auto format(const Aleph::Segment & s, std::format_context & ctx) const
3700 {
3701 return std::formatter<std::string>::format(
3702 std::format("Segment({} -> {})",
3703 s.get_src_point(), s.get_tgt_point()), ctx);
3704 }
3705};
3706
3708template <>
3709struct std::formatter<Aleph::Triangle> : std::formatter<std::string>
3710{
3711 auto format(const Aleph::Triangle & t, std::format_context & ctx) const
3712 {
3713 return std::formatter<std::string>::format(
3714 std::format("Triangle({}, {}, {})",
3715 t.get_p1(), t.get_p2(), t.get_p3()), ctx);
3716 }
3717};
3718
3720template <>
3721struct std::formatter<Aleph::Rectangle> : std::formatter<std::string>
3722{
3723 auto format(const Aleph::Rectangle & r, std::format_context & ctx) const
3724 {
3725 return std::formatter<std::string>::format(
3726 std::format("Rectangle({}, {} -- {}, {})",
3727 Aleph::geom_number_to_double(r.get_xmin()),
3728 Aleph::geom_number_to_double(r.get_ymin()),
3729 Aleph::geom_number_to_double(r.get_xmax()),
3730 Aleph::geom_number_to_double(r.get_ymax())), ctx);
3731 }
3732};
3733
3735template <>
3736struct std::formatter<Aleph::Polar_Point> : std::formatter<std::string>
3737{
3738 auto format(const Aleph::Polar_Point & p, std::format_context & ctx) const
3739 {
3740 return std::formatter<std::string>::format(
3741 std::format("PolarPoint(r={}, theta={})",
3744 }
3745};
3746
3748template <>
3749struct std::formatter<Aleph::Ellipse> : std::formatter<std::string>
3750{
3751 auto format(const Aleph::Ellipse & e, std::format_context & ctx) const
3752 {
3753 return std::formatter<std::string>::format(
3754 std::format("{}",
3755 e.to_string()), ctx);
3756 }
3757};
3758
3760template <>
3761struct std::formatter<Aleph::RotatedEllipse> : std::formatter<std::string>
3762{
3763 auto format(const Aleph::RotatedEllipse & e, std::format_context & ctx) const
3764 {
3765 return std::formatter<std::string>::format(
3766 std::format("{}",
3767 e.to_string()), ctx);
3768 }
3769};
3770
3772template <>
3773struct std::formatter<Aleph::Point3D> : std::formatter<std::string>
3774{
3775 auto format(const Aleph::Point3D & p, std::format_context & ctx) const
3776 {
3777 return std::formatter<std::string>::format(
3778 std::format("Point3D({}, {}, {})",
3782 }
3783};
3784
3786template <>
3787struct std::formatter<Aleph::Segment3D> : std::formatter<std::string>
3788{
3789 auto format(const Aleph::Segment3D & s, std::format_context & ctx) const
3790 {
3791 return std::formatter<std::string>::format(
3792 std::format("Segment3D({} -> {})",
3793 s.get_src(), s.get_tgt()), ctx);
3794 }
3795};
3796
3798template <>
3799struct std::formatter<Aleph::Triangle3D> : std::formatter<std::string>
3800{
3801 auto format(const Aleph::Triangle3D & t, std::format_context & ctx) const
3802 {
3803 return std::formatter<std::string>::format(
3804 std::format("Triangle3D({}, {}, {})",
3805 t.get_p1(), t.get_p2(), t.get_p3()), ctx);
3806 }
3807};
3808
3810template <>
3811struct std::formatter<Aleph::Tetrahedron> : std::formatter<std::string>
3812{
3813 auto format(const Aleph::Tetrahedron & t, std::format_context & ctx) const
3814 {
3815 return std::formatter<std::string>::format(
3816 std::format("Tetrahedron({}, {}, {}, {})",
3817 t.get_p1(), t.get_p2(), t.get_p3(), t.get_p4()), ctx);
3818 }
3819};
3820
3821# endif // __cpp_lib_format
3822#endif // C++20 format
3823
3824# endif // POINT_H
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
#define ah_range_error_if(C)
Throws std::range_error if condition holds.
Definition ah-errors.H:207
Debug assertion and warning utilities.
General utility functions and helpers.
long double hr
Definition btreepic.C:149
long double vr
Definition btreepic.C:150
long double h
Definition btreepic.C:154
long double w
Definition btreepic.C:153
An axis-aligned ellipse.
Definition point.H:2006
static bool in_unit_interval(const Geom_Number &t)
Helper to check if a parameter t is in the [0, 1] interval.
Definition point.H:2146
const Geom_Number & get_vradius() const
Gets the vertical radius.
Definition point.H:2080
Ellipse(const Ellipse &e)=default
Copy constructor.
Geom_Number area() const
Calculates the area of the ellipse.
Definition point.H:2086
void compute_tangents(Segment &s1, Segment &s2, const Geom_Number &m) const
Computes the two tangent segments to this ellipse with a given slope.
Definition point.H:2189
std::string to_string() const
Returns a string representation of the ellipse.
Definition point.H:2126
Point sample(const Geom_Number &angle) const
Samples a point on the ellipse's boundary at a given angle.
Definition point.H:2113
Ellipse(Point center, const Geom_Number &hr, const Geom_Number &vr)
Constructs an ellipse.
Definition point.H:2032
bool intersects_with(const Point &p) const
Checks if a point lies exactly on the ellipse boundary.
Definition point.H:2299
const Point & get_center() const
Gets the center point of the ellipse.
Definition point.H:2076
Point highest_point() const
Gets the highest point on the ellipse boundary.
Definition point.H:2159
Point rightmost_point() const
Gets the rightmost point on the ellipse boundary.
Definition point.H:2177
Geom_Number perimeter() const
Approximates the perimeter of the ellipse.
Definition point.H:2097
bool intersects_with(const Segment &s) const
Checks if a segment intersects the ellipse.
Definition point.H:2224
bool contains_to(const Point &p) const
Definition point.H:2292
static bool is_clockwise()
Returns the orientation of the ellipse.
Definition point.H:2156
const Geom_Number & get_hradius() const
Gets the horizontal radius.
Definition point.H:2078
void validate_positive_radii() const
Validates that radii are positive.
Definition point.H:2138
Segment intersection_with(const Segment &sg) const
Computes the intersection segment between a line segment and this ellipse.
Definition point.H:2311
Geom_Number vr_
Definition point.H:2022
Geom_Number compute_radius(const Point &p) const
Computes the ellipse equation value for a point.
Definition point.H:2266
Point leftmost_point() const
Gets the leftmost point on the ellipse boundary.
Definition point.H:2171
friend class Point
Definition point.H:2007
Geom_Number hr_
Definition point.H:2021
Point lowest_point() const
Gets the lowest point on the ellipse boundary.
Definition point.H:2165
bool operator!=(const Ellipse &e) const noexcept
Checks for inequality.
Definition point.H:2070
Ellipse()
Default constructor.
Definition point.H:2050
bool operator==(const Ellipse &e) const noexcept
Checks for exact equality.
Definition point.H:2060
Point center_
Definition point.H:2019
bool contains(const Point &p) const
Checks if a point lies inside or on the boundary of this ellipse.
Definition point.H:2285
Represents a point in 3D space with exact rational coordinates.
Definition point.H:2973
Point3D operator+(const Point3D &p) const
Vector addition.
Definition point.H:3027
Point3D & operator=(const Point3D &)=default
Point3D operator/(const Geom_Number &s) const
Scalar division.
Definition point.H:3092
Point3D()
Default constructor.
Definition point.H:2980
Geom_Number y_
Definition point.H:2974
Geom_Number z_
Definition point.H:2974
Point3D operator-() const
Unary negation.
Definition point.H:3072
static Point3D from_2d(const Point &p)
Lifts a 2D point to 3D, setting its z-coordinate to 0.
Definition point.H:3185
Point3D normalize() const
Returns a normalized copy of this vector (unit vector).
Definition point.H:3167
Point3D(const Geom_Number &x, const Geom_Number &y, const Geom_Number &z)
Constructs a 3D point from x, y, and z coordinates.
Definition point.H:2988
bool operator==(const Point3D &p) const
Checks for exact equality between two 3D points.
Definition point.H:3007
Point3D operator-(const Point3D &p) const
Vector subtraction.
Definition point.H:3037
Geom_Number x_
Definition point.H:2974
bool operator!=(const Point3D &p) const
Checks for inequality between two 3D points.
Definition point.H:3017
Geom_Number distance_squared_to(const Point3D &p) const
Squared Euclidean distance to another point.
Definition point.H:3126
Point3D cross(const Point3D &p) const
Cross product.
Definition point.H:3112
const Geom_Number & get_z() const
Gets the z-coordinate.
Definition point.H:3000
Point3D & operator+=(const Point3D &p)
Vector addition and assignment.
Definition point.H:3047
const Geom_Number & get_x() const
Gets the x-coordinate.
Definition point.H:2996
Point3D operator*(const Geom_Number &s) const
Scalar multiplication.
Definition point.H:3082
Point3D(const Point3D &)=default
Geom_Number norm_squared() const
Squared Euclidean norm (magnitude).
Definition point.H:3138
const Geom_Number & get_y() const
Gets the y-coordinate.
Definition point.H:2998
Geom_Number distance_to(const Point3D &p) const
Euclidean distance to another point.
Definition point.H:3157
Point to_2d() const
Projects this 3D point to a 2D point by dropping the z-coordinate.
Definition point.H:3178
Point3D & operator-=(const Point3D &p)
Vector subtraction and assignment.
Definition point.H:3060
Geom_Number dot(const Point3D &p) const
Dot product.
Definition point.H:3102
static Point3D from_2d(const Point &p, const Geom_Number &z)
Lifts a 2D point to 3D with a specified z-coordinate.
Definition point.H:3196
Geom_Number norm() const
Euclidean norm (magnitude).
Definition point.H:3147
Represents a point with rectangular coordinates in a 2D plane.
Definition point.H:229
bool is_to_right_from(const Point &p1, const Point &p2) const
Checks if this point is to the right of the directed line from p1 to p2.
Definition point.H:513
bool is_between(const Point &p1, const Point &p2) const
Checks if this point is on the bounding box of p1 and p2 and is collinear with them.
Definition point.H:599
Geom_Number distance_with(const Point &p) const
Definition point.H:679
Geom_Number y_
Definition point.H:235
Geom_Number norm_squared() const
Squared Euclidean norm.
Definition point.H:393
bool is_inside(const Segment &s) const
Checks if this point is contained within a segment.
Definition point.H:1423
Geom_Number norm() const
Euclidean norm (vector magnitude).
Definition point.H:402
const Point & rightmost_point() const
Returns the rightmost point (largest x-coordinate).
Definition point.H:706
const Geom_Number & get_x() const noexcept
Gets the x-coordinate value.
Definition point.H:457
bool operator==(const Point &point) const noexcept
Checks for exact equality between two points.
Definition point.H:268
std::string to_string() const
Returns a string representation of the point as "(x,y)".
Definition point.H:651
Geom_Number dot(const Point &p) const
Dot product.
Definition point.H:373
bool intersects_with(const Ellipse &e) const
Checks if this point lies exactly on the boundary of an ellipse.
Definition point.H:2366
const Point & leftmost_point() const
Returns the leftmost point (smallest x-coordinate).
Definition point.H:700
Point operator*(const Geom_Number &s) const
Scalar multiplication.
Definition point.H:353
bool operator<(const Point &point) const noexcept
Defines a strict lexicographical ordering for points.
Definition point.H:288
Point(const Geom_Number &x, const Geom_Number &y)
Constructs a point from Cartesian coordinates.
Definition point.H:251
const Geom_Number & get_y() const noexcept
Gets the y-coordinate value.
Definition point.H:466
Point operator/(const Geom_Number &s) const
Scalar division.
Definition point.H:363
Point midpoint(const Point &other) const
Calculates the midpoint between this point and another.
Definition point.H:448
bool is_to_left_from(const Point &p1, const Point &p2) const
Definition point.H:502
Geom_Number distance_to(const Point &p) const
Calculates the Euclidean distance to another point.
Definition point.H:1463
const Point & highest_point() const
Returns the highest point (largest y-coordinate).
Definition point.H:688
Geom_Number cross(const Point &p) const
2D cross-product (z-component of the 3D cross-product).
Definition point.H:383
bool is_clockwise_with(const Point &p1, const Point &p2) const
Determines if the sequence of three points (this, p1, p2) makes a clockwise turn.
Definition point.H:553
Geom_Number x_
Definition point.H:234
Point lerp(const Point &other, const Geom_Number &t) const
Linear interpolation between this point and another.
Definition point.H:437
const Point & nearest_point(const Point &p1, const Point &p2) const
Returns which of the two points, p1 or p2, is nearer to this point.
Definition point.H:618
const Point & lowest_point() const
Returns the lowest point (smallest y-coordinate).
Definition point.H:694
Point & operator-=(const Point &p)
Vector subtraction and assignment.
Definition point.H:331
Point & operator+=(const Point &p)
Vector addition and assignment.
Definition point.H:308
bool is_to_right_from(const Segment &s) const
Definition point.H:581
bool is_right_on_of(const Point &p1, const Point &p2) const
Checks if this point is to the right of or on the line from p1 to p2.
Definition point.H:535
bool is_to_left_from(const Segment &s) const
Definition point.H:574
bool is_to_left_on_from(const Point &p1, const Point &p2) const
Checks if this point is to the left of or on the line from p1 to p2.
Definition point.H:524
bool is_to_right_on_from(const Point &p1, const Point &p2) const
Definition point.H:542
bool operator!=(const Point &point) const noexcept
Checks for inequality between two points.
Definition point.H:278
Point operator+(const Point &p) const
Vector addition.
Definition point.H:298
bool is_right_of(const Segment &s) const
Checks if this point is to the right of a directed segment.
Definition point.H:1441
bool is_left_of(const Point &p1, const Point &p2) const
Checks if this point is to the left of the directed line from p1 to p2.
Definition point.H:495
Geom_Number distance_squared_to(const Point &that) const
Calculates the squared Euclidean distance to another point.
Definition point.H:1454
Point rotate(const Geom_Number &angle) const
Rotates the point around the origin by a given angle.
Definition point.H:424
Point()
Default constructor.
Definition point.H:242
Point operator-() const
Unary negation (vector inversion).
Definition point.H:343
bool is_colinear_with(const Point &p1, const Point &p2) const
Checks if this point is collinear with two other points.
Definition point.H:477
Point normalize() const
Returns a normalized copy of this vector (magnitude 1).
Definition point.H:412
Polar representation of a 2D point.
Definition point.H:726
Polar_Point(const Geom_Number &r, const Geom_Number &theta)
Constructs a polar point from a radius and an angle.
Definition point.H:750
Quadrant
Enumerates polar quadrants in counterclockwise order.
Definition point.H:770
Polar_Point(const Point &p)
Constructs a polar point from a Cartesian point.
Definition point.H:760
const Geom_Number & get_theta() const
Gets the angle (theta) of the polar point.
Definition point.H:743
Quadrant get_quadrant() const
Returns the quadrant where the point lies.
Definition point.H:777
std::string to_string() const
Converts the polar point to a string representation "[r,theta]".
Definition point.H:796
Polar_Point()=default
Default constructor.
const Geom_Number & get_r() const
Gets the radius (magnitude) of the polar point.
Definition point.H:737
Geom_Number theta_
Definition point.H:730
Geom_Number r_
Definition point.H:729
An axis-aligned rectangle.
Definition point.H:1737
const Geom_Number & get_xmin() const
Gets the minimum x-coordinate.
Definition point.H:1764
std::string to_string() const
Returns a string representation of the rectangle.
Definition point.H:1912
void set_rect(const Geom_Number &xmin, const Geom_Number &ymin, const Geom_Number &xmax, const Geom_Number &ymax)
Sets the coordinates of the rectangle.
Definition point.H:1803
Geom_Number area() const noexcept
Calculates the area of the rectangle.
Definition point.H:1819
Rectangle()
Default constructor.
Definition point.H:1775
Geom_Number perimeter() const noexcept
Calculates the perimeter of the rectangle.
Definition point.H:1825
bool intersects(const Rectangle &that) const noexcept
Checks if this axis-aligned rectangle intersects another one.
Definition point.H:1859
Geom_Number width() const noexcept
Calculates the width of the rectangle.
Definition point.H:1815
std::array< Point, 4 > corners() const noexcept
Gets the four corners of the rectangle.
Definition point.H:1844
const Geom_Number & get_ymax() const
Gets the maximum y-coordinate.
Definition point.H:1770
const Geom_Number & get_ymin() const
Gets the minimum y-coordinate.
Definition point.H:1766
Geom_Number distance_squared_to(const Point &p) const noexcept
Calculates the squared Euclidean distance from the rectangle to a point.
Definition point.H:1871
Geom_Number ymax_
Definition point.H:1739
const Geom_Number & get_xmax() const
Gets the maximum x-coordinate.
Definition point.H:1768
Rectangle(const Geom_Number &xmin, const Geom_Number &ymin, const Geom_Number &xmax, const Geom_Number &ymax)
Constructs a rectangle from four coordinates.
Definition point.H:1788
Geom_Number distance_to(const Point &p) const
Calculates the Euclidean distance from the rectangle to a point.
Definition point.H:1892
Geom_Number xmin_
Definition point.H:1738
Geom_Number ymin_
Definition point.H:1738
bool operator==(const Rectangle &r) const noexcept
Checks for exact equality between two rectangles.
Definition point.H:1747
bool contains(const Point &p) const noexcept
Checks if this axis-aligned rectangle contains a point.
Definition point.H:1902
bool operator!=(const Rectangle &r) const noexcept
Checks for inequality between two rectangles.
Definition point.H:1758
Point center() const noexcept
Calculates the center point of the rectangle.
Definition point.H:1834
Geom_Number height() const noexcept
Calculates the height of the rectangle.
Definition point.H:1817
Geom_Number xmax_
Definition point.H:1739
An ellipse with arbitrary rotation.
Definition point.H:2404
bool strictly_contains(const Point &p) const
Checks if a point lies strictly inside the ellipse.
Definition point.H:2583
RotatedEllipse(Point center, const Geom_Number &a, const Geom_Number &b, const Geom_Number &cos_theta, const Geom_Number &sin_theta)
Constructs a rotated ellipse.
Definition point.H:2473
bool operator!=(const RotatedEllipse &e) const noexcept
Checks for inequality between two rotated ellipses.
Definition point.H:2527
bool contains(const Point &p) const
Checks if a point lies inside or on the ellipse.
Definition point.H:2573
Point sample(const Geom_Number &t) const
Samples a point on the ellipse boundary for a parameter t.
Definition point.H:2618
RotatedEllipse(Point center, const Geom_Number &a, const Geom_Number &b)
Constructs an axis-aligned ellipse (rotation angle is 0).
Definition point.H:2490
Geom_Number cos_th_
Definition point.H:2408
Geom_Number a_
Definition point.H:2406
Geom_Number radius_value(const Point &p) const
Evaluates the ellipse equation for a given point.
Definition point.H:2559
RotatedEllipse & operator=(const RotatedEllipse &)=default
const Geom_Number & get_b() const
Gets the semi-axis 'b' (local y-axis radius).
Definition point.H:2537
Geom_Number sin_th_
Definition point.H:2409
Geom_Number b_
Definition point.H:2407
void normalize_rotation()
Normalizes the rotation (cos, sin) vector to ensure it's a unit vector.
Definition point.H:2452
Geom_Number area() const
Calculates the area of the ellipse.
Definition point.H:2547
Segment intersection_with(const Segment &s) const
Computes the intersection segment between s and this ellipse.
Definition point.H:2642
Point to_world(const Point &p) const
Transforms a point from the ellipse's local frame back to world coordinates.
Definition point.H:2431
Point sample(const Geom_Number &cos_t, const Geom_Number &sin_t) const
Samples a point on the ellipse boundary from a parametric angle.
Definition point.H:2604
ExtremalPoints extremal_points() const
Computes the four axis-extremal points of the rotated ellipse.
Definition point.H:2671
RotatedEllipse()
Default constructor.
Definition point.H:2501
const Geom_Number & get_sin() const
Gets the sine of the rotation angle.
Definition point.H:2541
std::string to_string() const
Definition point.H:2650
RotatedEllipse(const RotatedEllipse &)=default
Point to_local(const Point &p) const
Transforms a world point to the ellipse's local, unrotated frame.
Definition point.H:2416
bool on_boundary(const Point &p) const
Checks if a point lies exactly on the ellipse boundary.
Definition point.H:2593
bool intersects_with(const Segment &s) const
Checks if a segment intersects this rotated ellipse.
Definition point.H:2629
const Geom_Number & get_a() const
Gets the semi-axis 'a' (local x-axis radius).
Definition point.H:2535
const Geom_Number & get_cos() const
Gets the cosine of the rotation angle.
Definition point.H:2539
const Point & get_center() const
Gets the center point.
Definition point.H:2533
bool operator==(const RotatedEllipse &e) const noexcept
Checks for exact equality between two rotated ellipses.
Definition point.H:2516
void validate_positive_radii() const
Validates that semi-axes are positive.
Definition point.H:2443
Represents a line segment in 3D space.
Definition point.H:3214
bool operator==(const Segment3D &s) const
Checks for equality between two segments.
Definition point.H:3288
bool operator!=(const Segment3D &s) const
Checks for inequality.
Definition point.H:3299
Point3D midpoint() const
Calculates the midpoint of the segment.
Definition point.H:3276
bool contains(const Point3D &p) const
Checks if a point lies on the segment.
Definition point.H:3309
Point3D src_
Definition point.H:3215
const Point3D & get_src() const noexcept
Gets the source point of the segment.
Definition point.H:3229
Geom_Number distance_to(const Point3D &p) const
Calculates the shortest distance from a point to this segment.
Definition point.H:3331
const Point3D & get_tgt() const noexcept
Gets the target point of the segment.
Definition point.H:3231
Segment3D()=default
Default constructor.
const Point3D & get_src_point() const noexcept
Gets the source point of the segment (alias for get_src).
Definition point.H:3233
Point3D at(const Geom_Number &t) const
Evaluates a point on the segment via linear interpolation.
Definition point.H:3266
Segment3D(const Point3D &src, const Point3D &tgt)
Constructs a 3D segment from two endpoints.
Definition point.H:3226
const Point3D & get_tgt_point() const noexcept
Gets the target point of the segment (alias for get_tgt).
Definition point.H:3235
Point3D direction() const
Calculates the direction vector of the segment.
Definition point.H:3241
Geom_Number length_squared() const
Calculates the squared length of the segment.
Definition point.H:3247
Point3D tgt_
Definition point.H:3215
Geom_Number length() const
Calculates the length of the segment.
Definition point.H:3256
Represents a line segment between two points.
Definition point.H:827
const Point & rightmost_point() const noexcept
Gets the endpoint with the largest x-coordinate.
Definition point.H:906
bool intersects_properly_with(const Segment &s) const
Checks if this segment properly intersects another segment.
Definition point.H:1228
bool is_to_right_from(const Point &p) const
Definition point.H:1106
bool contains_to(const Point &p) const
Definition point.H:1253
bool contains(const Segment &s) const
Checks if another segment s lies entirely inside this segment.
Definition point.H:1260
Point mid_point() const
Returns the midpoint of this segment.
Definition point.H:1115
bool contains_to(const Segment &s) const
Definition point.H:1267
double slope() const
Returns the slope of the segment.
Definition point.H:999
const Point & nearest_point(const Point &p) const
Returns whichever endpoint of this segment is nearer to a given point.
Definition point.H:1180
double counterclockwise_angle() const
Computes the counter-clockwise angle of this segment with respect to the x-axis.
Definition point.H:1046
Sense
Cardinal and intercardinal directions associated with a segment's vector.
Definition point.H:1327
bool is_left_of(const Point &p) const
Checks if this segment is to the left of a given point.
Definition point.H:1081
std::string to_string() const
Returns a string representation of the segment, e.g., "(x1,y1)(x2,y2)".
Definition point.H:1381
const Point & highest_point() const noexcept
Gets the endpoint with the largest y-coordinate.
Definition point.H:879
double compute_slope() const
Internal helper to compute the slope as a double.
Definition point.H:837
bool is_to_left_from(const Point &p) const
Definition point.H:1099
void rotate(const Geom_Number &angle)
Rotates the segment by a given angle around its source point.
Definition point.H:1398
Point intersection_with(const Segment &s) const
Computes the intersection point of the infinite lines defined by two segments.
Definition point.H:1313
const Point & lowest_point() const noexcept
Gets the endpoint with the smallest y-coordinate.
Definition point.H:888
Segment(Point src, Point tgt)
Constructs a segment from two endpoints.
Definition point.H:933
bool intersects_with(const Segment &s) const
Checks if this segment intersects another one (including endpoints and collinear overlap).
Definition point.H:1274
void enlarge_src(const Geom_Number &dist)
Extends the segment by a given distance from the source endpoint.
Definition point.H:1361
void enlarge_tgt(const Geom_Number &dist)
Extends the segment by a given distance from the target endpoint.
Definition point.H:1371
Point project(const Point &p) const
Orthogonal projection of a point onto this segment's infinite line, clamped to the segment's endpoint...
Definition point.H:1147
const Point & leftmost_point() const noexcept
Gets the endpoint with the smallest x-coordinate.
Definition point.H:897
Segment(const Segment &sg, const Geom_Number &dist)
Constructs a new segment parallel to a given segment at a specified distance.
Definition point.H:983
Point at(const Geom_Number &t) const
Evaluates a point on the segment via linear interpolation.
Definition point.H:1137
Geom_Number size() const
Definition point.H:1063
double counterclockwise_angle_with(const Segment &s) const
Computes the counter-clockwise rotation angle FROM this segment TO another.
Definition point.H:1025
Segment()=default
Default constructor.
Segment mid_perpendicular(const Geom_Number &dist) const
Returns the perpendicular chord of a given length passing through the midpoint.
Definition point.H:1212
friend class Point
Definition point.H:828
Segment reversed() const
Returns a new segment with the endpoints swapped.
Definition point.H:1127
bool is_right_of(const Point &p) const
Checks if this segment is to the right of a given point.
Definition point.H:1092
Geom_Number distance_to(const Point &p) const
Calculates the Euclidean distance from a point to this segment.
Definition point.H:1169
static Point compute_tgt_point(const Point &src, const Geom_Number &m, const Geom_Number &d)
Computes the target point of a segment given a source, slope, and length.
Definition point.H:948
bool contains(const Point &p) const
Checks if a point lies on this segment.
Definition point.H:1246
Geom_Number slope_exact() const
Returns the exact slope of the segment as a Geom_Number.
Definition point.H:1009
bool operator!=(const Segment &s) const noexcept
Checks for inequality between two segments.
Definition point.H:870
Sense sense() const
Determines the cardinal/intercardinal direction of the segment.
Definition point.H:1333
bool is_colinear_with(const Point &p) const
Checks if a point is collinear with this segment.
Definition point.H:1070
Point tgt_
Definition point.H:831
bool operator==(const Segment &s) const noexcept
Checks for equality between two segments.
Definition point.H:859
Segment(Point src, const Geom_Number &m, const Geom_Number &l)
Constructs a new segment from a source point, slope, and length.
Definition point.H:972
const Point & get_tgt_point() const noexcept
Gets the target point of the segment.
Definition point.H:921
bool is_parallel_with(const Segment &s) const
Checks if this segment is parallel to another one.
Definition point.H:1302
const Point & get_src_point() const noexcept
Gets the source point of the segment.
Definition point.H:915
Geom_Number length() const
Returns the Euclidean length of the segment.
Definition point.H:1056
Point src_
Definition point.H:831
Segment get_perpendicular(const Point &p) const
Constructs a segment perpendicular to this that passes through point p.
Definition point.H:1190
Represents a tetrahedron in 3D space defined by four points.
Definition point.H:3463
Point3D centroid() const
Computes the centroid (center of mass) of the tetrahedron.
Definition point.H:3526
const Point3D & get_p4() const
Gets the fourth vertex.
Definition point.H:3488
Faces faces() const
Gets the four faces of the tetrahedron.
Definition point.H:3573
Geom_Number signed_volume_x6() const
Calculates 6 times the signed volume of the tetrahedron.
Definition point.H:3497
Tetrahedron(const Point3D &p1, const Point3D &p2, const Point3D &p3, const Point3D &p4)
Constructs a tetrahedron from four vertices.
Definition point.H:3477
Tetrahedron()=default
Default constructor.
bool is_degenerate() const
Checks if the tetrahedron is degenerate (i.e., its vertices are coplanar).
Definition point.H:3517
Geom_Number volume() const
Calculates the unsigned volume of the tetrahedron.
Definition point.H:3506
const Point3D & get_p1() const
Gets the first vertex.
Definition point.H:3482
const Point3D & get_p2() const
Gets the second vertex.
Definition point.H:3484
const Point3D & get_p3() const
Gets the third vertex.
Definition point.H:3486
bool contains(const Point3D &p) const
Checks if a point lies inside the tetrahedron.
Definition point.H:3538
Represents a text string positioned at a 2D point.
Definition point.H:2739
Text(Point p, const std::string &str)
Constructs a Text object.
Definition point.H:2756
std::string str_
Definition point.H:2742
Point lowest_point() const
Gets the lowest point (the anchor point).
Definition point.H:2784
static constexpr double font_height_in_points
Definition point.H:2749
static constexpr double font_width_in_points
Definition point.H:2747
Point highest_point() const
Gets the highest point (the anchor point).
Definition point.H:2778
Point rightmost_point() const
Gets the rightmost point (the anchor point).
Definition point.H:2796
const std::string & get_str() const
Gets the text string.
Definition point.H:2775
const Point & get_point() const
Gets the position point of the text.
Definition point.H:2769
Point p_
Definition point.H:2740
Point leftmost_point() const
Gets the leftmost point (the anchor point).
Definition point.H:2790
const size_t & len() const
Gets the estimated length of the text.
Definition point.H:2766
Text()=default
Default constructor.
size_t len_
Definition point.H:2744
Represents a triangle in 3D space defined by three points.
Definition point.H:3359
BaryCoords barycentric(const Point3D &p) const
Computes the barycentric coordinates of a point p with respect to this triangle.
Definition point.H:3438
Point3D centroid() const
Computes the centroid (center of mass) of the triangle.
Definition point.H:3407
const Point3D & get_p2() const
Gets the second vertex of the triangle.
Definition point.H:3378
Triangle3D()=default
Default constructor.
const Point3D & get_p3() const
Gets the third vertex of the triangle.
Definition point.H:3380
Triangle3D(const Point3D &p1, const Point3D &p2, const Point3D &p3)
Constructs a 3D triangle from three vertices.
Definition point.H:3372
Point3D normal() const
Computes the normal vector of the triangle's plane.
Definition point.H:3389
const Point3D & get_p1() const
Gets the first vertex of the triangle.
Definition point.H:3376
Geom_Number double_area_squared() const
Calculates twice the squared area of the triangle.
Definition point.H:3398
bool is_degenerate() const
Checks if the triangle is degenerate (i.e., its vertices are collinear).
Definition point.H:3416
A non-degenerate triangle defined by three points.
Definition point.H:1478
Geom_Number perimeter() const
Computes the perimeter of the triangle.
Definition point.H:1631
Segment intersection_with(const Segment &s) const
Computes the intersection segment between this triangle and a segment.
Definition point.H:1723
const Point & get_p3() const
Gets the third vertex.
Definition point.H:1591
const Point & get_p2() const
Gets the second vertex.
Definition point.H:1589
bool contains(const Point &p) const
Checks if a point lies strictly inside this triangle.
Definition point.H:1706
Geom_Number area() const
Calculates the unsigned area of the triangle.
Definition point.H:1532
friend class Segment
Definition point.H:1480
Point incenter() const
Computes the incenter of the triangle.
Definition point.H:1678
const Point & rightmost_point() const
Gets the vertex with the largest x-coordinate.
Definition point.H:1580
const Point & lowest_point() const
Gets the vertex with the smallest y-coordinate.
Definition point.H:1560
const Point & get_p1() const
Gets the first vertex.
Definition point.H:1587
bool operator==(const Triangle &t) const noexcept
Checks for equality between two triangles.
Definition point.H:1600
Triangle(const Segment &s, Point p)
Constructs a triangle from a segment and a point.
Definition point.H:1521
std::array< Segment, 3 > edges() const
Gets the three edges of the triangle.
Definition point.H:1693
const Point & highest_point() const
Gets the vertex with the largest y-coordinate.
Definition point.H:1550
const Point & leftmost_point() const
Gets the vertex with the smallest x-coordinate.
Definition point.H:1570
Point circumcenter() const
Computes the circumcenter of the triangle.
Definition point.H:1643
bool operator!=(const Triangle &t) const noexcept
Checks for inequality between two triangles.
Definition point.H:1613
Point centroid() const
Computes the centroid (center of mass) of the triangle.
Definition point.H:1622
Triangle(Point p1, Point p2, Point p3)
Constructs a triangle from three points.
Definition point.H:1494
Triangle(Point p, const Segment &s)
Constructs a triangle from a point and a segment.
Definition point.H:1508
bool is_clockwise() const
Checks if the triangle vertices are ordered clockwise.
Definition point.H:1541
Geom_Number area_
Definition point.H:1484
std::string get_str(int base=10) const
Definition gmpfrxx.h:2169
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_hypot_function > > hypot(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4112
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_y1_function > > y1(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4103
__gmp_expr< mpfr_t, mpfr_t > mpfr_class
Definition gmpfrxx.h:2446
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_cos_function > > cos(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4069
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_acos_function > > acos(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4075
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_max_function > > max(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4110
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_sin_function > > sin(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4070
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_sqrt_function > > sqrt(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4058
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_abs_function > > abs(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4051
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_atan2_function > > atan2(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4078
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_atan_function > > atan(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4077
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_min_function > > min(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4111
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4060
__gmp_expr< mpq_t, mpq_t > mpq_class
Definition gmpfrxx.h:2214
ostream & operator<<(ostream &os, const Task &t)
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Orientation
Classification of three-point orientation.
Definition point.H:2812
Geom_Number in_circle_determinant(const Point &a, const Point &b, const Point &c, const Point &p)
Return the exact in-circle determinant for (a,b,c,p).
Definition point.H:2835
Geom_Number pitag(const Geom_Number &x, const Geom_Number &y)
Definition point.H:175
Geom_Number area_of_parallelogram(const Point &a, const Point &b, const Point &c)
Compute the signed area of the parallelogram defined by vectors a->b and a->c.
Definition point.H:2803
Geom_Number scalar_triple_product(const Point3D &a, const Point3D &b, const Point3D &c)
Scalar triple product: a · (b × c).
Definition point.H:3204
bool on_segment(const Segment &s, const Point &p)
Return true if p lies on segment s (exact).
Definition point.H:2876
Geom_Number cosinus(const Geom_Number &x)
Cosine of x (wrapper over mpfr).
Definition point.H:199
constexpr double PI_4
Definition point.H:134
bool segments_intersect(const Segment &s1, const Segment &s2)
Return true if segments s1 and s2 intersect (including endpoints).
Definition point.H:2883
InCircleResult in_circle(const Point &a, const Point &b, const Point &c, const Point &p)
Classify point p against circumcircle of triangle (a,b,c), exactly.
Definition point.H:2857
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
Point segment_intersection_point(const Segment &s1, const Segment &s2)
Compute the exact intersection point of segments s1 and s2.
Definition point.H:2900
InCircleResult
Classification of a point with respect to a triangle circumcircle.
Definition point.H:2830
Geom_Number square_root(const Geom_Number &x)
Square root of x (wrapper over mpfr).
Definition point.H:205
double geom_number_to_double(const Geom_Number &n)
Converts a Geom_Number to its double precision representation.
Definition point.H:122
Geom_Number area_of_triangle(const Point &a, const Point &b, const Point &c)
Return the (unsigned) area of triangle (a, b, c) as an exact rational.
Definition point.H:2956
Matrix< Trow, Tcol, NumType > operator*(const NumType &scalar, const Matrix< Trow, Tcol, NumType > &m)
Scalar-matrix multiplication (scalar * matrix).
Definition al-matrix.H:995
Orientation orientation(const Point &a, const Point &b, const Point &c)
Return the orientation of the triple (a, b, c).
Definition point.H:2821
constexpr double PI_2
Definition point.H:133
mpq_class Geom_Number
Numeric type used by the geometry module.
Definition point.H:115
Geom_Number arctan2(const Geom_Number &m, const Geom_Number &n)
Two-argument arc tangent (wrapper over mpfr).
Definition point.H:187
const Geom_Number & geom_pi()
High-precision pi value for computations that require Geom_Number.
Definition point.H:140
size_t approximate_string_size(const std::string &str)
Estimate the count of printable characters in a LaTeX string.
Definition point.H:2700
Geom_Number arctan(const Geom_Number &m)
Arc tangent of m (wrapper over mpfr).
Definition point.H:181
Geom_Number euclidean_distance(const Geom_Number &x, const Geom_Number &y)
Euclidean distance (hypotenuse) of the vector (x, y).
Definition point.H:167
std::ostream & operator<<(std::ostream &osObject, const Field< T > &rightOp)
Definition ahField.H:121
constexpr double PI
Definition point.H:132
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
Geom_Number sinus(const Geom_Number &x)
Sine of x (wrapper over mpfr).
Definition point.H:193
STL namespace.
Base class for all geometric objects.
Definition point.H:214
virtual ~Geom_Object()=default
Geom_Object()=default
Holds the four axis-extremal points of a rotated ellipse.
Definition point.H:2661
A struct holding the four faces of the tetrahedron.
Definition point.H:3565
Structure to hold barycentric coordinates.
Definition point.H:3428
std::size_t operator()(const Aleph::Point &p) const
Definition point.H:3664
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
gsl_rng * r
DynList< int > l