| 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 |  | 
|---|