1 | /**************************************************************************/ |
2 | /* geometry_2d.h */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #ifndef GEOMETRY_2D_H |
32 | #define GEOMETRY_2D_H |
33 | |
34 | #include "core/math/delaunay_2d.h" |
35 | #include "core/math/math_funcs.h" |
36 | #include "core/math/triangulate.h" |
37 | #include "core/math/vector2.h" |
38 | #include "core/math/vector2i.h" |
39 | #include "core/math/vector3.h" |
40 | #include "core/math/vector3i.h" |
41 | #include "core/templates/vector.h" |
42 | |
43 | class Geometry2D { |
44 | public: |
45 | static real_t get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2, Vector2 &c1, Vector2 &c2) { |
46 | Vector2 d1 = q1 - p1; // Direction vector of segment S1. |
47 | Vector2 d2 = q2 - p2; // Direction vector of segment S2. |
48 | Vector2 r = p1 - p2; |
49 | real_t a = d1.dot(d1); // Squared length of segment S1, always nonnegative. |
50 | real_t e = d2.dot(d2); // Squared length of segment S2, always nonnegative. |
51 | real_t f = d2.dot(r); |
52 | real_t s, t; |
53 | // Check if either or both segments degenerate into points. |
54 | if (a <= (real_t)CMP_EPSILON && e <= (real_t)CMP_EPSILON) { |
55 | // Both segments degenerate into points. |
56 | c1 = p1; |
57 | c2 = p2; |
58 | return Math::sqrt((c1 - c2).dot(c1 - c2)); |
59 | } |
60 | if (a <= (real_t)CMP_EPSILON) { |
61 | // First segment degenerates into a point. |
62 | s = 0.0; |
63 | t = f / e; // s = 0 => t = (b*s + f) / e = f / e |
64 | t = CLAMP(t, 0.0f, 1.0f); |
65 | } else { |
66 | real_t c = d1.dot(r); |
67 | if (e <= (real_t)CMP_EPSILON) { |
68 | // Second segment degenerates into a point. |
69 | t = 0.0; |
70 | s = CLAMP(-c / a, 0.0f, 1.0f); // t = 0 => s = (b*t - c) / a = -c / a |
71 | } else { |
72 | // The general nondegenerate case starts here. |
73 | real_t b = d1.dot(d2); |
74 | real_t denom = a * e - b * b; // Always nonnegative. |
75 | // If segments not parallel, compute closest point on L1 to L2 and |
76 | // clamp to segment S1. Else pick arbitrary s (here 0). |
77 | if (denom != 0.0f) { |
78 | s = CLAMP((b * f - c * e) / denom, 0.0f, 1.0f); |
79 | } else { |
80 | s = 0.0; |
81 | } |
82 | // Compute point on L2 closest to S1(s) using |
83 | // t = Dot((P1 + D1*s) - P2,D2) / Dot(D2,D2) = (b*s + f) / e |
84 | t = (b * s + f) / e; |
85 | |
86 | //If t in [0,1] done. Else clamp t, recompute s for the new value |
87 | // of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a |
88 | // and clamp s to [0, 1]. |
89 | if (t < 0.0f) { |
90 | t = 0.0; |
91 | s = CLAMP(-c / a, 0.0f, 1.0f); |
92 | } else if (t > 1.0f) { |
93 | t = 1.0; |
94 | s = CLAMP((b - c) / a, 0.0f, 1.0f); |
95 | } |
96 | } |
97 | } |
98 | c1 = p1 + d1 * s; |
99 | c2 = p2 + d2 * t; |
100 | return Math::sqrt((c1 - c2).dot(c1 - c2)); |
101 | } |
102 | |
103 | static Vector2 get_closest_point_to_segment(const Vector2 &p_point, const Vector2 *p_segment) { |
104 | Vector2 p = p_point - p_segment[0]; |
105 | Vector2 n = p_segment[1] - p_segment[0]; |
106 | real_t l2 = n.length_squared(); |
107 | if (l2 < 1e-20f) { |
108 | return p_segment[0]; // Both points are the same, just give any. |
109 | } |
110 | |
111 | real_t d = n.dot(p) / l2; |
112 | |
113 | if (d <= 0.0f) { |
114 | return p_segment[0]; // Before first point. |
115 | } else if (d >= 1.0f) { |
116 | return p_segment[1]; // After first point. |
117 | } else { |
118 | return p_segment[0] + n * d; // Inside. |
119 | } |
120 | } |
121 | |
122 | static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) { |
123 | Vector2 an = a - s; |
124 | Vector2 bn = b - s; |
125 | Vector2 cn = c - s; |
126 | |
127 | bool orientation = an.cross(bn) > 0; |
128 | |
129 | if ((bn.cross(cn) > 0) != orientation) { |
130 | return false; |
131 | } |
132 | |
133 | return (cn.cross(an) > 0) == orientation; |
134 | } |
135 | |
136 | static Vector2 get_closest_point_to_segment_uncapped(const Vector2 &p_point, const Vector2 *p_segment) { |
137 | Vector2 p = p_point - p_segment[0]; |
138 | Vector2 n = p_segment[1] - p_segment[0]; |
139 | real_t l2 = n.length_squared(); |
140 | if (l2 < 1e-20f) { |
141 | return p_segment[0]; // Both points are the same, just give any. |
142 | } |
143 | |
144 | real_t d = n.dot(p) / l2; |
145 | |
146 | return p_segment[0] + n * d; // Inside. |
147 | } |
148 | |
149 | // Disable False Positives in MSVC compiler; we correctly check for 0 here to prevent a division by 0. |
150 | // See: https://github.com/godotengine/godot/pull/44274 |
151 | #ifdef _MSC_VER |
152 | #pragma warning(disable : 4723) |
153 | #endif |
154 | |
155 | static bool line_intersects_line(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b, Vector2 &r_result) { |
156 | // See http://paulbourke.net/geometry/pointlineplane/ |
157 | |
158 | const real_t denom = p_dir_b.y * p_dir_a.x - p_dir_b.x * p_dir_a.y; |
159 | if (Math::is_zero_approx(denom)) { // Parallel? |
160 | return false; |
161 | } |
162 | |
163 | const Vector2 v = p_from_a - p_from_b; |
164 | const real_t t = (p_dir_b.x * v.y - p_dir_b.y * v.x) / denom; |
165 | r_result = p_from_a + t * p_dir_a; |
166 | return true; |
167 | } |
168 | |
169 | // Re-enable division by 0 warning |
170 | #ifdef _MSC_VER |
171 | #pragma warning(default : 4723) |
172 | #endif |
173 | |
174 | static bool segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b, Vector2 *r_result) { |
175 | Vector2 B = p_to_a - p_from_a; |
176 | Vector2 C = p_from_b - p_from_a; |
177 | Vector2 D = p_to_b - p_from_a; |
178 | |
179 | real_t ABlen = B.dot(B); |
180 | if (ABlen <= 0) { |
181 | return false; |
182 | } |
183 | Vector2 Bn = B / ABlen; |
184 | C = Vector2(C.x * Bn.x + C.y * Bn.y, C.y * Bn.x - C.x * Bn.y); |
185 | D = Vector2(D.x * Bn.x + D.y * Bn.y, D.y * Bn.x - D.x * Bn.y); |
186 | |
187 | // Fail if C x B and D x B have the same sign (segments don't intersect). |
188 | if ((C.y < (real_t)-CMP_EPSILON && D.y < (real_t)-CMP_EPSILON) || (C.y > (real_t)CMP_EPSILON && D.y > (real_t)CMP_EPSILON)) { |
189 | return false; |
190 | } |
191 | |
192 | // Fail if segments are parallel or colinear. |
193 | // (when A x B == zero, i.e (C - D) x B == zero, i.e C x B == D x B) |
194 | if (Math::is_equal_approx(C.y, D.y)) { |
195 | return false; |
196 | } |
197 | |
198 | real_t ABpos = D.x + (C.x - D.x) * D.y / (D.y - C.y); |
199 | |
200 | // Fail if segment C-D crosses line A-B outside of segment A-B. |
201 | if ((ABpos < 0) || (ABpos > 1)) { |
202 | return false; |
203 | } |
204 | |
205 | // Apply the discovered position to line A-B in the original coordinate system. |
206 | if (r_result) { |
207 | *r_result = p_from_a + B * ABpos; |
208 | } |
209 | |
210 | return true; |
211 | } |
212 | |
213 | static inline bool is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { |
214 | return p_point.distance_squared_to(p_circle_pos) <= p_circle_radius * p_circle_radius; |
215 | } |
216 | |
217 | static real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { |
218 | Vector2 line_vec = p_to - p_from; |
219 | Vector2 vec_to_line = p_from - p_circle_pos; |
220 | |
221 | // Create a quadratic formula of the form ax^2 + bx + c = 0 |
222 | real_t a, b, c; |
223 | |
224 | a = line_vec.dot(line_vec); |
225 | b = 2 * vec_to_line.dot(line_vec); |
226 | c = vec_to_line.dot(vec_to_line) - p_circle_radius * p_circle_radius; |
227 | |
228 | // Solve for t. |
229 | real_t sqrtterm = b * b - 4 * a * c; |
230 | |
231 | // If the term we intend to square root is less than 0 then the answer won't be real, |
232 | // so it definitely won't be t in the range 0 to 1. |
233 | if (sqrtterm < 0) { |
234 | return -1; |
235 | } |
236 | |
237 | // If we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection) |
238 | // then the following can be skipped and we can just return the equivalent of res1. |
239 | sqrtterm = Math::sqrt(sqrtterm); |
240 | real_t res1 = (-b - sqrtterm) / (2 * a); |
241 | real_t res2 = (-b + sqrtterm) / (2 * a); |
242 | |
243 | if (res1 >= 0 && res1 <= 1) { |
244 | return res1; |
245 | } |
246 | if (res2 >= 0 && res2 <= 1) { |
247 | return res2; |
248 | } |
249 | return -1; |
250 | } |
251 | |
252 | enum PolyBooleanOperation { |
253 | OPERATION_UNION, |
254 | OPERATION_DIFFERENCE, |
255 | OPERATION_INTERSECTION, |
256 | OPERATION_XOR |
257 | }; |
258 | enum PolyJoinType { |
259 | JOIN_SQUARE, |
260 | JOIN_ROUND, |
261 | JOIN_MITER |
262 | }; |
263 | enum PolyEndType { |
264 | END_POLYGON, |
265 | END_JOINED, |
266 | END_BUTT, |
267 | END_SQUARE, |
268 | END_ROUND |
269 | }; |
270 | |
271 | static Vector<Vector<Point2>> merge_polygons(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { |
272 | return _polypaths_do_operation(OPERATION_UNION, p_polygon_a, p_polygon_b); |
273 | } |
274 | |
275 | static Vector<Vector<Point2>> clip_polygons(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { |
276 | return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polygon_a, p_polygon_b); |
277 | } |
278 | |
279 | static Vector<Vector<Point2>> intersect_polygons(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { |
280 | return _polypaths_do_operation(OPERATION_INTERSECTION, p_polygon_a, p_polygon_b); |
281 | } |
282 | |
283 | static Vector<Vector<Point2>> exclude_polygons(const Vector<Point2> &p_polygon_a, const Vector<Point2> &p_polygon_b) { |
284 | return _polypaths_do_operation(OPERATION_XOR, p_polygon_a, p_polygon_b); |
285 | } |
286 | |
287 | static Vector<Vector<Point2>> clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { |
288 | return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polyline, p_polygon, true); |
289 | } |
290 | |
291 | static Vector<Vector<Point2>> intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { |
292 | return _polypaths_do_operation(OPERATION_INTERSECTION, p_polyline, p_polygon, true); |
293 | } |
294 | |
295 | static Vector<Vector<Point2>> offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { |
296 | return _polypath_offset(p_polygon, p_delta, p_join_type, END_POLYGON); |
297 | } |
298 | |
299 | static Vector<Vector<Point2>> offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { |
300 | ERR_FAIL_COND_V_MSG(p_end_type == END_POLYGON, Vector<Vector<Point2>>(), "Attempt to offset a polyline like a polygon (use offset_polygon instead)." ); |
301 | |
302 | return _polypath_offset(p_polygon, p_delta, p_join_type, p_end_type); |
303 | } |
304 | |
305 | static Vector<int> triangulate_delaunay(const Vector<Vector2> &p_points) { |
306 | Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(p_points); |
307 | Vector<int> triangles; |
308 | |
309 | for (int i = 0; i < tr.size(); i++) { |
310 | triangles.push_back(tr[i].points[0]); |
311 | triangles.push_back(tr[i].points[1]); |
312 | triangles.push_back(tr[i].points[2]); |
313 | } |
314 | return triangles; |
315 | } |
316 | |
317 | static Vector<int> triangulate_polygon(const Vector<Vector2> &p_polygon) { |
318 | Vector<int> triangles; |
319 | if (!Triangulate::triangulate(p_polygon, triangles)) { |
320 | return Vector<int>(); //fail |
321 | } |
322 | return triangles; |
323 | } |
324 | |
325 | static bool is_polygon_clockwise(const Vector<Vector2> &p_polygon) { |
326 | int c = p_polygon.size(); |
327 | if (c < 3) { |
328 | return false; |
329 | } |
330 | const Vector2 *p = p_polygon.ptr(); |
331 | real_t sum = 0; |
332 | for (int i = 0; i < c; i++) { |
333 | const Vector2 &v1 = p[i]; |
334 | const Vector2 &v2 = p[(i + 1) % c]; |
335 | sum += (v2.x - v1.x) * (v2.y + v1.y); |
336 | } |
337 | |
338 | return sum > 0.0f; |
339 | } |
340 | |
341 | // Alternate implementation that should be faster. |
342 | static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) { |
343 | int c = p_polygon.size(); |
344 | if (c < 3) { |
345 | return false; |
346 | } |
347 | const Vector2 *p = p_polygon.ptr(); |
348 | Vector2 further_away(-1e20, -1e20); |
349 | Vector2 further_away_opposite(1e20, 1e20); |
350 | |
351 | for (int i = 0; i < c; i++) { |
352 | further_away.x = MAX(p[i].x, further_away.x); |
353 | further_away.y = MAX(p[i].y, further_away.y); |
354 | further_away_opposite.x = MIN(p[i].x, further_away_opposite.x); |
355 | further_away_opposite.y = MIN(p[i].y, further_away_opposite.y); |
356 | } |
357 | |
358 | // Make point outside that won't intersect with points in segment from p_point. |
359 | further_away += (further_away - further_away_opposite) * Vector2(1.221313, 1.512312); |
360 | |
361 | int intersections = 0; |
362 | for (int i = 0; i < c; i++) { |
363 | const Vector2 &v1 = p[i]; |
364 | const Vector2 &v2 = p[(i + 1) % c]; |
365 | |
366 | Vector2 res; |
367 | if (segment_intersects_segment(v1, v2, p_point, further_away, &res)) { |
368 | intersections++; |
369 | if (res.is_equal_approx(p_point)) { |
370 | // Point is in one of the polygon edges. |
371 | return true; |
372 | } |
373 | } |
374 | } |
375 | |
376 | return (intersections & 1); |
377 | } |
378 | |
379 | static bool is_segment_intersecting_polygon(const Vector2 &p_from, const Vector2 &p_to, const Vector<Vector2> &p_polygon) { |
380 | int c = p_polygon.size(); |
381 | const Vector2 *p = p_polygon.ptr(); |
382 | for (int i = 0; i < c; i++) { |
383 | const Vector2 &v1 = p[i]; |
384 | const Vector2 &v2 = p[(i + 1) % c]; |
385 | if (segment_intersects_segment(p_from, p_to, v1, v2, nullptr)) { |
386 | return true; |
387 | } |
388 | } |
389 | return false; |
390 | } |
391 | |
392 | static real_t vec2_cross(const Point2 &O, const Point2 &A, const Point2 &B) { |
393 | return (real_t)(A.x - O.x) * (B.y - O.y) - (real_t)(A.y - O.y) * (B.x - O.x); |
394 | } |
395 | |
396 | // Returns a list of points on the convex hull in counter-clockwise order. |
397 | // Note: the last point in the returned list is the same as the first one. |
398 | static Vector<Point2> convex_hull(Vector<Point2> P) { |
399 | int n = P.size(), k = 0; |
400 | Vector<Point2> H; |
401 | H.resize(2 * n); |
402 | |
403 | // Sort points lexicographically. |
404 | P.sort(); |
405 | |
406 | // Build lower hull. |
407 | for (int i = 0; i < n; ++i) { |
408 | while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) { |
409 | k--; |
410 | } |
411 | H.write[k++] = P[i]; |
412 | } |
413 | |
414 | // Build upper hull. |
415 | for (int i = n - 2, t = k + 1; i >= 0; i--) { |
416 | while (k >= t && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) { |
417 | k--; |
418 | } |
419 | H.write[k++] = P[i]; |
420 | } |
421 | |
422 | H.resize(k); |
423 | return H; |
424 | } |
425 | |
426 | static Vector<Point2i> bresenham_line(const Point2i &p_start, const Point2i &p_end) { |
427 | Vector<Point2i> points; |
428 | |
429 | Vector2i delta = (p_end - p_start).abs() * 2; |
430 | Vector2i step = (p_end - p_start).sign(); |
431 | Vector2i current = p_start; |
432 | |
433 | if (delta.x > delta.y) { |
434 | int err = delta.x / 2; |
435 | |
436 | for (; current.x != p_end.x; current.x += step.x) { |
437 | points.push_back(current); |
438 | |
439 | err -= delta.y; |
440 | if (err < 0) { |
441 | current.y += step.y; |
442 | err += delta.x; |
443 | } |
444 | } |
445 | } else { |
446 | int err = delta.y / 2; |
447 | |
448 | for (; current.y != p_end.y; current.y += step.y) { |
449 | points.push_back(current); |
450 | |
451 | err -= delta.x; |
452 | if (err < 0) { |
453 | current.x += step.x; |
454 | err += delta.y; |
455 | } |
456 | } |
457 | } |
458 | |
459 | points.push_back(current); |
460 | |
461 | return points; |
462 | } |
463 | |
464 | static Vector<Vector<Vector2>> decompose_polygon_in_convex(Vector<Point2> polygon); |
465 | |
466 | static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size); |
467 | static Vector<Vector3i> partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size); |
468 | |
469 | private: |
470 | static Vector<Vector<Point2>> _polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open = false); |
471 | static Vector<Vector<Point2>> _polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type); |
472 | }; |
473 | |
474 | #endif // GEOMETRY_2D_H |
475 | |