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
42#ifndef POINT_H
43#define POINT_H
44
45#include <cmath>
46
47#include <cstddef>
48#include <limits>
49#include <array>
50#include <functional>
51
52#include <iomanip>
53#include <string>
54
55#include <ahAssert.H>
56#include <ahUtils.H>
57#include <ah-errors.H>
58#include <utility>
59
60#include <gmpfrxx.h>
61
68#if defined(_LIBCPP_VERSION)
75inline std::ostream &operator << (std::ostream &o, mpq_srcptr q)
76{
77 o << mpq_get_d(q);
78 return o;
79}
80
87inline std::ostream &operator << (std::ostream &o, mpz_srcptr z)
88{
89 o << mpz_get_d(z);
90 return o;
91}
92
99inline std::ostream &operator << (std::ostream &o, mpf_srcptr f)
100{
101 o << mpf_get_d(f);
102 return o;
103}
104#endif
105
106namespace Aleph {
114
120inline double geom_number_to_double(const Geom_Number &n)
121{
122 return n.get_d();
123}
124
125// TODO: provide helpers for rotating special figures (e.g., ellipses) by
126// rotating the Cartesian axes instead of recomputing points from scratch.
127
128// Legacy double-precision constants kept for backward compatibility.
129constexpr double PI = 3.1415926535897932384626433832795028841971693993751;
130constexpr double PI_2 = PI / 2.0;
131constexpr double PI_4 = PI / 4.0;
132
137inline const Geom_Number &geom_pi()
138{
139 static const Geom_Number pi = acos(mpfr_class(-1));
140 return pi;
141}
142
143class Point;
144class Polar_Point;
145class Segment;
146class Triangle;
147class Ellipse;
148
157inline Geom_Number area_of_parallelogram(const Point &a, const Point &b, const Point &c);
158
161{
162 return hypot(mpfr_class(x), mpfr_class(y));
163}
164
166[[deprecated("Use euclidean_distance() instead")]]
167inline Geom_Number pitag(const Geom_Number &x, const Geom_Number &y)
168{
169 return euclidean_distance(x, y);
170}
171
174{
175 return atan(mpfr_class(m));
176}
177
180{
181 return atan2(mpfr_class(m), mpfr_class(n));
182}
183
186{
187 return sin(mpfr_class(x));
188}
189
192{
193 return cos(mpfr_class(x));
194}
195
198{
199 return sqrt(mpfr_class(x));
200}
201
206{
207 Geom_Object() = default;
208
209 virtual ~Geom_Object() = default;
210};
211
220class Point : public Geom_Object
221{
222 friend class Segment;
223 friend class Triangle;
224 friend class Polar_Point;
225
228
229public:
234 Point() : Geom_Object(), x_(0), y_(0)
235 { /* empty */
236 }
237
243 Point(const Geom_Number &x, const Geom_Number &y) : Geom_Object(), x_(x), y_(y)
244 {
245 // empty
246 }
247
252 inline Point(const Polar_Point &pp);
253
259 [[nodiscard]] bool operator == (const Point &point) const noexcept
260 {
261 return x_ == point.x_ and y_ == point.y_;
262 }
263
269 [[nodiscard]] bool operator != (const Point &point) const noexcept
270 {
271 return not (*this == point);
272 }
273
279 [[nodiscard]] bool operator < (const Point &point) const noexcept
280 {
281 return x_ < point.x_ or (x_ == point.x_ and y_ < point.y_);
282 }
283
289 [[nodiscard]] Point operator + (const Point &p) const
290 {
291 return {x_ + p.x_, y_ + p.y_};
292 }
293
300 {
301 x_ += p.x_;
302 y_ += p.y_;
303
304 return *this;
305 }
306
312 Point operator - (const Point &p) const
313 {
314 return {x_ - p.x_, y_ - p.y_};
315 }
316
323 {
324 x_ -= p.x_;
325 y_ -= p.y_;
326
327 return *this;
328 }
329
335 {
336 return {-x_, -y_};
337 }
338
345 {
346 return {x_ * s, y_ * s};
347 }
348
355 {
356 return {x_ / s, y_ / s};
357 }
358
364 [[nodiscard]] Geom_Number dot(const Point &p) const
365 {
366 return x_ * p.x_ + y_ * p.y_;
367 }
368
374 [[nodiscard]] Geom_Number cross(const Point &p) const
375 {
376 return x_ * p.y_ - y_ * p.x_;
377 }
378
385 {
386 return x_ * x_ + y_ * y_;
387 }
388
394 {
395 return euclidean_distance(x_, y_);
396 }
397
404 {
405 const Geom_Number n = norm();
406 ah_domain_error_if(n == 0) << "Cannot normalize the zero vector";
407 return *this / n;
408 }
409
416 {
417 const Geom_Number c = cosinus(angle);
418 const Geom_Number s = sinus(angle);
419 return {x_ * c - y_ * s, x_ * s + y_ * c};
420 }
421
428 [[nodiscard]] Point lerp(const Point &other, const Geom_Number &t) const
429 {
430 const Geom_Number one_minus_t = Geom_Number(1) - t;
431 return *this * one_minus_t + other * t;
432 }
433
440 {
441 return lerp(other, Geom_Number(1, 2));
442 }
443
449 {
450 return x_;
451 }
452
458 {
459 return y_;
460 }
461
468 [[nodiscard]] bool is_colinear_with(const Point &p1, const Point &p2) const
469 {
470 return area_of_parallelogram(*this, p1, p2) == 0;
471 }
472
478 [[nodiscard]] inline bool is_colinear_with(const Segment &s) const;
479
486 [[nodiscard]] bool is_left_of(const Point &p1, const Point &p2) const
487 {
488 return area_of_parallelogram(p1, p2, *this) > 0;
489 }
490
492 [[deprecated("Use is_left_of() instead")]] [[nodiscard]] bool is_to_left_from(const Point &p1,
493 const Point &p2) const
494 {
495 return is_left_of(p1, p2);
496 }
497
504 [[nodiscard]] bool is_to_right_from(const Point &p1, const Point &p2) const
505 {
506 return area_of_parallelogram(p1, p2, *this) < 0;
507 }
508
515 [[nodiscard]] bool is_to_left_on_from(const Point &p1, const Point &p2) const
516 {
517 return not is_to_right_from(p1, p2);
518 }
519
526 [[nodiscard]] bool is_right_on_of(const Point &p1, const Point &p2) const
527 {
528 return not is_left_of(p1, p2);
529 }
530
532 [[deprecated("Use is_right_on_of() instead")]] [[nodiscard]] bool is_to_right_on_from(
533 const Point &p1, const Point &p2) const
534 {
535 return is_right_on_of(p1, p2);
536 }
537
544 [[nodiscard]] bool is_clockwise_with(const Point &p1, const Point &p2) const
545 {
546 return area_of_parallelogram(*this, p1, p2) < 0;
547 }
548
554 [[nodiscard]] inline bool is_left_of(const Segment &s) const;
555
561 [[nodiscard]] inline bool is_right_of(const Segment &s) const;
562
564 [[deprecated("Use is_left_of() instead")]] [[nodiscard]] bool is_to_left_from(const Segment &s) const
565 {
566 return is_left_of(s);
567 }
568
570 [[deprecated("Use is_right_of() instead")]] [[nodiscard]] bool is_to_right_from(const Segment &s) const
571 {
572 return is_right_of(s);
573 }
574
580 [[nodiscard]] inline bool is_clockwise_with(const Segment &s) const;
581
588 [[nodiscard]] bool is_between(const Point &p1, const Point &p2) const
589 {
590 if (not this->is_colinear_with(p1, p2))
591 return false;
592
593 if (p1.get_x() == p2.get_x())
594 return (p1.get_y() <= this->get_y() and this->get_y() <= p2.get_y()) or
595 (p1.get_y() >= this->get_y() and (this->get_y() >= p2.get_y()));
596
597 return (p1.get_x() <= this->get_x() and (this->get_x() <= p2.get_x())) or
598 (p1.get_x() >= this->get_x() and (this->get_x() >= p2.get_x()));
599 }
600
607 [[nodiscard]] const Point &nearest_point(const Point &p1, const Point &p2) const
608 {
609 return this->distance_squared_to(p1) < this->distance_squared_to(p2) ? p1 : p2;
610 }
611
618 [[nodiscard]] inline bool is_inside(const Segment &s) const;
619
626 [[nodiscard]] inline bool is_inside(const Ellipse &e) const;
627
634 [[nodiscard]] inline bool intersects_with(const Ellipse &e) const;
635
640 [[nodiscard]] std::string to_string() const
641 {
642 return "(" + std::to_string(geom_number_to_double(x_)) + "," +
643 std::to_string(geom_number_to_double(y_)) + ")";
644 }
645
650 operator std::string() const
651 {
652 return to_string();
653 }
654
660 [[nodiscard]] inline Geom_Number distance_squared_to(const Point &that) const;
661
667 [[nodiscard]] inline Geom_Number distance_to(const Point &p) const;
668
670 [[deprecated("Use distance_to() instead")]] [[nodiscard]] Geom_Number distance_with(const Point &p) const
671 {
672 return distance_to(p);
673 }
674
679 [[nodiscard]] const Point &highest_point() const
680 {
681 return *this;
682 }
683
688 [[nodiscard]] const Point &lowest_point() const
689 {
690 return *this;
691 }
692
697 [[nodiscard]] const Point &leftmost_point() const
698 {
699 return *this;
700 }
701
706 [[nodiscard]] const Point &rightmost_point() const
707 {
708 return *this;
709 }
710};
711
713[[nodiscard]] inline Point operator * (const Geom_Number &s, const Point &p)
714{
715 return p * s;
716}
717
728{
729 friend class Point;
730
731 Geom_Number r_ = 0; // distance from origin
732 Geom_Number theta_ = 0; // angle in radians from positive x-axis
733
734public:
739 [[nodiscard]] const Geom_Number &get_r() const
740 {
741 return r_;
742 }
743
748 [[nodiscard]] const Geom_Number &get_theta() const
749 {
750 return theta_;
751 }
752
759 {
760 // empty
761 }
762
767 explicit Polar_Point(const Point &p)
768 {
769 const Geom_Number &x = p.get_x();
770 const Geom_Number &y = p.get_y();
771
772 r_ = euclidean_distance(x, y);
773 theta_ = arctan2(y, x);
774 }
775
784
791 {
792 const Point cartesian(*this);
793 const bool east = cartesian.get_x() >= 0;
794 const bool north = cartesian.get_y() >= 0;
795
796 if (east and north)
797 return First;
798 if (not east and north)
799 return Second;
800 if (not east and not north)
801 return Third;
802 return Fourth;
803 }
804
809 [[nodiscard]] std::string to_string() const
810 {
811 return "[" + std::to_string(geom_number_to_double(r_)) + "," +
812 std::to_string(geom_number_to_double(theta_)) + "]";
813 }
814
819 Polar_Point() = default;
820};
821
823 : x_(pp.r_ * cosinus(pp.theta_)), y_(pp.r_ * sinus(pp.theta_))
824{
825 // empty
826}
827
836class Segment : public Geom_Object
837{
838 friend class Point;
839 friend class Triangle;
840
842
847 [[nodiscard]] double compute_slope() const
848 {
849 if (tgt_.get_x() == src_.get_x())
850 {
851 if (src_.get_y() < tgt_.get_y())
852 return std::numeric_limits<double>::max();
853 return -std::numeric_limits<double>::max();
854 }
855
856 const Geom_Number slope_ = (tgt_.get_y() - src_.get_y()) / (tgt_.get_x() - src_.get_x());
857
858 return slope_.get_d();
859 }
860
861public:
869 [[nodiscard]] bool operator == (const Segment &s) const noexcept
870 {
871 return (src_ == s.src_ and tgt_ == s.tgt_) or (src_ == s.tgt_ and tgt_ == s.src_);
872 }
873
879 [[nodiscard]] bool operator != (const Segment &s) const noexcept
880 {
881 return not (*this == s);
882 }
883
889 {
890 return src_.get_y() > tgt_.get_y() ? src_ : tgt_;
891 }
892
898 {
899 return src_.get_y() < tgt_.get_y() ? src_ : tgt_;
900 }
901
907 {
908 return src_.get_x() < tgt_.get_x() ? src_ : tgt_;
909 }
910
916 {
917 return src_.get_x() > tgt_.get_x() ? src_ : tgt_;
918 }
919
925 {
926 return src_;
927 }
928
934 {
935 return tgt_;
936 }
937
941 Segment() = default;
942
948 Segment(Point src, Point tgt) : Geom_Object(), src_(std::move(src)), tgt_(std::move(tgt))
949 {
950 // empty
951 }
952
953private:
961 static Point compute_tgt_point(const Point &src, // Point of origin
962 const Geom_Number &m, // slope
963 const Geom_Number &d) // segment length
964 {
965 const Geom_Number den2 = 1 + m * m;
966
968
969 const Geom_Number x = src.get_x() + d / den;
970
971 const Geom_Number y = src.get_y() + d * m / den;
972
973 return {x, y};
974 }
975
976public:
985 Segment(Point src, const Geom_Number &m, const Geom_Number &l)
986 : Geom_Object(), src_(std::move(src)), tgt_(compute_tgt_point(src_, m, l))
987 {
988 // empty
989 }
990
996 Segment(const Segment &sg, const Geom_Number &dist)
997 {
998 const Segment perp = sg.mid_perpendicular(dist);
999
1000 const Point mid_point = sg.mid_point();
1001
1002 const Point diff_point = mid_point - perp.get_src_point();
1003
1004 src_ = sg.get_src_point() + diff_point;
1005 tgt_ = sg.get_tgt_point() + diff_point;
1006 }
1007
1012 [[nodiscard]] double slope() const
1013 {
1014 return compute_slope();
1015 }
1016
1023 {
1024 ah_domain_error_if(tgt_.get_x() == src_.get_x()) << "Vertical segment has undefined slope";
1025 return (tgt_.get_y() - src_.get_y()) / (tgt_.get_x() - src_.get_x());
1026 }
1027
1039 {
1040 const Geom_Number x1 = tgt_.get_x() - src_.get_x();
1041 const Geom_Number x2 = s.tgt_.get_x() - s.src_.get_x();
1042 const Geom_Number y1 = tgt_.get_y() - src_.get_y();
1043 const Geom_Number y2 = s.tgt_.get_y() - s.src_.get_y();
1044 const Geom_Number dot = x1 * x2 + y1 * y2;
1045 const Geom_Number det = x1 * y2 - y1 * x2;
1046
1047 const Geom_Number angle = arctan2(det, dot);
1048
1049 if (angle < 0)
1050 return geom_number_to_double(angle + 2 * geom_pi());
1051
1052 return angle.get_d();
1053 }
1054
1060 {
1061 const Segment x_axis(Point(0, 0), Point(1, 0));
1062 return x_axis.counterclockwise_angle_with(*this);
1063 }
1064
1070 {
1071 return euclidean_distance(tgt_.get_x() - src_.get_x(), tgt_.get_y() - src_.get_y());
1072 }
1073
1075 [[deprecated("Use length() instead")]] [[nodiscard]] Geom_Number size() const
1076 {
1077 return length();
1078 }
1079
1085 [[nodiscard]] bool is_colinear_with(const Point &p) const
1086 {
1087 return p.is_colinear_with(src_, tgt_);
1088 }
1089
1096 [[nodiscard]] bool is_left_of(const Point &p) const
1097 {
1098 return p.is_right_of(*this);
1099 }
1100
1107 [[nodiscard]] bool is_right_of(const Point &p) const
1108 {
1109 return p.is_left_of(*this);
1110 }
1111
1113 [[deprecated("Use is_left_of() instead")]] [[nodiscard]] bool is_to_left_from(const Point &p) const
1114 {
1115 return is_left_of(p);
1116 }
1117
1119 [[deprecated("Use is_right_of() instead")]] [[nodiscard]] bool is_to_right_from(const Point &p) const
1120 {
1121 return is_right_of(p);
1122 }
1123
1129 {
1130 const Geom_Number x = (src_.get_x() + tgt_.get_x()) / 2;
1131 const Geom_Number y = (src_.get_y() + tgt_.get_y()) / 2;
1132
1133 return {x, y};
1134 }
1135
1141 {
1142 return {tgt_, src_};
1143 }
1144
1150 [[nodiscard]] Point at(const Geom_Number &t) const
1151 {
1152 return src_.lerp(tgt_, t);
1153 }
1154
1161 [[nodiscard]] Point project(const Point &p) const
1162 {
1163 const Point dir = tgt_ - src_;
1164 const Geom_Number len2 = dir.dot(dir);
1165
1166 if (len2 == 0)
1167 return src_;
1168
1169 Geom_Number t = (p - src_).dot(dir) / len2;
1170 if (t < 0)
1171 t = 0;
1172 else if (t > 1)
1173 t = 1;
1174
1175 return at(t);
1176 }
1177
1184 {
1185 return p.distance_to(project(p));
1186 }
1187
1193 [[nodiscard]] const Point &nearest_point(const Point &p) const
1194 {
1196 }
1197
1204 {
1205 if (src_ == tgt_)
1206 return *this;
1207
1208 const Point dir = tgt_ - src_;
1209 const Geom_Number len2 = dir.dot(dir);
1210 ah_domain_error_if(len2 == 0) << "Cannot get perpendicular of zero-length segment";
1211
1212 const Geom_Number t = (p - src_).dot(dir) / len2;
1213 const Point foot = src_.lerp(tgt_, t);
1214
1215 // The perpendicular segment goes from the foot to p.
1216 return {foot, p};
1217 }
1218
1226 {
1227 const Point mid = mid_point();
1228 const Point dir = (tgt_ - src_).normalize();
1229 const Point perp_dir(-dir.get_y(), dir.get_x()); // Rotated by +90 degrees
1230
1231 return {mid - perp_dir * dist, mid + perp_dir * dist};
1232 }
1233
1242 {
1243 // Each pair of endpoints must lie strictly on opposite sides of the
1244 // other segment's supporting line. Using the product of signed areas
1245 // guarantees that shared endpoints (area == 0) are never treated as a
1246 // proper crossing.
1247 const auto d1 = area_of_parallelogram(s.src_, s.tgt_, src_);
1248 const auto d2 = area_of_parallelogram(s.src_, s.tgt_, tgt_);
1249 const auto d3 = area_of_parallelogram(src_, tgt_, s.src_);
1250 const auto d4 = area_of_parallelogram(src_, tgt_, s.tgt_);
1251 return (d1 * d2 < 0) and (d3 * d4 < 0);
1252 }
1253
1259 [[nodiscard]] bool contains(const Point &p) const
1260 {
1261 return p.is_between(src_, tgt_);
1262 }
1263
1265 [[deprecated("Use contains() instead")]] [[nodiscard]] bool contains_to(const Point &p) const
1266 {
1267 return contains(p);
1268 }
1269
1275 [[nodiscard]] bool contains(const Segment &s) const
1276 {
1278 }
1279
1281 [[deprecated("Use contains() instead")]] [[nodiscard]] bool contains_to(const Segment &s) const
1282 {
1283 return contains(s);
1284 }
1285
1291 [[nodiscard]] bool intersects_with(const Segment &s) const
1292 {
1293 if (this->intersects_properly_with(s))
1294 return true;
1295
1296 return this->contains(s.src_) or this->contains(s.tgt_) or s.contains(this->src_) or
1297 s.contains(this->tgt_);
1298 }
1299
1307 [[nodiscard]] inline bool intersects_with(const Triangle &t) const;
1308
1316 [[nodiscard]] inline bool intersects_with(const Ellipse &e) const;
1317
1323 [[nodiscard]] bool is_parallel_with(const Segment &s) const
1324 {
1325 return (tgt_ - src_).cross(s.tgt_ - s.src_) == 0;
1326 }
1327
1335 {
1336 const Point v1 = tgt_ - src_;
1337 const Point v2 = s.tgt_ - s.src_;
1338 const Geom_Number cross = v1.cross(v2);
1339
1340 ah_domain_error_if(cross == 0) << "Segments are parallel";
1341
1342 const Geom_Number t = (s.src_ - src_).cross(v2) / cross;
1343
1344 return src_ + v1 * t;
1345 }
1346
1359
1365 {
1366 const Point dir = tgt_ - src_;
1367 if (dir.get_x() > 0) // headed east?
1368 {
1369 if (dir.get_y() > 0) // and north?
1370 return NE;
1371 if (dir.get_y() < 0) // and south?
1372 return SE;
1373 return E;
1374 }
1375 if (dir.get_x() < 0) // headed west?
1376 {
1377 if (dir.get_y() > 0) // and north?
1378 return NW;
1379 if (dir.get_y() < 0) // and south?
1380 return SW;
1381 return W;
1382 }
1383
1384 // Vertical segment
1385 return dir.get_y() > 0 ? N : S;
1386 }
1387
1392 void enlarge_src(const Geom_Number &dist)
1393 {
1394 if (src_ == tgt_)
1395 return;
1396 src_ -= (tgt_ - src_).normalize() * dist;
1397 }
1398
1403 void enlarge_tgt(const Geom_Number &dist)
1404 {
1405 if (src_ == tgt_)
1406 return;
1407 tgt_ += (tgt_ - src_).normalize() * dist;
1408 }
1409
1414 [[nodiscard]] std::string to_string() const
1415 {
1416 return src_.to_string() + tgt_.to_string();
1417 }
1418
1422 operator std::string() const
1423 {
1424 return to_string();
1425 }
1426
1432 {
1433 if (angle == 0)
1434 return;
1435 tgt_ = src_ + (tgt_ - src_).rotate(angle);
1436 }
1437
1446 [[nodiscard]] inline Segment intersection_with(const Triangle &t) const;
1447
1456 [[nodiscard]] inline Segment intersection_with(const Ellipse &e) const;
1457};
1458
1459// Return true if this point lies inside segment s.
1460inline bool Point::is_inside(const Segment &s) const
1461{
1462 return s.contains(*this);
1463}
1464
1465// Return true if this point is colinear with segment s.
1466inline bool Point::is_colinear_with(const Segment &s) const
1467{
1468 return this->is_colinear_with(s.src_, s.tgt_);
1469}
1470
1471// Return true if this point is to the left of the segment s.
1472inline bool Point::is_left_of(const Segment &s) const
1473{
1474 return this->is_left_of(s.src_, s.tgt_);
1475}
1476
1477// Return true if this point is to the right of the segment s.
1478inline bool Point::is_right_of(const Segment &s) const
1479{
1480 return area_of_parallelogram(s.src_, s.tgt_, *this) < 0;
1481}
1482
1483// Return true if the sequence (this, segment) is clockwise.
1484inline bool Point::is_clockwise_with(const Segment &s) const
1485{
1486 return this->is_clockwise_with(s.src_, s.tgt_);
1487}
1488
1489// Return the squared Euclidean distance to that.
1491{
1492 const Geom_Number dx = this->x_ - that.x_;
1493 const Geom_Number dy = this->y_ - that.y_;
1494 return dx * dx + dy * dy;
1495}
1496
1497// Return the Euclidean distance to p.
1499{
1500 return euclidean_distance(p.x_ - x_, p.y_ - y_);
1501}
1502
1511class Triangle : public Geom_Object
1512{
1513 friend class Point;
1514 friend class Segment;
1515
1517
1518 Geom_Number area_; // signed area * 2
1519
1520public:
1529 : p1_(std::move(p1)), p2_(std::move(p2)), p3_(std::move(p3))
1530 {
1531 // Note: area_ is twice the signed area
1533 ah_domain_error_if(area_ == 0) << "The three points of a triangle cannot be collinear";
1534 }
1535
1542 Triangle(Point p, const Segment &s) : p1_(std::move(p)), p2_(s.src_), p3_(s.tgt_)
1543 {
1545 ah_domain_error_if(area_ == 0) << "The three points of a triangle cannot be collinear";
1546 }
1547
1554 Triangle(const Segment &s, Point p) : p1_(s.src_), p2_(s.tgt_), p3_(std::move(p))
1555 {
1557 ah_domain_error_if(area_ == 0) << "The three points of a triangle cannot be collinear";
1558 }
1559
1565 {
1566 return abs(area_) / 2;
1567 }
1568
1573 [[nodiscard]] bool is_clockwise() const
1574 {
1575 return area_ < 0;
1576 }
1577
1582 [[nodiscard]] const Point &highest_point() const
1583 {
1584 const Point &max = p1_.get_y() > p2_.get_y() ? p1_ : p2_;
1585 return p3_.get_y() > max.get_y() ? p3_ : max;
1586 }
1587
1592 [[nodiscard]] const Point &lowest_point() const
1593 {
1594 const Point &min = p1_.get_y() < p2_.get_y() ? p1_ : p2_;
1595 return p3_.get_y() < min.get_y() ? p3_ : min;
1596 }
1597
1602 [[nodiscard]] const Point &leftmost_point() const
1603 {
1604 const Point &min = p1_.get_x() < p2_.get_x() ? p1_ : p2_;
1605 return p3_.get_x() < min.get_x() ? p3_ : min;
1606 }
1607
1613 {
1614 const Point &max = p1_.get_x() > p2_.get_x() ? p1_ : p2_;
1615 return p3_.get_x() > max.get_x() ? p3_ : max;
1616 }
1617
1619 [[nodiscard]] const Point &get_p1() const
1620 {
1621 return p1_;
1622 }
1624 [[nodiscard]] const Point &get_p2() const
1625 {
1626 return p2_;
1627 }
1629 [[nodiscard]] const Point &get_p3() const
1630 {
1631 return p3_;
1632 }
1633
1641 [[nodiscard]] bool operator == (const Triangle &t) const noexcept
1642 {
1643 const bool p1_in = p1_ == t.p1_ or p1_ == t.p2_ or p1_ == t.p3_;
1644 const bool p2_in = p2_ == t.p1_ or p2_ == t.p2_ or p2_ == t.p3_;
1645 const bool p3_in = p3_ == t.p1_ or p3_ == t.p2_ or p3_ == t.p3_;
1646 return p1_in and p2_in and p3_in;
1647 }
1648
1654 [[nodiscard]] bool operator != (const Triangle &t) const noexcept
1655 {
1656 return not (*this == t);
1657 }
1658
1664 {
1665 return (p1_ + p2_ + p3_) / Geom_Number(3);
1666 }
1667
1673 {
1675 }
1676
1685 {
1686 const Geom_Number &x1 = p1_.get_x();
1687 const Geom_Number &y1 = p1_.get_y();
1688 const Geom_Number &x2 = p2_.get_x();
1689 const Geom_Number &y2 = p2_.get_y();
1690 const Geom_Number &x3 = p3_.get_x();
1691 const Geom_Number &y3 = p3_.get_y();
1692
1693 const Geom_Number d = Geom_Number(2) * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
1694 ah_domain_error_if(d == 0) << "Cannot compute circumcenter of a degenerate triangle";
1695
1696 const Geom_Number x1sq_y1sq = x1 * x1 + y1 * y1;
1697 const Geom_Number x2sq_y2sq = x2 * x2 + y2 * y2;
1698 const Geom_Number x3sq_y3sq = x3 * x3 + y3 * y3;
1699
1700 const Geom_Number ux =
1701 (x1sq_y1sq * (y2 - y3) + x2sq_y2sq * (y3 - y1) + x3sq_y3sq * (y1 - y2)) / d;
1702
1703 const Geom_Number uy =
1704 (x1sq_y1sq * (x3 - x2) + x2sq_y2sq * (x1 - x3) + x3sq_y3sq * (x2 - x1)) / d;
1705
1706 return {ux, uy};
1707 }
1708
1717 {
1718 const Geom_Number a = p2_.distance_to(p3_);
1719 const Geom_Number b = p1_.distance_to(p3_);
1720 const Geom_Number c = p1_.distance_to(p2_);
1721 const Geom_Number sum = a + b + c;
1722 ah_domain_error_if(sum == 0) << "Cannot compute incenter of a degenerate triangle";
1723
1724 return (p1_ * a + p2_ * b + p3_ * c) / sum;
1725 }
1726
1731 [[nodiscard]] std::array<Segment, 3> edges() const
1732 {
1733 return {Segment(p1_, p2_), Segment(p2_, p3_), Segment(p3_, p1_)};
1734 }
1735
1744 [[nodiscard]] bool contains(const Point &p) const
1745 {
1746 if (is_clockwise())
1749 return p.is_left_of(p1_, p2_) and p.is_left_of(p2_, p3_) and p.is_left_of(p3_, p1_);
1750 }
1751
1759 {
1760 return s.intersection_with(*this);
1761 }
1762
1770 [[nodiscard]] bool covers(const Point &p) const
1771 {
1772 if (contains(p))
1773 return true;
1774 for (const Segment &edge : edges())
1775 if (edge.contains(p))
1776 return true;
1777 return false;
1778 }
1779};
1780
1789{
1792
1793public:
1799 [[nodiscard]] bool operator == (const Rectangle &r) const noexcept
1800 {
1801 return xmin_ == r.xmin_ and ymin_ == r.ymin_ and xmax_ == r.xmax_ and ymax_ == r.ymax_;
1802 }
1803
1809 [[nodiscard]] bool operator != (const Rectangle &r) const noexcept
1810 {
1811 return not (*this == r);
1812 }
1813
1815 [[nodiscard]] const Geom_Number &get_xmin() const
1816 {
1817 return xmin_;
1818 }
1820 [[nodiscard]] const Geom_Number &get_ymin() const
1821 {
1822 return ymin_;
1823 }
1825 [[nodiscard]] const Geom_Number &get_xmax() const
1826 {
1827 return xmax_;
1828 }
1830 [[nodiscard]] const Geom_Number &get_ymax() const
1831 {
1832 return ymax_;
1833 }
1834
1838 Rectangle() : xmin_(0), ymin_(0), xmax_(0), ymax_(0)
1839 {
1840 // empty
1841 }
1842
1851 Rectangle(const Geom_Number &xmin, const Geom_Number &ymin, const Geom_Number &xmax,
1852 const Geom_Number &ymax)
1853 : xmin_(xmin), ymin_(ymin), xmax_(xmax), ymax_(ymax)
1854 {
1855 ah_range_error_if(xmax_ < xmin_ or ymax_ < ymin_) << "Invalid rectangle";
1856 }
1857
1866 void set_rect(const Geom_Number &xmin, const Geom_Number &ymin, const Geom_Number &xmax,
1867 const Geom_Number &ymax)
1868 {
1869 ah_range_error_if(xmax < xmin or ymax < ymin) << "Invalid rectangle";
1870
1871 xmin_ = xmin;
1872 ymin_ = ymin;
1873 xmax_ = xmax;
1874 ymax_ = ymax;
1875 }
1876
1879 {
1880 return xmax_ - xmin_;
1881 }
1884 {
1885 return ymax_ - ymin_;
1886 }
1889 {
1890 return width() * height();
1891 }
1892
1898 {
1899 return 2 * (width() + height());
1900 }
1901
1907 {
1908 return {(xmin_ + xmax_) / 2, (ymin_ + ymax_) / 2};
1909 }
1910
1916 [[nodiscard]] std::array<Point, 4> corners() const noexcept
1917 {
1919 }
1920
1926 [[nodiscard]] bool intersects(const Rectangle &that) const noexcept
1927 {
1928 return this->xmax_ >= that.xmin_ and this->ymax_ >= that.ymin_ and that.xmax_ >= this->xmin_ and
1929 that.ymax_ >= this->ymin_;
1930 }
1931
1938 [[nodiscard]] Geom_Number distance_squared_to(const Point &p) const noexcept
1939 {
1940 Geom_Number dx = 0.0, dy = 0.0;
1941 if (p.get_x() < xmin_)
1942 dx = p.get_x() - xmin_;
1943 else if (p.get_x() > xmax_)
1944 dx = p.get_x() - xmax_;
1945
1946 if (p.get_y() < ymin_)
1947 dy = p.get_y() - ymin_;
1948 else if (p.get_y() > ymax_)
1949 dy = p.get_y() - ymax_;
1950
1951 return dx * dx + dy * dy;
1952 }
1953
1960 {
1962 }
1963
1969 [[nodiscard]] bool contains(const Point &p) const noexcept
1970 {
1971 return p.get_x() >= xmin_ and p.get_x() <= xmax_ and p.get_y() >= ymin_ and p.get_y() <= ymax_;
1972 }
1973
1978 [[nodiscard]] std::string to_string() const
1979 {
1980 return "(" + std::to_string(geom_number_to_double(xmin_)) + "," +
1981 std::to_string(geom_number_to_double(ymin_)) + ")-(" +
1982 std::to_string(geom_number_to_double(xmax_)) + "," +
1983 std::to_string(geom_number_to_double(ymax_)) + ")";
1984 }
1985};
1986
1987// Return true if this segment overlaps the closed filled triangle @p t.
1988inline bool Segment::intersects_with(const Triangle &t) const
1989{
1990 if (t.covers(src_) or t.covers(tgt_))
1991 return true;
1992
1993 for (const Segment &edge : t.edges())
1994 if (this->intersects_with(edge))
1995 return true;
1996 return false;
1997}
1998
1999// Return the portion of this segment covered by the closed filled triangle.
2001{
2002 ah_domain_error_if(not this->intersects_with(t)) << "Segment does not intersect the triangle";
2003
2004 std::array<Point, 6> points;
2005 size_t count = 0;
2006
2007 auto append_unique = [&points, &count](const Point &pt)
2008 {
2009 for (size_t i = 0; i < count; ++i)
2010 if (points[i] == pt)
2011 return;
2012 points[count++] = pt;
2013 };
2014
2015 auto collect_with_edge = [this, &append_unique](const Segment &edge)
2016 {
2017 if (not this->intersects_with(edge))
2018 return;
2019
2020 if (this->is_parallel_with(edge))
2021 {
2022 if (this->contains(edge.get_src_point()))
2023 append_unique(edge.get_src_point());
2024 if (this->contains(edge.get_tgt_point()))
2025 append_unique(edge.get_tgt_point());
2026 if (edge.contains(this->get_src_point()))
2028 if (edge.contains(this->get_tgt_point()))
2030 return;
2031 }
2032
2033 append_unique(this->intersection_with(edge));
2034 };
2035
2036 const auto edges = t.edges();
2037 if (t.covers(src_))
2039 if (t.covers(tgt_))
2041 for (const Segment &edge : edges)
2042 collect_with_edge(edge);
2043
2044 ah_domain_error_if(count == 0) << "no intersection point found despite intersection confirmed";
2045
2046 if (count == 1)
2047 return {points[0], points[0]};
2048
2049 // Keep the farthest pair in case collinear overlaps produced >2 points.
2050 size_t i_best = 0;
2051 size_t j_best = 1;
2052 Geom_Number best_d2 = points[0].distance_squared_to(points[1]);
2053 for (size_t i = 0; i < count; ++i)
2054 for (size_t j = i + 1; j < count; ++j)
2055 if (const Geom_Number d2 = points[i].distance_squared_to(points[j]); d2 > best_d2)
2056 {
2057 best_d2 = d2;
2058 i_best = i;
2059 j_best = j;
2060 }
2061
2063 return {points[i_best], points[j_best]};
2064 return {points[j_best], points[i_best]};
2065}
2066
2075class Ellipse : public Geom_Object
2076{
2077 friend class Point;
2078
2079 /*
2080 The ellipse is defined relative to its center (xc, yc) by:
2081
2082 2 2
2083 (y - yc) (x - xc)
2084 --------- + --------- = 1
2085 2 2
2086 vr hr
2087 */
2088
2089 Point center_; // ellipse center
2090
2091 Geom_Number hr_; // horizontal radius (parameter a)
2092 Geom_Number vr_; // vertical radius (parameter b)
2093
2094public:
2102 Ellipse(Point center, const Geom_Number &hr, const Geom_Number &vr)
2103 : center_(std::move(center)), hr_(hr), vr_(vr)
2104 {
2106 }
2107
2111 Ellipse(const Ellipse &e) = default;
2112
2117 Ellipse() : center_(0, 0), hr_(1), vr_(1)
2118 {
2119 // empty
2120 }
2121
2127 [[nodiscard]] bool operator == (const Ellipse &e) const noexcept
2128 {
2129 return center_ == e.center_ and hr_ == e.hr_ and vr_ == e.vr_;
2130 }
2131
2137 [[nodiscard]] bool operator != (const Ellipse &e) const noexcept
2138 {
2139 return not (*this == e);
2140 }
2141
2143 [[nodiscard]] const Point &get_center() const
2144 {
2145 return center_;
2146 }
2149 {
2150 return hr_;
2151 }
2154 {
2155 return vr_;
2156 }
2157
2163 {
2165 return geom_pi() * hr_ * vr_;
2166 }
2167
2174 {
2176 const Geom_Number ab_sum = hr_ + vr_;
2177 const Geom_Number h = (hr_ - vr_) * (hr_ - vr_) / (ab_sum * ab_sum);
2179 Geom_Number(3) * h / (Geom_Number(10) + square_root(Geom_Number(4) - Geom_Number(3) * h));
2180 return geom_pi() * ab_sum * correction;
2181 }
2182
2189 {
2191 return {center_.get_x() + hr_ * cosinus(angle), center_.get_y() + vr_ * sinus(angle)};
2192 }
2193
2198 [[nodiscard]] std::string to_string() const
2199 {
2200 return "Ellipse(center=" + center_.to_string() +
2201 ", hr=" + std::to_string(geom_number_to_double(hr_)) +
2202 ", vr=" + std::to_string(geom_number_to_double(vr_)) + ")";
2203 }
2204
2205private:
2211 {
2212 ah_domain_error_if(hr_ <= 0 or vr_ <= 0) << "Ellipse radii must be > 0";
2213 }
2214
2218 [[nodiscard]] static bool in_unit_interval(const Geom_Number &t)
2219 {
2220 return t >= 0 and t <= 1;
2221 }
2222
2223public:
2228 static bool is_clockwise()
2229 {
2230 return false;
2231 }
2232
2235 {
2236 return {center_.get_x(), center_.get_y() + vr_};
2237 }
2238
2241 {
2242 return {center_.get_x(), center_.get_y() - vr_};
2243 }
2244
2247 {
2248 return {center_.get_x() - hr_, center_.get_y()};
2249 }
2250
2253 {
2254 return {center_.get_x() + hr_, center_.get_y()};
2255 }
2256
2264 {
2266
2267 const Geom_Number m2 = m * m;
2268 const Geom_Number hr2 = hr_ * hr_;
2269 const Geom_Number vr2 = vr_ * vr_;
2270 const Geom_Number y_intercept_sq = hr2 * m2 + vr2;
2271 ah_domain_error_if(y_intercept_sq < 0) << "No tangent for this slope";
2272
2273 const Geom_Number y_intercept = square_root(y_intercept_sq);
2274
2275 // Tangent points on origin-centered ellipse
2276 const Point t1_local(-hr2 * m / y_intercept, vr2 / y_intercept);
2277 const Point t2_local(hr2 * m / y_intercept, -vr2 / y_intercept);
2278
2279 // Translate to ellipse's actual center
2282
2283 // Create segments of arbitrary length through the tangent points
2284 const Geom_Number tangent_len = hr_ > vr_ ? hr_ : vr_;
2285 s1 = Segment(tangent_point1 - (Point(1, m).normalize() * tangent_len),
2286 tangent_point1 + (Point(1, m).normalize() * tangent_len));
2287 s2 = Segment(tangent_point2 - (Point(1, m).normalize() * tangent_len),
2288 tangent_point2 + (Point(1, m).normalize() * tangent_len));
2289 }
2290
2298 [[nodiscard]] bool intersects_with(const Segment &s) const
2299 {
2301
2302 const Point p0 = s.get_src_point() - center_;
2303 const Point p1 = s.get_tgt_point() - center_;
2304 const Geom_Number dx = p1.get_x() - p0.get_x();
2305 const Geom_Number dy = p1.get_y() - p0.get_y();
2306 const Geom_Number a2 = hr_ * hr_;
2307 const Geom_Number b2 = vr_ * vr_;
2308
2309 const Geom_Number A = (dx * dx) / a2 + (dy * dy) / b2;
2310 const Geom_Number B = Geom_Number(2) * (p0.get_x() * dx / a2 + p0.get_y() * dy / b2);
2311 const Geom_Number C =
2312 (p0.get_x() * p0.get_x()) / a2 + (p0.get_y() * p0.get_y()) / b2 - Geom_Number(1);
2313
2315 (p1.get_x() * p1.get_x()) / a2 + (p1.get_y() * p1.get_y()) / b2 - Geom_Number(1);
2316
2317 if (C <= 0 or target_value <= 0)
2318 return true;
2319
2320 // For a segment, A can be 0 if it's a point.
2321 if (A == 0)
2322 return false;
2323
2324 const Geom_Number discriminant = B * B - Geom_Number(4) * A * C;
2325 if (discriminant < 0)
2326 return false; // No real roots, no intersection
2327
2328 // Check if intersection points lie on the segment (t in [0,1])
2330 const Geom_Number denom = Geom_Number(2) * A;
2331 const Geom_Number t1 = (-B - root) / denom;
2332 const Geom_Number t2 = (-B + root) / denom;
2334 }
2335
2336private:
2345 {
2347
2348 Geom_Number x2 = (p.get_x() - center_.get_x());
2349 x2 = x2 * x2;
2350
2351 Geom_Number y2 = (p.get_y() - center_.get_y());
2352 y2 = y2 * y2;
2353
2354 return x2 / (hr_ * hr_) + y2 / (vr_ * vr_);
2355 }
2356
2357public:
2363 [[nodiscard]] bool contains(const Point &p) const
2364 {
2365 return compute_radius(p) <= 1;
2366 }
2367
2369 [[deprecated("Use contains() instead")]] [[nodiscard]] bool contains_to(const Point &p) const
2370 {
2371 return contains(p);
2372 }
2373
2379 [[nodiscard]] bool intersects_with(const Point &p) const
2380 {
2381 return compute_radius(p) == 1;
2382 }
2383
2392 {
2394
2395 const Point p0 = sg.get_src_point() - center_;
2396 const Point p1 = sg.get_tgt_point() - center_;
2397 const Geom_Number dx = p1.get_x() - p0.get_x();
2398 const Geom_Number dy = p1.get_y() - p0.get_y();
2399 const Geom_Number a2 = hr_ * hr_;
2400 const Geom_Number b2 = vr_ * vr_;
2401
2402 const Geom_Number A = dx * dx / a2 + dy * dy / b2;
2403 const Geom_Number B = Geom_Number(2) * (p0.get_x() * dx / a2 + p0.get_y() * dy / b2);
2404 const Geom_Number C =
2405 p0.get_x() * p0.get_x() / a2 + p0.get_y() * p0.get_y() / b2 - Geom_Number(1);
2406
2407 if (A == 0)
2408 {
2409 ah_domain_error_if(C > 0) << "No intersection (point outside)";
2410 return {sg.get_src_point(), sg.get_src_point()};
2411 }
2412
2413 const Geom_Number discriminant = B * B - Geom_Number(4) * A * C;
2414 ah_domain_error_if(discriminant < 0) << "No intersection (no real roots)";
2415
2417 const Geom_Number denom = Geom_Number(2) * A;
2418 const Geom_Number t1 = (-B - root) / denom;
2419 const Geom_Number t2 = (-B + root) / denom;
2420
2421 const Geom_Number lo = t1 < 0 ? Geom_Number(0) : t1;
2422 const Geom_Number hi = t2 > 1 ? Geom_Number(1) : t2;
2423 ah_domain_error_if(hi < lo) << "Ellipse and segment do not share any point";
2424
2425 return {sg.at(lo), sg.at(hi)};
2426 }
2427};
2428
2429// Return true if this point lies inside ellipse e.
2430inline bool Point::is_inside(const Ellipse &e) const
2431{
2432 return e.contains(*this);
2433}
2434
2435// Return true if this point lies exactly on ellipse e.
2436inline bool Point::intersects_with(const Ellipse &e) const
2437{
2438 return e.intersects_with(*this);
2439}
2440
2441// Return true if this segment intersects ellipse e.
2442inline bool Segment::intersects_with(const Ellipse &e) const
2443{
2444 return e.intersects_with(*this);
2445}
2446
2447// Return the portion of this segment covered by the closed filled ellipse.
2449{
2450 return e.intersection_with(*this);
2451}
2452
2453// ============================================================================
2454// Rotated Ellipse
2455// ============================================================================
2456
2473{
2475 Geom_Number a_; // semi-axis along the local x (before rotation)
2476 Geom_Number b_; // semi-axis along the local y (before rotation)
2477 Geom_Number cos_th_; // cosine of the rotation angle
2478 Geom_Number sin_th_; // sine of the rotation angle
2479
2485 [[nodiscard]] Point to_local(const Point &p) const
2486 {
2487 const Geom_Number dx = p.get_x() - center_.get_x();
2488 const Geom_Number dy = p.get_y() - center_.get_y();
2489 return {cos_th_ * dx + sin_th_ * dy, -sin_th_ * dx + cos_th_ * dy};
2490 }
2491
2497 [[nodiscard]] Point to_world(const Point &p) const
2498 {
2499 return {cos_th_ * p.get_x() - sin_th_ * p.get_y() + center_.get_x(),
2500 sin_th_ * p.get_x() + cos_th_ * p.get_y() + center_.get_y()};
2501 }
2502
2508 {
2509 ah_domain_error_if(a_ <= 0 or b_ <= 0) << "RotatedEllipse radii must be > 0";
2510 }
2511
2517 {
2518 const Geom_Number norm2 = cos_th_ * cos_th_ + sin_th_ * sin_th_;
2519 ah_domain_error_if(norm2 == 0) << "Rotation vector (cos,sin) cannot be (0,0)";
2520 if (norm2 != 1)
2521 {
2522 const Geom_Number norm = square_root(norm2);
2523 cos_th_ /= norm;
2524 sin_th_ /= norm;
2525 }
2526 }
2527
2528public:
2537 RotatedEllipse(Point center, const Geom_Number &a, const Geom_Number &b,
2539 : center_(std::move(center)), a_(a), b_(b), cos_th_(cos_theta), sin_th_(sin_theta)
2540 {
2543 }
2544
2551 RotatedEllipse(Point center, const Geom_Number &a, const Geom_Number &b)
2552 : center_(std::move(center)), a_(a), b_(b), cos_th_(1), sin_th_(0)
2553 {
2555 }
2556
2561 RotatedEllipse() : center_(0, 0), a_(1), b_(1), cos_th_(1), sin_th_(0)
2562 {
2563 // empty
2564 }
2565
2566 RotatedEllipse(const RotatedEllipse &) = default;
2567
2569
2575 [[nodiscard]] bool operator == (const RotatedEllipse &e) const noexcept
2576 {
2577 return center_ == e.center_ and a_ == e.a_ and b_ == e.b_ and cos_th_ == e.cos_th_ and
2578 sin_th_ == e.sin_th_;
2579 }
2580
2586 [[nodiscard]] bool operator != (const RotatedEllipse &e) const noexcept
2587 {
2588 return not (*this == e);
2589 }
2590
2592 [[nodiscard]] const Point &get_center() const
2593 {
2594 return center_;
2595 }
2597 [[nodiscard]] const Geom_Number &get_a() const
2598 {
2599 return a_;
2600 }
2602 [[nodiscard]] const Geom_Number &get_b() const
2603 {
2604 return b_;
2605 }
2607 [[nodiscard]] const Geom_Number &get_cos() const
2608 {
2609 return cos_th_;
2610 }
2612 [[nodiscard]] const Geom_Number &get_sin() const
2613 {
2614 return sin_th_;
2615 }
2616
2622 {
2624 return geom_pi() * a_ * b_;
2625 }
2626
2634 {
2636
2637 const Point lp = to_local(p);
2638 return (lp.get_x() * lp.get_x()) / (a_ * a_) + (lp.get_y() * lp.get_y()) / (b_ * b_);
2639 }
2640
2646 [[nodiscard]] bool contains(const Point &p) const
2647 {
2648 return radius_value(p) <= 1;
2649 }
2650
2656 [[nodiscard]] bool strictly_contains(const Point &p) const
2657 {
2658 return radius_value(p) < 1;
2659 }
2660
2666 [[nodiscard]] bool on_boundary(const Point &p) const
2667 {
2668 return radius_value(p) == 1;
2669 }
2670
2678 {
2680 // Point on axis-aligned ellipse: (a cos t, b sin t).
2681 // Rotate and translate to the world.
2682 return to_world(Point(a_ * cos_t, b_ * sin_t));
2683 }
2684
2690 [[nodiscard]] Point sample(const Geom_Number &t) const
2691 {
2692 const Geom_Number angle = Geom_Number(2) * geom_pi() * t;
2693 return sample(cosinus(angle), sinus(angle));
2694 }
2695
2701 [[nodiscard]] bool intersects_with(const Segment &s) const
2702 {
2705 return Ellipse(Point(0, 0), a_, b_).intersects_with(local_s);
2706 }
2707
2715 {
2719 return {to_world(local_i.get_src_point()), to_world(local_i.get_tgt_point())};
2720 }
2721
2722 [[nodiscard]] std::string to_string() const
2723 {
2724 return "RotatedEllipse(center=" + center_.to_string() +
2725 ", a=" + std::to_string(geom_number_to_double(a_)) +
2726 ", b=" + std::to_string(geom_number_to_double(b_)) +
2727 ", cos=" + std::to_string(geom_number_to_double(cos_th_)) +
2728 ", sin=" + std::to_string(geom_number_to_double(sin_th_)) + ")";
2729 }
2730
2733 {
2735 };
2736
2744 {
2746
2747 const Geom_Number a2 = a_ * a_;
2748 const Geom_Number b2 = b_ * b_;
2749 const Geom_Number c2 = cos_th_ * cos_th_;
2750 const Geom_Number s2 = sin_th_ * sin_th_;
2751 const Geom_Number x_extent = square_root(a2 * c2 + b2 * s2);
2752 const Geom_Number y_extent = square_root(a2 * s2 + b2 * c2);
2753 const Geom_Number cross = (a2 - b2) * sin_th_ * cos_th_;
2754 const Geom_Number right_y = cross / x_extent;
2755 const Geom_Number top_x = cross / y_extent;
2756
2757 return {Point(center_.get_x() + x_extent, center_.get_y() + right_y),
2761 }
2762};
2763
2764/*****************************************************************
2765
2766 Fundamental text primitive
2767
2768 Utility to draw text strings on the plane. Offsets are not stored because
2769 callers can shift the reference point directly.
2770*/
2771
2778inline size_t approximate_string_size(const std::string &str)
2779{
2780 const char *ptr = str.c_str();
2781
2782 size_t _len = 0;
2783 for (int i = 0; true; /* empty */)
2784 {
2785 switch (ptr[i])
2786 {
2787 case '\\':
2788 // Skip all characters that compose the LaTeX command.
2789 for (++i; isalnum(ptr[i]) and ptr[i] != '\0'; /* nothing */)
2790 ++i;
2791 ++_len;
2792 break;
2793
2794 case '$':
2795 case '{':
2796 case '}':
2797 case '\n':
2798 ++i;
2799 break;
2800
2801 case '\0':
2802 return _len;
2803
2804 default:
2805 ++_len;
2806 ++i;
2807 break;
2808 }
2809 }
2810}
2811
2816class Text : public Geom_Object
2817{
2819
2820 std::string str_;
2821
2822 size_t len_ = 0;
2823
2824public:
2825 static constexpr double font_width_in_points = 0.8;
2826
2827 static constexpr double font_height_in_points = 1.2;
2828
2834 Text(Point p, const std::string &str)
2835 : p_(std::move(p)), str_(str), len_(approximate_string_size(str))
2836 {
2837 // empty
2838 }
2839
2841 Text() = default;
2842
2844 [[nodiscard]] const size_t &len() const
2845 {
2846 return len_;
2847 }
2848
2850 [[nodiscard]] const Point &get_point() const
2851 {
2852 return p_;
2853 }
2854
2856 [[nodiscard]] const std::string &get_str() const
2857 {
2858 return str_;
2859 }
2860
2863 {
2864 return p_;
2865 }
2866
2869 {
2870 return p_;
2871 }
2872
2875 {
2876 return p_;
2877 }
2878
2881 {
2882 return p_;
2883 }
2884};
2885
2886inline Geom_Number area_of_parallelogram(const Point &a, const Point &b, const Point &c)
2887{
2888 return ((b.get_x() - a.get_x()) * (c.get_y() - a.get_y()) -
2889 (c.get_x() - a.get_x()) * (b.get_y() - a.get_y()));
2890}
2891
2893enum class Orientation
2894{
2895 CCW, // counter-clockwise
2896 CW, // clockwise
2897 COLLINEAR
2898};
2899
2902[[nodiscard]] inline Orientation orientation(const Point &a, const Point &b, const Point &c)
2903{
2904 const Geom_Number area = area_of_parallelogram(a, b, c);
2905 if (area > 0)
2906 return Orientation::CCW;
2907 if (area < 0)
2908 return Orientation::CW;
2910}
2911
2914{
2915 INSIDE,
2916 ON_CIRCLE,
2917 OUTSIDE,
2919};
2920
2924 const Point &c, const Point &p)
2925{
2926 const Geom_Number adx = a.get_x() - p.get_x();
2927 const Geom_Number ady = a.get_y() - p.get_y();
2928 const Geom_Number bdx = b.get_x() - p.get_x();
2929 const Geom_Number bdy = b.get_y() - p.get_y();
2930 const Geom_Number cdx = c.get_x() - p.get_x();
2931 const Geom_Number cdy = c.get_y() - p.get_y();
2932
2933 const Geom_Number ad2 = adx * adx + ady * ady;
2934 const Geom_Number bd2 = bdx * bdx + bdy * bdy;
2935 const Geom_Number cd2 = cdx * cdx + cdy * cdy;
2936
2937 return ad2 * (bdx * cdy - bdy * cdx) - bd2 * (adx * cdy - ady * cdx) +
2938 cd2 * (adx * bdy - ady * bdx);
2939}
2940
2943[[nodiscard]] inline InCircleResult in_circle(const Point &a, const Point &b, const Point &c,
2944 const Point &p)
2945{
2946 const Orientation o = orientation(a, b, c);
2949
2950 const Geom_Number det = in_circle_determinant(a, b, c, p);
2951 if (det == 0)
2953
2954 if (o == Orientation::CCW)
2956
2958}
2959
2961[[nodiscard]] inline bool on_segment(const Segment &s, const Point &p)
2962{
2963 return s.contains(p);
2964}
2965
2967[[nodiscard]] inline bool segments_intersect(const Segment &s1, const Segment &s2)
2968{
2969 return s1.intersects_with(s2);
2970}
2971
2973[[nodiscard]] inline bool segments_intersect(const Point &a, const Point &b, const Point &c,
2974 const Point &d)
2975{
2976 return Segment(a, b).intersects_with(Segment(c, d));
2977}
2978
2983{
2984 ah_domain_error_if(not segments_intersect(s1, s2)) << "Segments do not intersect";
2985
2986 const Point &a = s1.get_src_point();
2987 const Point &b = s1.get_tgt_point();
2988 const Point &c = s2.get_src_point();
2989 const Point &d = s2.get_tgt_point();
2990
2991 // Degenerate-point cases may still have a unique intersection point.
2992 if (a == b and c == d)
2993 {
2994 ah_domain_error_if(a != c) << "Segments do not intersect";
2995 return a;
2996 }
2997
2998 if (a == b)
2999 return a;
3000
3001 if (c == d)
3002 return c;
3003
3004 if (not s1.is_parallel_with(s2))
3005 return s1.intersection_with(s2);
3006
3007 // Collinear/parallel intersections can still be unique (touching endpoint).
3009 bool has_unique_point = false;
3011 {
3013 {
3014 unique_point = p;
3015 has_unique_point = true;
3016 return;
3017 }
3018
3019 ah_domain_error_if(unique_point != p) << "No unique intersection point";
3020 };
3021
3022 if (on_segment(s2, a))
3024 if (on_segment(s2, b))
3026 if (on_segment(s1, c))
3028 if (on_segment(s1, d))
3030
3031 ah_domain_error_if(not has_unique_point) << "No unique intersection point";
3032
3033 return unique_point;
3034}
3035
3037[[nodiscard]] inline Geom_Number area_of_triangle(const Point &a, const Point &b, const Point &c)
3038{
3039 return abs(area_of_parallelogram(a, b, c)) / 2;
3040}
3041
3042// ============================================================================
3043// 3D Primitives
3044// ============================================================================
3045
3054{
3056
3057public:
3061 Point3D() : x_(0), y_(0), z_(0) {}
3062
3069 Point3D(const Geom_Number &x, const Geom_Number &y, const Geom_Number &z) : x_(x), y_(y), z_(z) {}
3070
3071 Point3D(const Point3D &) = default;
3072
3073 Point3D &operator = (const Point3D &) = default;
3074
3076 [[nodiscard]] const Geom_Number &get_x() const
3077 {
3078 return x_;
3079 }
3081 [[nodiscard]] const Geom_Number &get_y() const
3082 {
3083 return y_;
3084 }
3086 [[nodiscard]] const Geom_Number &get_z() const
3087 {
3088 return z_;
3089 }
3090
3096 [[nodiscard]] bool operator == (const Point3D &p) const
3097 {
3098 return x_ == p.x_ and y_ == p.y_ and z_ == p.z_;
3099 }
3100
3106 [[nodiscard]] bool operator != (const Point3D &p) const
3107 {
3108 return !(*this == p);
3109 }
3110
3117 {
3118 return {x_ + p.x_, y_ + p.y_, z_ + p.z_};
3119 }
3120
3127 {
3128 return {x_ - p.x_, y_ - p.y_, z_ - p.z_};
3129 }
3130
3137 {
3138 x_ += p.x_;
3139 y_ += p.y_;
3140 z_ += p.z_;
3141 return *this;
3142 }
3143
3150 {
3151 x_ -= p.x_;
3152 y_ -= p.y_;
3153 z_ -= p.z_;
3154 return *this;
3155 }
3156
3162 {
3163 return {-x_, -y_, -z_};
3164 }
3165
3172 {
3173 return {x_ * s, y_ * s, z_ * s};
3174 }
3175
3182 {
3183 return {x_ / s, y_ / s, z_ / s};
3184 }
3185
3191 [[nodiscard]] Geom_Number dot(const Point3D &p) const
3192 {
3193 return x_ * p.x_ + y_ * p.y_ + z_ * p.z_;
3194 }
3195
3201 [[nodiscard]] Point3D cross(const Point3D &p) const
3202 {
3203 return {y_ * p.z_ - z_ * p.y_, z_ * p.x_ - x_ * p.z_, x_ * p.y_ - y_ * p.x_};
3204 }
3205
3212 {
3213 const Geom_Number dx = x_ - p.x_;
3214 const Geom_Number dy = y_ - p.y_;
3215 const Geom_Number dz = z_ - p.z_;
3216 return dx * dx + dy * dy + dz * dz;
3217 }
3218
3224 {
3225 return x_ * x_ + y_ * y_ + z_ * z_;
3226 }
3227
3233 {
3234 return square_root(norm_squared());
3235 }
3236
3243 {
3245 }
3246
3253 {
3254 const Geom_Number n = norm();
3255 ah_domain_error_if(n == 0) << "Cannot normalize zero Point3D";
3256 return *this / n;
3257 }
3258
3264 {
3265 return {x_, y_};
3266 }
3267
3273 [[nodiscard]] static Point3D from_2d(const Point &p)
3274 {
3275 return {p.get_x(), p.get_y(), Geom_Number(0)};
3276 }
3277
3284 [[nodiscard]] static Point3D from_2d(const Point &p, const Geom_Number &z)
3285 {
3286 return {p.get_x(), p.get_y(), z};
3287 }
3288};
3289
3292 const Point3D &c)
3293{
3294 return a.dot(b.cross(c));
3295}
3296
3302{
3304
3305public:
3307 Segment3D() = default;
3308
3314 Segment3D(const Point3D &src, const Point3D &tgt) : src_(src), tgt_(tgt) {}
3315
3318 {
3319 return src_;
3320 }
3323 {
3324 return tgt_;
3325 }
3328 {
3329 return src_;
3330 }
3333 {
3334 return tgt_;
3335 }
3336
3342 {
3343 return tgt_ - src_;
3344 }
3345
3351 {
3353 }
3354
3360 {
3361 return src_.distance_to(tgt_);
3362 }
3363
3369 [[nodiscard]] Point3D at(const Geom_Number &t) const
3370 {
3371 const Geom_Number s = Geom_Number(1) - t;
3372 return src_ * s + tgt_ * t;
3373 }
3374
3380 {
3381 return at(Geom_Number(1, 2));
3382 }
3383
3391 [[nodiscard]] bool operator == (const Segment3D &s) const
3392 {
3393 return (src_ == s.src_ and tgt_ == s.tgt_) or (src_ == s.tgt_ and tgt_ == s.src_);
3394 }
3395
3401 [[nodiscard]] bool operator != (const Segment3D &s) const
3402 {
3403 return not (*this == s);
3404 }
3405
3411 [[nodiscard]] bool contains(const Point3D &p) const
3412 {
3413 const Point3D d = direction();
3414 const Point3D w = p - src_;
3415
3416 if (d == Point3D(0, 0, 0))
3417 return p == src_; // Segment is a point
3418
3419 // Check for collinearity using cross product
3420 if (d.cross(w) != Point3D(0, 0, 0))
3421 return false;
3422
3423 // Check if point is within the segment bounds using dot product
3424 const Geom_Number dot = w.dot(d);
3425 return dot >= 0 && dot <= d.dot(d);
3426 }
3427
3434 {
3435 const Point3D d = direction();
3436 const Geom_Number len2 = d.dot(d);
3437
3438 if (len2 == 0)
3439 return p.distance_to(src_); // Segment is a point
3440
3441 // Project p onto the line defined by the segment
3442 // t = ((p - src) . d) / |d|^2
3443 Geom_Number t = (p - src_).dot(d) / len2;
3444
3445 // Clamp t to the [0, 1] interval to stay on the segment
3446 if (t < 0)
3447 t = 0;
3448 else if (t > 1)
3449 t = 1;
3450
3451 const Point3D proj = at(t);
3452 return p.distance_to(proj);
3453 }
3454};
3455
3461{
3463
3464public:
3466 Triangle3D() = default;
3467
3474 Triangle3D(const Point3D &p1, const Point3D &p2, const Point3D &p3) : p1_(p1), p2_(p2), p3_(p3) {}
3475
3477 [[nodiscard]] const Point3D &get_p1() const
3478 {
3479 return p1_;
3480 }
3482 [[nodiscard]] const Point3D &get_p2() const
3483 {
3484 return p2_;
3485 }
3487 [[nodiscard]] const Point3D &get_p3() const
3488 {
3489 return p3_;
3490 }
3491
3500 {
3501 return (p2_ - p1_).cross(p3_ - p1_);
3502 }
3503
3509 {
3510 return normal().norm_squared() / Geom_Number(2);
3511 }
3512
3518 {
3519 return (p1_ + p2_ + p3_) / Geom_Number(3);
3520 }
3521
3526 [[nodiscard]] bool is_degenerate() const
3527 {
3528 return normal().norm_squared() == 0;
3529 }
3530
3538 {
3540 };
3541
3549 {
3551 << "Barycentric coordinates undefined for degenerate triangle";
3552
3553 const Point3D v0 = p2_ - p1_, v1 = p3_ - p1_, v2 = p - p1_;
3554 const Geom_Number d00 = v0.dot(v0);
3555 const Geom_Number d01 = v0.dot(v1);
3556 const Geom_Number d11 = v1.dot(v1);
3557 const Geom_Number d20 = v2.dot(v0);
3558 const Geom_Number d21 = v2.dot(v1);
3559 const Geom_Number denom = d00 * d11 - d01 * d01;
3560 ah_domain_error_if(denom == 0) << "Barycentric coordinates undefined for degenerate triangle";
3561 const Geom_Number v = (d11 * d20 - d01 * d21) / denom;
3562 const Geom_Number w = (d00 * d21 - d01 * d20) / denom;
3563 return {Geom_Number(1) - v - w, v, w};
3564 }
3565};
3566
3572{
3574
3575public:
3577 Tetrahedron() = default;
3578
3586 Tetrahedron(const Point3D &p1, const Point3D &p2, const Point3D &p3, const Point3D &p4)
3587 : p1_(p1), p2_(p2), p3_(p3), p4_(p4)
3588 {}
3589
3591 [[nodiscard]] const Point3D &get_p1() const
3592 {
3593 return p1_;
3594 }
3596 [[nodiscard]] const Point3D &get_p2() const
3597 {
3598 return p2_;
3599 }
3601 [[nodiscard]] const Point3D &get_p3() const
3602 {
3603 return p3_;
3604 }
3606 [[nodiscard]] const Point3D &get_p4() const
3607 {
3608 return p4_;
3609 }
3610
3619 {
3620 return scalar_triple_product(p2_ - p1_, p3_ - p1_, p4_ - p1_);
3621 }
3622
3628 {
3630 if (v < 0)
3631 v = -v;
3632 return v / Geom_Number(6);
3633 }
3634
3639 [[nodiscard]] bool is_degenerate() const
3640 {
3641 return signed_volume_x6() == 0;
3642 }
3643
3649 {
3650 return (p1_ + p2_ + p3_ + p4_) / Geom_Number(4);
3651 }
3652
3660 [[nodiscard]] bool contains(const Point3D &p) const
3661 {
3662 auto signed_volume_x6_of =
3663 [](const Point3D &a, const Point3D &b, const Point3D &c, const Point3D &d)
3664 {
3665 return scalar_triple_product(b - a, c - a, d - a);
3666 };
3667
3669 if (d0 == 0)
3670 return false; // Not contained in a degenerate tetrahedron
3671
3672 // Keep vertex order consistent with d0 by replacing one vertex at a time.
3673 const Geom_Number d1 = signed_volume_x6_of(p, p2_, p3_, p4_);
3674 const Geom_Number d2 = signed_volume_x6_of(p1_, p, p3_, p4_);
3675 const Geom_Number d3 = signed_volume_x6_of(p1_, p2_, p, p4_);
3676 const Geom_Number d4 = signed_volume_x6_of(p1_, p2_, p3_, p);
3677
3678 // Point is inside iff all sub-volumes have the same sign as d0.
3679 if (d0 > 0)
3680 return d1 >= 0 and d2 >= 0 and d3 >= 0 and d4 >= 0;
3681 return d1 <= 0 and d2 <= 0 and d3 <= 0 and d4 <= 0;
3682 }
3683
3687 struct Faces
3688 {
3690 };
3691
3697 {
3698 return {{Triangle3D(p1_, p2_, p3_), Triangle3D(p1_, p2_, p4_), Triangle3D(p1_, p3_, p4_),
3699 Triangle3D(p2_, p3_, p4_)}};
3700 }
3701};
3702
3703// ============================================================================
3704// Stream output operators for geometry classes
3705// ============================================================================
3706
3707inline std::ostream &operator << (std::ostream &o, const Point &p)
3708{
3709 o << "Point(" << p.get_x() << ", " << p.get_y() << ")";
3710 return o;
3711}
3712
3713inline std::ostream &operator << (std::ostream &o, const Segment &s)
3714{
3715 o << "Segment(" << s.get_src_point() << " -> " << s.get_tgt_point() << ")";
3716 return o;
3717}
3718
3719inline std::ostream &operator << (std::ostream &o, const Triangle &t)
3720{
3721 o << "Triangle(" << t.get_p1() << ", " << t.get_p2() << ", " << t.get_p3() << ")";
3722 return o;
3723}
3724
3725inline std::ostream &operator << (std::ostream &o, const Rectangle &r)
3726{
3727 o << "Rectangle" << r.to_string();
3728 return o;
3729}
3730
3731inline std::ostream &operator << (std::ostream &o, const Ellipse &e)
3732{
3733 o << e.to_string();
3734 return o;
3735}
3736
3737inline std::ostream &operator << (std::ostream &o, const RotatedEllipse &e)
3738{
3739 o << e.to_string();
3740 return o;
3741}
3742
3743inline std::ostream &operator << (std::ostream &o, const Point3D &p)
3744{
3745 o << "Point3D(" << p.get_x() << ", " << p.get_y() << ", " << p.get_z() << ")";
3746 return o;
3747}
3748
3749inline std::ostream &operator << (std::ostream &o, const Segment3D &s)
3750{
3751 o << "Segment3D(" << s.get_src() << " -> " << s.get_tgt() << ")";
3752 return o;
3753}
3754
3755inline std::ostream &operator << (std::ostream &o, const Triangle3D &t)
3756{
3757 o << "Triangle3D(" << t.get_p1() << ", " << t.get_p2() << ", " << t.get_p3() << ")";
3758 return o;
3759}
3760
3761inline std::ostream &operator << (std::ostream &o, const Tetrahedron &t)
3762{
3763 o << "Tetrahedron(" << t.get_p1() << ", " << t.get_p2() << ", " << t.get_p3() << ", "
3764 << t.get_p4() << ")";
3765 return o;
3766}
3767} // namespace Aleph
3768
3769namespace std {
3774template <>
3775struct hash<Aleph::Point>
3776{
3777 std::size_t operator () (const Aleph::Point &p) const
3778 {
3779 const std::size_t hx = std::hash<std::string>{}(p.get_x().get_str());
3780 const std::size_t hy = std::hash<std::string>{}(p.get_y().get_str());
3781 return hx ^ (hy + 0x9e3779b97f4a7c15ULL + (hx << 6) + (hx >> 2));
3782 }
3783};
3784} // namespace std
3785
3786// ============================================================================
3787// std::format support (C++20)
3788// ============================================================================
3789
3790#if __cplusplus >= 202002L && __has_include(<format>)
3791#include <format>
3792
3793#if defined(__cpp_lib_format)
3794
3796template <>
3797struct std::formatter<Aleph::Point> : std::formatter<std::string>
3798{
3799 auto format(const Aleph::Point &p, std::format_context &ctx) const
3800 {
3801 return std::formatter<std::string>::format(std::format("Point({}, {})",
3804 ctx);
3805 }
3806};
3807
3809template <>
3810struct std::formatter<Aleph::Segment> : std::formatter<std::string>
3811{
3812 auto format(const Aleph::Segment &s, std::format_context &ctx) const
3813 {
3814 return std::formatter<std::string>::format(
3815 std::format("Segment({} -> {})", s.get_src_point(), s.get_tgt_point()), ctx);
3816 }
3817};
3818
3820template <>
3821struct std::formatter<Aleph::Triangle> : std::formatter<std::string>
3822{
3823 auto format(const Aleph::Triangle &t, std::format_context &ctx) const
3824 {
3825 return std::formatter<std::string>::format(
3826 std::format("Triangle({}, {}, {})", t.get_p1(), t.get_p2(), t.get_p3()), ctx);
3827 }
3828};
3829
3831template <>
3832struct std::formatter<Aleph::Rectangle> : std::formatter<std::string>
3833{
3834 auto format(const Aleph::Rectangle &r, std::format_context &ctx) const
3835 {
3836 return std::formatter<std::string>::format(
3837 std::format("Rectangle({}, {} -- {}, {})", Aleph::geom_number_to_double(r.get_xmin()),
3838 Aleph::geom_number_to_double(r.get_ymin()),
3839 Aleph::geom_number_to_double(r.get_xmax()),
3840 Aleph::geom_number_to_double(r.get_ymax())),
3841 ctx);
3842 }
3843};
3844
3846template <>
3847struct std::formatter<Aleph::Polar_Point> : std::formatter<std::string>
3848{
3849 auto format(const Aleph::Polar_Point &p, std::format_context &ctx) const
3850 {
3851 return std::formatter<std::string>::format(
3852 std::format("PolarPoint(r={}, theta={})", Aleph::geom_number_to_double(p.get_r()),
3854 ctx);
3855 }
3856};
3857
3859template <>
3860struct std::formatter<Aleph::Ellipse> : std::formatter<std::string>
3861{
3862 auto format(const Aleph::Ellipse &e, std::format_context &ctx) const
3863 {
3864 return std::formatter<std::string>::format(std::format("{}", e.to_string()), ctx);
3865 }
3866};
3867
3869template <>
3870struct std::formatter<Aleph::RotatedEllipse> : std::formatter<std::string>
3871{
3872 auto format(const Aleph::RotatedEllipse &e, std::format_context &ctx) const
3873 {
3874 return std::formatter<std::string>::format(std::format("{}", e.to_string()), ctx);
3875 }
3876};
3877
3879template <>
3880struct std::formatter<Aleph::Point3D> : std::formatter<std::string>
3881{
3882 auto format(const Aleph::Point3D &p, std::format_context &ctx) const
3883 {
3884 return std::formatter<std::string>::format(
3885 std::format("Point3D({}, {}, {})", Aleph::geom_number_to_double(p.get_x()),
3887 ctx);
3888 }
3889};
3890
3892template <>
3893struct std::formatter<Aleph::Segment3D> : std::formatter<std::string>
3894{
3895 auto format(const Aleph::Segment3D &s, std::format_context &ctx) const
3896 {
3897 return std::formatter<std::string>::format(
3898 std::format("Segment3D({} -> {})", s.get_src(), s.get_tgt()), ctx);
3899 }
3900};
3901
3903template <>
3904struct std::formatter<Aleph::Triangle3D> : std::formatter<std::string>
3905{
3906 auto format(const Aleph::Triangle3D &t, std::format_context &ctx) const
3907 {
3908 return std::formatter<std::string>::format(
3909 std::format("Triangle3D({}, {}, {})", t.get_p1(), t.get_p2(), t.get_p3()), ctx);
3910 }
3911};
3912
3914template <>
3915struct std::formatter<Aleph::Tetrahedron> : std::formatter<std::string>
3916{
3917 auto format(const Aleph::Tetrahedron &t, std::format_context &ctx) const
3918 {
3919 return std::formatter<std::string>::format(
3920 std::format("Tetrahedron({}, {}, {}, {})", t.get_p1(), t.get_p2(), t.get_p3(), t.get_p4()),
3921 ctx);
3922 }
3923};
3924
3925#endif // __cpp_lib_format
3926#endif // C++20 format
3927
3928#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:527
#define ah_range_error_if(C)
Throws std::range_error if condition holds.
Definition ah-errors.H:212
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:2076
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:2218
const Geom_Number & get_vradius() const
Gets the vertical radius.
Definition point.H:2153
Ellipse(const Ellipse &e)=default
Copy constructor.
Geom_Number area() const
Calculates the area of the ellipse.
Definition point.H:2162
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:2263
std::string to_string() const
Returns a string representation of the ellipse.
Definition point.H:2198
Point sample(const Geom_Number &angle) const
Samples a point on the ellipse's boundary at a given angle.
Definition point.H:2188
Ellipse(Point center, const Geom_Number &hr, const Geom_Number &vr)
Constructs an ellipse.
Definition point.H:2102
bool intersects_with(const Point &p) const
Checks if a point lies exactly on the ellipse boundary.
Definition point.H:2379
const Point & get_center() const
Gets the center point of the ellipse.
Definition point.H:2143
Point highest_point() const
Gets the highest point on the ellipse boundary.
Definition point.H:2234
Point rightmost_point() const
Gets the rightmost point on the ellipse boundary.
Definition point.H:2252
Geom_Number perimeter() const
Approximates the perimeter of the ellipse.
Definition point.H:2173
bool intersects_with(const Segment &s) const
Checks if a segment intersects the ellipse.
Definition point.H:2298
bool contains_to(const Point &p) const
Definition point.H:2369
static bool is_clockwise()
Returns the orientation of the ellipse.
Definition point.H:2228
const Geom_Number & get_hradius() const
Gets the horizontal radius.
Definition point.H:2148
void validate_positive_radii() const
Validates that radii are positive.
Definition point.H:2210
Segment intersection_with(const Segment &sg) const
Computes the intersection segment between a line segment and this ellipse.
Definition point.H:2391
Geom_Number vr_
Definition point.H:2092
Geom_Number compute_radius(const Point &p) const
Computes the ellipse equation value for a point.
Definition point.H:2344
Point leftmost_point() const
Gets the leftmost point on the ellipse boundary.
Definition point.H:2246
friend class Point
Definition point.H:2077
Geom_Number hr_
Definition point.H:2091
Point lowest_point() const
Gets the lowest point on the ellipse boundary.
Definition point.H:2240
bool operator!=(const Ellipse &e) const noexcept
Checks for inequality.
Definition point.H:2137
Ellipse()
Default constructor.
Definition point.H:2117
bool operator==(const Ellipse &e) const noexcept
Checks for exact equality.
Definition point.H:2127
Point center_
Definition point.H:2089
bool contains(const Point &p) const
Checks if a point lies inside or on the boundary of this ellipse.
Definition point.H:2363
Represents a point in 3D space with exact rational coordinates.
Definition point.H:3054
Point3D operator+(const Point3D &p) const
Vector addition.
Definition point.H:3116
Point3D & operator=(const Point3D &)=default
Point3D operator/(const Geom_Number &s) const
Scalar division.
Definition point.H:3181
Point3D()
Default constructor.
Definition point.H:3061
Geom_Number y_
Definition point.H:3055
Geom_Number z_
Definition point.H:3055
Point3D operator-() const
Unary negation.
Definition point.H:3161
static Point3D from_2d(const Point &p)
Lifts a 2D point to 3D, setting its z-coordinate to 0.
Definition point.H:3273
Point3D normalize() const
Returns a normalized copy of this vector (unit vector).
Definition point.H:3252
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:3069
bool operator==(const Point3D &p) const
Checks for exact equality between two 3D points.
Definition point.H:3096
Geom_Number x_
Definition point.H:3055
bool operator!=(const Point3D &p) const
Checks for inequality between two 3D points.
Definition point.H:3106
Geom_Number distance_squared_to(const Point3D &p) const
Squared Euclidean distance to another point.
Definition point.H:3211
Point3D cross(const Point3D &p) const
Cross product.
Definition point.H:3201
const Geom_Number & get_z() const
Gets the z-coordinate.
Definition point.H:3086
Point3D & operator+=(const Point3D &p)
Vector addition and assignment.
Definition point.H:3136
const Geom_Number & get_x() const
Gets the x-coordinate.
Definition point.H:3076
Point3D operator*(const Geom_Number &s) const
Scalar multiplication.
Definition point.H:3171
Point3D(const Point3D &)=default
Geom_Number norm_squared() const
Squared Euclidean norm (magnitude).
Definition point.H:3223
const Geom_Number & get_y() const
Gets the y-coordinate.
Definition point.H:3081
Geom_Number distance_to(const Point3D &p) const
Euclidean distance to another point.
Definition point.H:3242
Point to_2d() const
Projects this 3D point to a 2D point by dropping the z-coordinate.
Definition point.H:3263
Point3D & operator-=(const Point3D &p)
Vector subtraction and assignment.
Definition point.H:3149
Geom_Number dot(const Point3D &p) const
Dot product.
Definition point.H:3191
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:3284
Geom_Number norm() const
Euclidean norm (magnitude).
Definition point.H:3232
Represents a point with rectangular coordinates in a 2D plane.
Definition point.H:221
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:504
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:588
Geom_Number distance_with(const Point &p) const
Definition point.H:670
Geom_Number y_
Definition point.H:227
Geom_Number norm_squared() const
Squared Euclidean norm.
Definition point.H:384
bool is_inside(const Segment &s) const
Checks if this point is contained within a segment.
Definition point.H:1460
Geom_Number norm() const
Euclidean norm (vector magnitude).
Definition point.H:393
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:448
bool operator==(const Point &point) const noexcept
Checks for exact equality between two points.
Definition point.H:259
std::string to_string() const
Returns a string representation of the point as "(x,y)".
Definition point.H:640
Geom_Number dot(const Point &p) const
Dot product.
Definition point.H:364
bool intersects_with(const Ellipse &e) const
Checks if this point lies exactly on the boundary of an ellipse.
Definition point.H:2436
const Point & leftmost_point() const
Returns the leftmost point (smallest x-coordinate).
Definition point.H:697
Point operator*(const Geom_Number &s) const
Scalar multiplication.
Definition point.H:344
bool operator<(const Point &point) const noexcept
Defines a strict lexicographical ordering for points.
Definition point.H:279
Point(const Geom_Number &x, const Geom_Number &y)
Constructs a point from Cartesian coordinates.
Definition point.H:243
const Geom_Number & get_y() const noexcept
Gets the y-coordinate value.
Definition point.H:457
Point operator/(const Geom_Number &s) const
Scalar division.
Definition point.H:354
Point midpoint(const Point &other) const
Calculates the midpoint between this point and another.
Definition point.H:439
bool is_to_left_from(const Point &p1, const Point &p2) const
Definition point.H:492
Geom_Number distance_to(const Point &p) const
Calculates the Euclidean distance to another point.
Definition point.H:1498
const Point & highest_point() const
Returns the highest point (largest y-coordinate).
Definition point.H:679
Geom_Number cross(const Point &p) const
2D cross-product (z-component of the 3D cross-product).
Definition point.H:374
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:544
Geom_Number x_
Definition point.H:226
Point lerp(const Point &other, const Geom_Number &t) const
Linear interpolation between this point and another.
Definition point.H:428
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:607
const Point & lowest_point() const
Returns the lowest point (smallest y-coordinate).
Definition point.H:688
Point & operator-=(const Point &p)
Vector subtraction and assignment.
Definition point.H:322
Point & operator+=(const Point &p)
Vector addition and assignment.
Definition point.H:299
bool is_to_right_from(const Segment &s) const
Definition point.H:570
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:526
bool is_to_left_from(const Segment &s) const
Definition point.H:564
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:515
bool is_to_right_on_from(const Point &p1, const Point &p2) const
Definition point.H:532
bool operator!=(const Point &point) const noexcept
Checks for inequality between two points.
Definition point.H:269
Point operator+(const Point &p) const
Vector addition.
Definition point.H:289
bool is_right_of(const Segment &s) const
Checks if this point is to the right of a directed segment.
Definition point.H:1478
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:486
Geom_Number distance_squared_to(const Point &that) const
Calculates the squared Euclidean distance to another point.
Definition point.H:1490
Point rotate(const Geom_Number &angle) const
Rotates the point around the origin by a given angle.
Definition point.H:415
Point()
Default constructor.
Definition point.H:234
Point operator-() const
Unary negation (vector inversion).
Definition point.H:334
bool is_colinear_with(const Point &p1, const Point &p2) const
Checks if this point is collinear with two other points.
Definition point.H:468
Point normalize() const
Returns a normalized copy of this vector (magnitude 1).
Definition point.H:403
Polar representation of a 2D point.
Definition point.H:728
Polar_Point(const Geom_Number &r, const Geom_Number &theta)
Constructs a polar point from a radius and an angle.
Definition point.H:758
Quadrant
Enumerates polar quadrants in counterclockwise order.
Definition point.H:778
Polar_Point(const Point &p)
Constructs a polar point from a Cartesian point.
Definition point.H:767
const Geom_Number & get_theta() const
Gets the angle (theta) of the polar point.
Definition point.H:748
Quadrant get_quadrant() const
Returns the quadrant where the point lies.
Definition point.H:790
std::string to_string() const
Converts the polar point to a string representation "[r,theta]".
Definition point.H:809
Polar_Point()=default
Default constructor.
const Geom_Number & get_r() const
Gets the radius (magnitude) of the polar point.
Definition point.H:739
Geom_Number theta_
Definition point.H:732
Geom_Number r_
Definition point.H:731
An axis-aligned rectangle.
Definition point.H:1789
const Geom_Number & get_xmin() const
Gets the minimum x-coordinate.
Definition point.H:1815
std::string to_string() const
Returns a string representation of the rectangle.
Definition point.H:1978
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:1866
Geom_Number area() const noexcept
Calculates the area of the rectangle.
Definition point.H:1888
Rectangle()
Default constructor.
Definition point.H:1838
Geom_Number perimeter() const noexcept
Calculates the perimeter of the rectangle.
Definition point.H:1897
bool intersects(const Rectangle &that) const noexcept
Checks if this axis-aligned rectangle intersects another one.
Definition point.H:1926
Geom_Number width() const noexcept
Calculates the width of the rectangle.
Definition point.H:1878
std::array< Point, 4 > corners() const noexcept
Gets the four corners of the rectangle.
Definition point.H:1916
const Geom_Number & get_ymax() const
Gets the maximum y-coordinate.
Definition point.H:1830
const Geom_Number & get_ymin() const
Gets the minimum y-coordinate.
Definition point.H:1820
Geom_Number distance_squared_to(const Point &p) const noexcept
Calculates the squared Euclidean distance from the rectangle to a point.
Definition point.H:1938
Geom_Number ymax_
Definition point.H:1791
const Geom_Number & get_xmax() const
Gets the maximum x-coordinate.
Definition point.H:1825
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:1851
Geom_Number distance_to(const Point &p) const
Calculates the Euclidean distance from the rectangle to a point.
Definition point.H:1959
Geom_Number xmin_
Definition point.H:1790
Geom_Number ymin_
Definition point.H:1790
bool operator==(const Rectangle &r) const noexcept
Checks for exact equality between two rectangles.
Definition point.H:1799
bool contains(const Point &p) const noexcept
Checks if this axis-aligned rectangle contains a point.
Definition point.H:1969
bool operator!=(const Rectangle &r) const noexcept
Checks for inequality between two rectangles.
Definition point.H:1809
Point center() const noexcept
Calculates the center point of the rectangle.
Definition point.H:1906
Geom_Number height() const noexcept
Calculates the height of the rectangle.
Definition point.H:1883
Geom_Number xmax_
Definition point.H:1791
An ellipse with arbitrary rotation.
Definition point.H:2473
bool strictly_contains(const Point &p) const
Checks if a point lies strictly inside the ellipse.
Definition point.H:2656
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:2537
bool operator!=(const RotatedEllipse &e) const noexcept
Checks for inequality between two rotated ellipses.
Definition point.H:2586
bool contains(const Point &p) const
Checks if a point lies inside or on the ellipse.
Definition point.H:2646
Point sample(const Geom_Number &t) const
Samples a point on the ellipse boundary for a parameter t.
Definition point.H:2690
RotatedEllipse(Point center, const Geom_Number &a, const Geom_Number &b)
Constructs an axis-aligned ellipse (rotation angle is 0).
Definition point.H:2551
Geom_Number cos_th_
Definition point.H:2477
Geom_Number a_
Definition point.H:2475
Geom_Number radius_value(const Point &p) const
Evaluates the ellipse equation for a given point.
Definition point.H:2633
RotatedEllipse & operator=(const RotatedEllipse &)=default
const Geom_Number & get_b() const
Gets the semi-axis 'b' (local y-axis radius).
Definition point.H:2602
Geom_Number sin_th_
Definition point.H:2478
Geom_Number b_
Definition point.H:2476
void normalize_rotation()
Normalizes the rotation (cos, sin) vector to ensure it's a unit vector.
Definition point.H:2516
Geom_Number area() const
Calculates the area of the ellipse.
Definition point.H:2621
Segment intersection_with(const Segment &s) const
Computes the intersection segment between s and this ellipse.
Definition point.H:2714
Point to_world(const Point &p) const
Transforms a point from the ellipse's local frame back to world coordinates.
Definition point.H:2497
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:2677
ExtremalPoints extremal_points() const
Computes the four axis-extremal points of the rotated ellipse.
Definition point.H:2743
RotatedEllipse()
Default constructor.
Definition point.H:2561
const Geom_Number & get_sin() const
Gets the sine of the rotation angle.
Definition point.H:2612
std::string to_string() const
Definition point.H:2722
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:2485
bool on_boundary(const Point &p) const
Checks if a point lies exactly on the ellipse boundary.
Definition point.H:2666
bool intersects_with(const Segment &s) const
Checks if a segment intersects this rotated ellipse.
Definition point.H:2701
const Geom_Number & get_a() const
Gets the semi-axis 'a' (local x-axis radius).
Definition point.H:2597
const Geom_Number & get_cos() const
Gets the cosine of the rotation angle.
Definition point.H:2607
const Point & get_center() const
Gets the center point.
Definition point.H:2592
bool operator==(const RotatedEllipse &e) const noexcept
Checks for exact equality between two rotated ellipses.
Definition point.H:2575
void validate_positive_radii() const
Validates that semi-axes are positive.
Definition point.H:2507
Represents a line segment in 3D space.
Definition point.H:3302
bool operator==(const Segment3D &s) const
Checks for equality between two segments.
Definition point.H:3391
bool operator!=(const Segment3D &s) const
Checks for inequality.
Definition point.H:3401
Point3D midpoint() const
Calculates the midpoint of the segment.
Definition point.H:3379
bool contains(const Point3D &p) const
Checks if a point lies on the segment.
Definition point.H:3411
Point3D src_
Definition point.H:3303
const Point3D & get_src() const noexcept
Gets the source point of the segment.
Definition point.H:3317
Geom_Number distance_to(const Point3D &p) const
Calculates the shortest distance from a point to this segment.
Definition point.H:3433
const Point3D & get_tgt() const noexcept
Gets the target point of the segment.
Definition point.H:3322
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:3327
Point3D at(const Geom_Number &t) const
Evaluates a point on the segment via linear interpolation.
Definition point.H:3369
Segment3D(const Point3D &src, const Point3D &tgt)
Constructs a 3D segment from two endpoints.
Definition point.H:3314
const Point3D & get_tgt_point() const noexcept
Gets the target point of the segment (alias for get_tgt).
Definition point.H:3332
Point3D direction() const
Calculates the direction vector of the segment.
Definition point.H:3341
Geom_Number length_squared() const
Calculates the squared length of the segment.
Definition point.H:3350
Point3D tgt_
Definition point.H:3303
Geom_Number length() const
Calculates the length of the segment.
Definition point.H:3359
Represents a line segment between two points.
Definition point.H:837
const Point & rightmost_point() const noexcept
Gets the endpoint with the largest x-coordinate.
Definition point.H:915
bool intersects_properly_with(const Segment &s) const
Checks if this segment properly intersects another segment.
Definition point.H:1241
bool is_to_right_from(const Point &p) const
Definition point.H:1119
bool contains_to(const Point &p) const
Definition point.H:1265
bool contains(const Segment &s) const
Checks if another segment s lies entirely inside this segment.
Definition point.H:1275
Point mid_point() const
Returns the midpoint of this segment.
Definition point.H:1128
bool contains_to(const Segment &s) const
Definition point.H:1281
double slope() const
Returns the slope of the segment.
Definition point.H:1012
const Point & nearest_point(const Point &p) const
Returns whichever endpoint of this segment is nearer to a given point.
Definition point.H:1193
double counterclockwise_angle() const
Computes the counter-clockwise angle of this segment with respect to the x-axis.
Definition point.H:1059
Sense
Cardinal and intercardinal directions associated with a segment's vector.
Definition point.H:1349
bool is_left_of(const Point &p) const
Checks if this segment is to the left of a given point.
Definition point.H:1096
std::string to_string() const
Returns a string representation of the segment, e.g., "(x1,y1)(x2,y2)".
Definition point.H:1414
const Point & highest_point() const noexcept
Gets the endpoint with the largest y-coordinate.
Definition point.H:888
double compute_slope() const
Internal helper to compute the slope as a double.
Definition point.H:847
bool is_to_left_from(const Point &p) const
Definition point.H:1113
void rotate(const Geom_Number &angle)
Rotates the segment by a given angle around its source point.
Definition point.H:1431
Point intersection_with(const Segment &s) const
Computes the intersection point of the infinite lines defined by two segments.
Definition point.H:1334
const Point & lowest_point() const noexcept
Gets the endpoint with the smallest y-coordinate.
Definition point.H:897
Segment(Point src, Point tgt)
Constructs a segment from two endpoints.
Definition point.H:948
bool intersects_with(const Segment &s) const
Checks if this segment intersects another one (including endpoints and collinear overlap).
Definition point.H:1291
void enlarge_src(const Geom_Number &dist)
Extends the segment by a given distance from the source endpoint.
Definition point.H:1392
void enlarge_tgt(const Geom_Number &dist)
Extends the segment by a given distance from the target endpoint.
Definition point.H:1403
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:1161
const Point & leftmost_point() const noexcept
Gets the endpoint with the smallest x-coordinate.
Definition point.H:906
Segment(const Segment &sg, const Geom_Number &dist)
Constructs a new segment parallel to a given segment at a specified distance.
Definition point.H:996
Point at(const Geom_Number &t) const
Evaluates a point on the segment via linear interpolation.
Definition point.H:1150
Geom_Number size() const
Definition point.H:1075
double counterclockwise_angle_with(const Segment &s) const
Computes the counter-clockwise rotation angle FROM this segment TO another.
Definition point.H:1038
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:1225
friend class Point
Definition point.H:838
Segment reversed() const
Returns a new segment with the endpoints swapped.
Definition point.H:1140
bool is_right_of(const Point &p) const
Checks if this segment is to the right of a given point.
Definition point.H:1107
Geom_Number distance_to(const Point &p) const
Calculates the Euclidean distance from a point to this segment.
Definition point.H:1183
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:961
bool contains(const Point &p) const
Checks if a point lies on this segment.
Definition point.H:1259
Geom_Number slope_exact() const
Returns the exact slope of the segment as a Geom_Number.
Definition point.H:1022
bool operator!=(const Segment &s) const noexcept
Checks for inequality between two segments.
Definition point.H:879
Sense sense() const
Determines the cardinal/intercardinal direction of the segment.
Definition point.H:1364
bool is_colinear_with(const Point &p) const
Checks if a point is collinear with this segment.
Definition point.H:1085
Point tgt_
Definition point.H:841
bool operator==(const Segment &s) const noexcept
Checks for equality between two segments.
Definition point.H:869
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:985
const Point & get_tgt_point() const noexcept
Gets the target point of the segment.
Definition point.H:933
bool is_parallel_with(const Segment &s) const
Checks if this segment is parallel to another one.
Definition point.H:1323
const Point & get_src_point() const noexcept
Gets the source point of the segment.
Definition point.H:924
Geom_Number length() const
Returns the Euclidean length of the segment.
Definition point.H:1069
Point src_
Definition point.H:841
Segment get_perpendicular(const Point &p) const
Constructs a segment perpendicular to this that passes through point p.
Definition point.H:1203
Represents a tetrahedron in 3D space defined by four points.
Definition point.H:3572
Point3D centroid() const
Computes the centroid (center of mass) of the tetrahedron.
Definition point.H:3648
const Point3D & get_p4() const
Gets the fourth vertex.
Definition point.H:3606
Faces faces() const
Gets the four faces of the tetrahedron.
Definition point.H:3696
Geom_Number signed_volume_x6() const
Calculates 6 times the signed volume of the tetrahedron.
Definition point.H:3618
Tetrahedron(const Point3D &p1, const Point3D &p2, const Point3D &p3, const Point3D &p4)
Constructs a tetrahedron from four vertices.
Definition point.H:3586
Tetrahedron()=default
Default constructor.
bool is_degenerate() const
Checks if the tetrahedron is degenerate (i.e., its vertices are coplanar).
Definition point.H:3639
Geom_Number volume() const
Calculates the unsigned volume of the tetrahedron.
Definition point.H:3627
const Point3D & get_p1() const
Gets the first vertex.
Definition point.H:3591
const Point3D & get_p2() const
Gets the second vertex.
Definition point.H:3596
const Point3D & get_p3() const
Gets the third vertex.
Definition point.H:3601
bool contains(const Point3D &p) const
Checks if a point lies inside the tetrahedron.
Definition point.H:3660
Represents a text string positioned at a 2D point.
Definition point.H:2817
Text(Point p, const std::string &str)
Constructs a Text object.
Definition point.H:2834
std::string str_
Definition point.H:2820
Point lowest_point() const
Gets the lowest point (the anchor point).
Definition point.H:2868
static constexpr double font_height_in_points
Definition point.H:2827
static constexpr double font_width_in_points
Definition point.H:2825
Point highest_point() const
Gets the highest point (the anchor point).
Definition point.H:2862
Point rightmost_point() const
Gets the rightmost point (the anchor point).
Definition point.H:2880
const std::string & get_str() const
Gets the text string.
Definition point.H:2856
const Point & get_point() const
Gets the position point of the text.
Definition point.H:2850
Point p_
Definition point.H:2818
Point leftmost_point() const
Gets the leftmost point (the anchor point).
Definition point.H:2874
const size_t & len() const
Gets the estimated length of the text.
Definition point.H:2844
Text()=default
Default constructor.
size_t len_
Definition point.H:2822
Represents a triangle in 3D space defined by three points.
Definition point.H:3461
BaryCoords barycentric(const Point3D &p) const
Computes the barycentric coordinates of a point p with respect to this triangle.
Definition point.H:3548
Point3D centroid() const
Computes the centroid (center of mass) of the triangle.
Definition point.H:3517
const Point3D & get_p2() const
Gets the second vertex of the triangle.
Definition point.H:3482
Triangle3D()=default
Default constructor.
const Point3D & get_p3() const
Gets the third vertex of the triangle.
Definition point.H:3487
Triangle3D(const Point3D &p1, const Point3D &p2, const Point3D &p3)
Constructs a 3D triangle from three vertices.
Definition point.H:3474
Point3D normal() const
Computes the normal vector of the triangle's plane.
Definition point.H:3499
const Point3D & get_p1() const
Gets the first vertex of the triangle.
Definition point.H:3477
Geom_Number double_area_squared() const
Calculates twice the squared area of the triangle.
Definition point.H:3508
bool is_degenerate() const
Checks if the triangle is degenerate (i.e., its vertices are collinear).
Definition point.H:3526
A non-degenerate triangle defined by three points.
Definition point.H:1512
bool covers(const Point &p) const
Checks if a point lies within the closed, filled triangle.
Definition point.H:1770
Geom_Number perimeter() const
Computes the perimeter of the triangle.
Definition point.H:1672
Segment intersection_with(const Segment &s) const
Computes the intersection segment between this triangle and a segment.
Definition point.H:1758
const Point & get_p3() const
Gets the third vertex.
Definition point.H:1629
const Point & get_p2() const
Gets the second vertex.
Definition point.H:1624
bool contains(const Point &p) const
Checks if a point lies strictly inside this triangle.
Definition point.H:1744
Geom_Number area() const
Calculates the unsigned area of the triangle.
Definition point.H:1564
friend class Segment
Definition point.H:1514
Point incenter() const
Computes the incenter of the triangle.
Definition point.H:1716
const Point & rightmost_point() const
Gets the vertex with the largest x-coordinate.
Definition point.H:1612
const Point & lowest_point() const
Gets the vertex with the smallest y-coordinate.
Definition point.H:1592
const Point & get_p1() const
Gets the first vertex.
Definition point.H:1619
bool operator==(const Triangle &t) const noexcept
Checks for equality between two triangles.
Definition point.H:1641
Triangle(const Segment &s, Point p)
Constructs a triangle from a segment and a point.
Definition point.H:1554
std::array< Segment, 3 > edges() const
Gets the three edges of the triangle.
Definition point.H:1731
const Point & highest_point() const
Gets the vertex with the largest y-coordinate.
Definition point.H:1582
const Point & leftmost_point() const
Gets the vertex with the smallest x-coordinate.
Definition point.H:1602
Point circumcenter() const
Computes the circumcenter of the triangle.
Definition point.H:1684
bool operator!=(const Triangle &t) const noexcept
Checks for inequality between two triangles.
Definition point.H:1654
Point centroid() const
Computes the centroid (center of mass) of the triangle.
Definition point.H:1663
Triangle(Point p1, Point p2, Point p3)
Constructs a triangle from three points.
Definition point.H:1528
Triangle(Point p, const Segment &s)
Constructs a triangle from a point and a segment.
Definition point.H:1542
bool is_clockwise() const
Checks if the triangle vertices are ordered clockwise.
Definition point.H:1573
Geom_Number area_
Definition point.H:1518
std::string get_str(int base=10) const
Definition gmpfrxx.h:2180
__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:4123
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_y1_function > > y1(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4114
__gmp_expr< mpfr_t, mpfr_t > mpfr_class
Definition gmpfrxx.h:2457
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_cos_function > > cos(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4080
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_acos_function > > acos(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4086
__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:4121
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_sin_function > > sin(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4081
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_sqrt_function > > sqrt(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4069
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_abs_function > > abs(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4062
__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:4089
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_atan_function > > atan(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4088
__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:4122
__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:4071
__gmp_expr< mpq_t, mpq_t > mpq_class
Definition gmpfrxx.h:2225
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
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
Orientation
Classification of three-point orientation.
Definition point.H:2894
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:2923
Geom_Number pitag(const Geom_Number &x, const Geom_Number &y)
Definition point.H:167
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:2886
Geom_Number scalar_triple_product(const Point3D &a, const Point3D &b, const Point3D &c)
Scalar triple product: a · (b × c).
Definition point.H:3291
bool on_segment(const Segment &s, const Point &p)
Return true if p lies on segment s (exact).
Definition point.H:2961
Geom_Number cosinus(const Geom_Number &x)
Cosine of x (wrapper over mpfr).
Definition point.H:191
constexpr double PI_4
Definition point.H:131
bool segments_intersect(const Segment &s1, const Segment &s2)
Return true if segments s1 and s2 intersect (including endpoints).
Definition point.H:2967
and
Check uniqueness with explicit hash + equality functors.
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:2943
Point segment_intersection_point(const Segment &s1, const Segment &s2)
Compute the exact intersection point of segments s1 and s2.
Definition point.H:2982
InCircleResult
Classification of a point with respect to a triangle circumcircle.
Definition point.H:2914
Geom_Number square_root(const Geom_Number &x)
Square root of x (wrapper over mpfr).
Definition point.H:197
double geom_number_to_double(const Geom_Number &n)
Converts a Geom_Number to its double precision representation.
Definition point.H:120
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:3037
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:2902
constexpr double PI_2
Definition point.H:130
mpq_class Geom_Number
Numeric type used by the geometry module.
Definition point.H:113
Geom_Number arctan2(const Geom_Number &m, const Geom_Number &n)
Two-argument arc tangent (wrapper over mpfr).
Definition point.H:179
const Geom_Number & geom_pi()
High-precision pi value for computations that require Geom_Number.
Definition point.H:137
size_t approximate_string_size(const std::string &str)
Estimate the count of printable characters in a LaTeX string.
Definition point.H:2778
Geom_Number arctan(const Geom_Number &m)
Arc tangent of m (wrapper over mpfr).
Definition point.H:173
Geom_Number euclidean_distance(const Geom_Number &x, const Geom_Number &y)
Euclidean distance (hypotenuse) of the vector (x, y).
Definition point.H:160
std::ostream & operator<<(std::ostream &osObject, const Field< T > &rightOp)
Definition ahField.H:121
constexpr double PI
Definition point.H:129
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:185
STL namespace.
Base class for all geometric objects.
Definition point.H:206
virtual ~Geom_Object()=default
Geom_Object()=default
Holds the four axis-extremal points of a rotated ellipse.
Definition point.H:2733
A struct holding the four faces of the tetrahedron.
Definition point.H:3688
Structure to hold barycentric coordinates.
Definition point.H:3538
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
gsl_rng * r
DynList< int > l