@@ -48,39 +48,29 @@ constexpr bool are_2d_counterclockwise_sorted(const PointT& a, const PointT& b,
4848 return signed_measure_2d_tri (a, b, c) > 0 ;
4949}
5050// area of 2D polygon given counterclockwise sorted vertices v_0, v_1, \ldots, v_{n - 1} (lemma 1.3.3 of (1))
51- template <typename PointList >
52- requires (internals::is_eigen_dense_xpr_v<PointList> || internals::is_subscriptable<PointList, int >)
53- constexpr double signed_measure_2d_polygon (const PointList & points) {
51+ template <typename PolygonT >
52+ requires (internals::is_matrix_like_v<PolygonT >)
53+ constexpr double signed_measure_2d_polygon (const PolygonT & points) {
5454 double area = 0 ;
55- if constexpr (internals::is_eigen_dense_xpr_v<PointList>) {
56- fdapde_assert (points.rows () > 0 && points.cols () == 2 );
57- int n_points = points.rows ();
58- for (int i = 0 ; i < n_points - 1 ; ++i) {
59- area += (points (i, 0 ) + points (i + 1 , 0 )) * (points (i + 1 , 1 ) - points (i, 1 ));
60- }
61- area += (points (n_points - 1 , 0 ) + points (0 , 0 )) * (points (0 , 1 ) - points (n_points - 1 , 1 ));
62- } else if (internals::is_subscriptable<PointList, int >) {
63- // assume points to be a RowMajor expansion of the polygon nodes' coordinates
64- fdapde_assert (points.size () % 2 == 0 );
65- int n_points = points.size () / 2 ;
66- for (int i = 0 ; i < n_points - 1 ; ++i) {
67- area += (points[i] + points[i + 2 ]) * (points[i + 3 ] - points[i + 1 ]);
68- }
69- area += (points[n_points - 2 ] + points[0 ]) * (points[1 ] - points[n_points - 1 ]);
55+ fdapde_assert (points.rows () > 0 && points.cols () == 2 );
56+ int n_points = points.rows ();
57+ for (int i = 0 ; i < n_points - 1 ; ++i) {
58+ area += (points (i, 0 ) + points (i + 1 , 0 )) * (points (i + 1 , 1 ) - points (i, 1 ));
7059 }
60+ area += (points (n_points - 1 , 0 ) + points (0 , 0 )) * (points (0 , 1 ) - points (n_points - 1 , 1 ));
7161 return area;
7262}
73- template <typename PointList > constexpr bool are_2d_counterclockwise_sorted (const PointList & points) {
63+ template <typename PolygonT > constexpr bool are_2d_counterclockwise_sorted (const PolygonT & points) {
7464 return signed_measure_2d_polygon (points) > 0 ;
7565}
76- template <typename PointList > constexpr bool are_2d_clockwise_sorted (const PointList & points) {
66+ template <typename PolygonT > constexpr bool are_2d_clockwise_sorted (const PolygonT & points) {
7767 return signed_measure_2d_polygon (points) < 0 ;
7868}
7969
8070// 2D point-line orientation test
8171
82- // finds whether a 2D point p is on the positive side (left), negative side (right) or is collinear to the 2D directed
83- // line identified by points (a, b)
72+ // finds whether point p is on the positive side (left), negative side (right) or is collinear to the directed line
73+ // identified by points (a, b)
8474enum Orientation { LEFT = 0 , RIGHT = 1 , COLLINEAR = 2 };
8575template <typename PointT>
8676 requires (internals::is_subscriptable<PointT, int >)
@@ -105,10 +95,10 @@ template <typename point_t> bool reflex_turn(const point_t& a, const point_t& b,
10595 return !convex_turn (a, b, c); // reflex turn \iff not convex turn
10696}
10797
108- // test whether a point a belongs to the 2D segment identified by points b and c
98+ // test whether point a belongs to the 2D segment identified by points b and c
10999template <typename PointT>
110100 requires (internals::is_subscriptable<PointT, int >)
111- constexpr bool contains (const PointT& a, const PointT& b, const PointT& c) {
101+ constexpr bool contains (const PointT& a, const PointT& b, const PointT& c) { // -------------------- rename in point_in_2d_segment
112102 if (!collinear (a, b, c)) return false ;
113103 // if bc is not vertical, check x coordinates, otherwise check y coordinates
114104 if (b[0 ] != c[0 ]) {
@@ -121,18 +111,28 @@ constexpr bool contains(const PointT& a, const PointT& b, const PointT& c) {
121111// 2D segment-segment intersection test
122112template <typename PointT>
123113 requires (internals::is_subscriptable<PointT, int >)
124- constexpr bool intersect (const PointT& a, const PointT& b, const PointT& c, const PointT& d) {
114+ constexpr bool segment_proper_intersect_2d_segment (const PointT& a, const PointT& b, const PointT& c, const PointT& d) {
125115 // check proper intersection (points {c, d} lies on opposite sides of ab and points {a, b} on opposite sides of cd)
126116 if (
127117 (orientation (c, a, b) == Orientation::LEFT ^ orientation (d, a, b) == Orientation::LEFT ) &&
128118 (orientation (a, c, d) == Orientation::LEFT ^ orientation (b, c, d) == Orientation::LEFT )) {
129119 return true ;
130120 }
121+ return false ;
122+ }
123+
124+ template <typename PointT>
125+ requires (internals::is_subscriptable<PointT, int >)
126+ constexpr bool intersect (const PointT& a, const PointT& b, const PointT& c, const PointT& d) {
127+ // check proper intersection
128+ if (segment_proper_intersect_2d_segment (a, b, c, d)) { return true ; }
131129 // check if an endpoint of a segment lies on the other segment
132- if (contains (c, a, b) || contains (d, a, b) || contains (a, c, d) || contains (b, c, d)) { return true ; }
130+ if (contains (c, a, b) || contains (d, a, b) || contains (a, c, d) || contains (b, c, d)) {
131+ return true ; }
133132 return false ;
134133}
135134
135+ // 2D point in triangle test
136136template <typename PointT>
137137 requires (internals::is_subscriptable<PointT, int >)
138138constexpr bool point_in_2d_tri (const PointT& a, const PointT& t1, const PointT& t2, const PointT& t3) {
@@ -141,7 +141,130 @@ constexpr bool point_in_2d_tri(const PointT& a, const PointT& t1, const PointT&
141141 (t2[0 ] - a[0 ]) * (t3[1 ] - a[1 ]) >= (t3[0 ] - a[0 ]) * (t2[1 ] - a[1 ]);
142142}
143143
144- // then we can detect if a diagonal is fully contained in a polygon
144+ // 2D point in polygon (closed simple chain of points) test. ray-casting algorithm
145+ template <typename PolygonT, typename PointT>
146+ requires (internals::is_matrix_like_v<PolygonT> && internals::is_vector_like_v<PointT>)
147+ constexpr bool point_in_2d_polygon (const PolygonT& points, const PointT& p) {
148+ bool inside = false ;
149+ fdapde_assert (points.rows () > 0 && points.cols () == 2 );
150+ int n = points.rows ();
151+ for (int i = 0 , j = n - 1 ; i < n; j = i++) {
152+ if (
153+ ((points (i, 1 ) > p[1 ]) != (points (j, 1 ) > p[1 ])) &&
154+ (p[0 ] <
155+ (points (j, 0 ) - points (i, 0 )) * (p[1 ] - points (i, 1 )) / (points (j, 1 ) - points (i, 1 )) + points (i, 0 ))) {
156+ inside = !inside;
157+ }
158+ }
159+ return inside;
160+ }
161+
162+ // 2D polygon in polygon test
163+ template <typename InnerPolygonT, typename OuterPolygonT>
164+ requires (internals::is_matrix_like_v<InnerPolygonT> && internals::is_matrix_like_v<OuterPolygonT>)
165+ constexpr bool polygon_in_2d_polygon (const InnerPolygonT& P, const OuterPolygonT& Q) {
166+ fdapde_assert (P.rows () > 0 && P.cols () == 2 && Q.rows () > 0 && Q.cols () == 2 );
167+ // check if all points of P are inside Q
168+ for (int i = 0 , n = P.rows (); i < n; ++i) {
169+ if (!point_in_2d_polygon (Q, P.row (i))) { return false ; }
170+ }
171+ // check if all edges of P do not properly intersect any edge of Q
172+ for (int i = 0 , n = P.rows () - 1 ; i < n; ++i) {
173+ int h = i + 1 % n;
174+ for (int j = 0 , m = Q.rows () - 1 ; j < m; ++j) {
175+ if (segment_proper_intersect_2d_segment (P.row (i), P.row (h), Q.row (j), Q.row (j + 1 % m))) { return false ; }
176+ }
177+ }
178+ return true ;
179+ }
180+
181+ // checks if point D is inside the circumcircle of the triangle (A, B, C) (Delaunay criterion)
182+ // template <typename PointT>
183+ // requires(internals::is_subscriptable<PointT, int>)
184+ // constexpr bool in_circle(const PointT& A, const PointT& B, const PointT& C, const PointT& D) {
185+ // double Ax = A[0] - D[0], Ay = A[1] - D[1];
186+ // double Bx = B[0] - D[0], By = B[1] - D[1];
187+ // double Cx = C[0] - D[0], Cy = C[1] - D[1];
188+
189+ // double det = Ax * (By * (Cx * Cx + Cy * Cy) - Cy * (Bx * Bx + By * By)) -
190+ // Ay * (Bx * (Cx * Cx + Cy * Cy) - Cx * (Bx * Bx + By * By)) +
191+ // (Ax * Ax + Ay * Ay) * (Bx * Cy - By * Cx);
192+
193+ // return det > 0; // D is inside the circumcircle if determinant is positive
194+ // }
195+
196+ // // computes circumcenter of triangle given its 2D coordinates
197+ // template <typename PointT>
198+ // requires(internals::is_subscriptable<PointT, int>)
199+ // constexpr PointT circumcenter(const PointT& A, const PointT& B, const PointT& C) {
200+ // double x1 = A[0], y1 = A[1];
201+ // double x2 = B[0], y2 = B[1];
202+ // double x3 = C[0], y3 = C[1];
203+
204+ // double D = 2.0 * (x1*(y2 - y3) + x2*(y3 - y1) + x3*(y1 - y2));
205+
206+ // double x1sq = x1 * x1 + y1 * y1;
207+ // double x2sq = x2 * x2 + y2 * y2;
208+ // double x3sq = x3 * x3 + y3 * y3;
209+
210+ // double Ux = (x1sq*(y2 - y3) + x2sq*(y3 - y1) + x3sq*(y1 - y2)) / D;
211+ // double Uy = (x1sq*(x3 - x2) + x2sq*(x1 - x3) + x3sq*(x2 - x1)) / D;
212+
213+ // return PointT(Ux, Uy);
214+ // }
215+
216+ // // detects if p is inside circle of diameter ab
217+ // template <typename PointT>
218+ // requires(internals::is_subscriptable<PointT, int>)
219+ // constexpr bool is_encroached(const PointT& p, const PointT& a, const PointT& b) {
220+ // PointT m = 0.5 * (a + b); // midpoint
221+ // double radius_sq = 0.25 * (a - b).squaredNorm();
222+ // double dist_sq = (p - m).squaredNorm();
223+ // return dist_sq < radius_sq - machine_epsilon;
224+ // }
225+
226+ // // computes the angle between two segments that share vertex p in 2D (counterclockwise)
227+ // // the angle is in degrees
228+ // template <typename PointT>
229+ // requires(internals::is_subscriptable<PointT, int>)
230+ // constexpr double angle_between(const PointT& a, const PointT& p, const PointT& b) {
231+ // PointT v1 = a - p;
232+ // PointT v2 = b - p;
233+ // double dot = v1.dot(v2);
234+ // double norm1 = std::sqrt(v1.squaredNorm());
235+ // double norm2 = std::sqrt(v2.squaredNorm());
236+
237+ // double cos_theta = dot / (norm1 * norm2);
238+ // cos_theta = std::fmax(-1.0, std::fmin(1.0, cos_theta));
239+
240+ // double angle_rad = std::acos(cos_theta);
241+ // // 2D vector product to dtermine orientation
242+ // double cross = v1[0] * v2[1] - v1[1] * v2[0];
243+ // // if cross > 0: angle is clockwise, so we need to subtract from 2 * pi since boundary is counterclockwise oriented
244+ // if (cross > 0)
245+ // angle_rad = 2 * M_PI - angle_rad;
246+
247+ // return angle_rad * 180.0 / M_PI;
248+ // }
249+
250+ // // checks if the angle between two segments that share vertex p in 2D is acute
251+ // template <typename PointT>
252+ // requires(internals::is_subscriptable<PointT, int>)
253+ // constexpr bool is_angle_acute(const PointT& a, const PointT& p, const PointT& b) {
254+ // return angle_between(a, p, b) < 90.0 - machine_epsilon;
255+ // }
256+
257+ // // calculates segment ab's length (2D)
258+ // template <typename PointT>
259+ // requires(internals::is_subscriptable<PointT, int>)
260+ // constexpr double segment_length(const PointT& a, const PointT& b) {
261+ // const double dx = a[0] - b[0];
262+ // const double dy = a[1] - b[1];
263+ // return std::sqrt(dx * dx + dy * dy);
264+ // }
265+
266+
267+ // then we can detect if a diagonal is fully contained in a polygon
145268
146269 // 3D geometry
147270
0 commit comments