polygon_path_finder.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /**************************************************************************/
  2. /* polygon_path_finder.cpp */
  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. #include "polygon_path_finder.h"
  31. #include "core/math/geometry_2d.h"
  32. bool PolygonPathFinder::_is_point_inside(const Vector2 &p_point) const {
  33. int crosses = 0;
  34. for (const Edge &E : edges) {
  35. const Edge &e = E;
  36. Vector2 a = points[e.points[0]].pos;
  37. Vector2 b = points[e.points[1]].pos;
  38. if (Geometry2D::segment_intersects_segment(a, b, p_point, outside_point, nullptr)) {
  39. crosses++;
  40. }
  41. }
  42. return crosses & 1;
  43. }
  44. void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> &p_connections) {
  45. ERR_FAIL_COND(p_connections.size() & 1);
  46. points.clear();
  47. edges.clear();
  48. //insert points
  49. int point_count = p_points.size();
  50. points.resize(point_count + 2);
  51. bounds = Rect2();
  52. for (int i = 0; i < p_points.size(); i++) {
  53. points.write[i].pos = p_points[i];
  54. points.write[i].penalty = 0;
  55. outside_point = i == 0 ? p_points[0] : p_points[i].max(outside_point);
  56. if (i == 0) {
  57. bounds.position = points[i].pos;
  58. } else {
  59. bounds.expand_to(points[i].pos);
  60. }
  61. }
  62. outside_point.x += 20.451 + Math::randf() * 10.2039;
  63. outside_point.y += 21.193 + Math::randf() * 12.5412;
  64. //insert edges (which are also connections)
  65. for (int i = 0; i < p_connections.size(); i += 2) {
  66. Edge e(p_connections[i], p_connections[i + 1]);
  67. ERR_FAIL_INDEX(e.points[0], point_count);
  68. ERR_FAIL_INDEX(e.points[1], point_count);
  69. points.write[p_connections[i]].connections.insert(p_connections[i + 1]);
  70. points.write[p_connections[i + 1]].connections.insert(p_connections[i]);
  71. edges.insert(e);
  72. }
  73. //fill the remaining connections based on visibility
  74. for (int i = 0; i < point_count; i++) {
  75. for (int j = i + 1; j < point_count; j++) {
  76. if (edges.has(Edge(i, j))) {
  77. continue; //if in edge ignore
  78. }
  79. Vector2 from = points[i].pos;
  80. Vector2 to = points[j].pos;
  81. if (!_is_point_inside(from * 0.5 + to * 0.5)) { //connection between points in inside space
  82. continue;
  83. }
  84. bool valid = true;
  85. for (const Edge &E : edges) {
  86. const Edge &e = E;
  87. if (e.points[0] == i || e.points[1] == i || e.points[0] == j || e.points[1] == j) {
  88. continue;
  89. }
  90. Vector2 a = points[e.points[0]].pos;
  91. Vector2 b = points[e.points[1]].pos;
  92. if (Geometry2D::segment_intersects_segment(a, b, from, to, nullptr)) {
  93. valid = false;
  94. break;
  95. }
  96. }
  97. if (valid) {
  98. points.write[i].connections.insert(j);
  99. points.write[j].connections.insert(i);
  100. }
  101. }
  102. }
  103. }
  104. Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector2 &p_to) {
  105. Vector<Vector2> path;
  106. Vector2 from = p_from;
  107. Vector2 to = p_to;
  108. Edge ignore_from_edge(-1, -1);
  109. Edge ignore_to_edge(-1, -1);
  110. if (!_is_point_inside(from)) {
  111. float closest_dist = 1e20f;
  112. Vector2 closest_point;
  113. for (const Edge &E : edges) {
  114. const Edge &e = E;
  115. const Vector2 segment_a = points[e.points[0]].pos;
  116. const Vector2 segment_b = points[e.points[1]].pos;
  117. Vector2 closest = Geometry2D::get_closest_point_to_segment(from, segment_a, segment_b);
  118. float d = from.distance_squared_to(closest);
  119. if (d < closest_dist) {
  120. ignore_from_edge = E;
  121. closest_dist = d;
  122. closest_point = closest;
  123. }
  124. }
  125. from = closest_point;
  126. };
  127. if (!_is_point_inside(to)) {
  128. float closest_dist = 1e20f;
  129. Vector2 closest_point;
  130. for (const Edge &E : edges) {
  131. const Edge &e = E;
  132. const Vector2 segment_a = points[e.points[0]].pos;
  133. const Vector2 segment_b = points[e.points[1]].pos;
  134. Vector2 closest = Geometry2D::get_closest_point_to_segment(to, segment_a, segment_b);
  135. float d = to.distance_squared_to(closest);
  136. if (d < closest_dist) {
  137. ignore_to_edge = E;
  138. closest_dist = d;
  139. closest_point = closest;
  140. }
  141. }
  142. to = closest_point;
  143. };
  144. //test direct connection
  145. {
  146. bool can_see_eachother = true;
  147. for (const Edge &E : edges) {
  148. const Edge &e = E;
  149. if (e.points[0] == ignore_from_edge.points[0] && e.points[1] == ignore_from_edge.points[1]) {
  150. continue;
  151. }
  152. if (e.points[0] == ignore_to_edge.points[0] && e.points[1] == ignore_to_edge.points[1]) {
  153. continue;
  154. }
  155. Vector2 a = points[e.points[0]].pos;
  156. Vector2 b = points[e.points[1]].pos;
  157. if (Geometry2D::segment_intersects_segment(a, b, from, to, nullptr)) {
  158. can_see_eachother = false;
  159. break;
  160. }
  161. }
  162. if (can_see_eachother) {
  163. path.push_back(from);
  164. path.push_back(to);
  165. return path;
  166. }
  167. }
  168. //add to graph
  169. int aidx = points.size() - 2;
  170. int bidx = points.size() - 1;
  171. points.write[aidx].pos = from;
  172. points.write[bidx].pos = to;
  173. points.write[aidx].distance = 0;
  174. points.write[bidx].distance = 0;
  175. points.write[aidx].prev = -1;
  176. points.write[bidx].prev = -1;
  177. points.write[aidx].penalty = 0;
  178. points.write[bidx].penalty = 0;
  179. for (int i = 0; i < points.size() - 2; i++) {
  180. bool valid_a = true;
  181. bool valid_b = true;
  182. points.write[i].prev = -1;
  183. points.write[i].distance = 0;
  184. if (!_is_point_inside(from * 0.5 + points[i].pos * 0.5)) {
  185. valid_a = false;
  186. }
  187. if (!_is_point_inside(to * 0.5 + points[i].pos * 0.5)) {
  188. valid_b = false;
  189. }
  190. for (const Edge &E : edges) {
  191. const Edge &e = E;
  192. if (e.points[0] == i || e.points[1] == i) {
  193. continue;
  194. }
  195. Vector2 a = points[e.points[0]].pos;
  196. Vector2 b = points[e.points[1]].pos;
  197. if (valid_a) {
  198. if (e.points[0] != ignore_from_edge.points[1] &&
  199. e.points[1] != ignore_from_edge.points[1] &&
  200. e.points[0] != ignore_from_edge.points[0] &&
  201. e.points[1] != ignore_from_edge.points[0]) {
  202. if (Geometry2D::segment_intersects_segment(a, b, from, points[i].pos, nullptr)) {
  203. valid_a = false;
  204. }
  205. }
  206. }
  207. if (valid_b) {
  208. if (e.points[0] != ignore_to_edge.points[1] &&
  209. e.points[1] != ignore_to_edge.points[1] &&
  210. e.points[0] != ignore_to_edge.points[0] &&
  211. e.points[1] != ignore_to_edge.points[0]) {
  212. if (Geometry2D::segment_intersects_segment(a, b, to, points[i].pos, nullptr)) {
  213. valid_b = false;
  214. }
  215. }
  216. }
  217. if (!valid_a && !valid_b) {
  218. break;
  219. }
  220. }
  221. if (valid_a) {
  222. points.write[i].connections.insert(aidx);
  223. points.write[aidx].connections.insert(i);
  224. }
  225. if (valid_b) {
  226. points.write[i].connections.insert(bidx);
  227. points.write[bidx].connections.insert(i);
  228. }
  229. }
  230. //solve graph
  231. HashSet<int> open_list;
  232. points.write[aidx].distance = 0;
  233. points.write[aidx].prev = aidx;
  234. for (const int &E : points[aidx].connections) {
  235. open_list.insert(E);
  236. points.write[E].distance = from.distance_to(points[E].pos);
  237. points.write[E].prev = aidx;
  238. }
  239. bool found_route = false;
  240. while (true) {
  241. if (open_list.is_empty()) {
  242. print_verbose("Open list empty.");
  243. break;
  244. }
  245. //check open list
  246. int least_cost_point = -1;
  247. float least_cost = 1e30;
  248. //this could be faster (cache previous results)
  249. for (const int &E : open_list) {
  250. const Point &p = points[E];
  251. float cost = p.distance;
  252. cost += p.pos.distance_to(to);
  253. cost += p.penalty;
  254. if (cost < least_cost) {
  255. least_cost_point = E;
  256. least_cost = cost;
  257. }
  258. }
  259. const Point &np = points[least_cost_point];
  260. //open the neighbors for search
  261. for (const int &E : np.connections) {
  262. Point &p = points.write[E];
  263. float distance = np.pos.distance_to(p.pos) + np.distance;
  264. if (p.prev != -1) {
  265. //oh this was visited already, can we win the cost?
  266. if (p.distance > distance) {
  267. p.prev = least_cost_point; //reassign previous
  268. p.distance = distance;
  269. }
  270. } else {
  271. //add to open neighbors
  272. p.prev = least_cost_point;
  273. p.distance = distance;
  274. open_list.insert(E);
  275. if (E == bidx) {
  276. //oh my reached end! stop algorithm
  277. found_route = true;
  278. break;
  279. }
  280. }
  281. }
  282. if (found_route) {
  283. break;
  284. }
  285. open_list.erase(least_cost_point);
  286. }
  287. if (found_route) {
  288. int at = bidx;
  289. path.push_back(points[at].pos);
  290. do {
  291. at = points[at].prev;
  292. path.push_back(points[at].pos);
  293. } while (at != aidx);
  294. path.reverse();
  295. }
  296. for (int i = 0; i < points.size() - 2; i++) {
  297. points.write[i].connections.erase(aidx);
  298. points.write[i].connections.erase(bidx);
  299. points.write[i].prev = -1;
  300. points.write[i].distance = 0;
  301. }
  302. points.write[aidx].connections.clear();
  303. points.write[aidx].prev = -1;
  304. points.write[aidx].distance = 0;
  305. points.write[bidx].connections.clear();
  306. points.write[bidx].prev = -1;
  307. points.write[bidx].distance = 0;
  308. return path;
  309. }
  310. void PolygonPathFinder::_set_data(const Dictionary &p_data) {
  311. ERR_FAIL_COND(!p_data.has("points"));
  312. ERR_FAIL_COND(!p_data.has("connections"));
  313. ERR_FAIL_COND(!p_data.has("segments"));
  314. ERR_FAIL_COND(!p_data.has("bounds"));
  315. Vector<Vector2> p = p_data["points"];
  316. Array c = p_data["connections"];
  317. ERR_FAIL_COND(c.size() != p.size());
  318. if (c.size()) {
  319. return;
  320. }
  321. int pc = p.size();
  322. points.resize(pc + 2);
  323. const Vector2 *pr = p.ptr();
  324. for (int i = 0; i < pc; i++) {
  325. points.write[i].pos = pr[i];
  326. Vector<int> con = c[i];
  327. const int *cr = con.ptr();
  328. int cc = con.size();
  329. for (int j = 0; j < cc; j++) {
  330. points.write[i].connections.insert(cr[j]);
  331. }
  332. }
  333. if (p_data.has("penalties")) {
  334. Vector<real_t> penalties = p_data["penalties"];
  335. if (penalties.size() == pc) {
  336. const real_t *pr2 = penalties.ptr();
  337. for (int i = 0; i < pc; i++) {
  338. points.write[i].penalty = pr2[i];
  339. }
  340. }
  341. }
  342. Vector<int> segs = p_data["segments"];
  343. int sc = segs.size();
  344. ERR_FAIL_COND(sc & 1);
  345. const int *sr = segs.ptr();
  346. for (int i = 0; i < sc; i += 2) {
  347. Edge e(sr[i], sr[i + 1]);
  348. edges.insert(e);
  349. }
  350. bounds = p_data["bounds"];
  351. }
  352. Dictionary PolygonPathFinder::_get_data() const {
  353. Dictionary d;
  354. Vector<Vector2> p;
  355. Vector<int> ind;
  356. Array path_connections;
  357. p.resize(MAX(0, points.size() - 2));
  358. path_connections.resize(MAX(0, points.size() - 2));
  359. ind.resize(edges.size() * 2);
  360. Vector<real_t> penalties;
  361. penalties.resize(MAX(0, points.size() - 2));
  362. {
  363. Vector2 *wp = p.ptrw();
  364. real_t *pw = penalties.ptrw();
  365. for (int i = 0; i < points.size() - 2; i++) {
  366. wp[i] = points[i].pos;
  367. pw[i] = points[i].penalty;
  368. Vector<int> c;
  369. c.resize(points[i].connections.size());
  370. {
  371. int *cw = c.ptrw();
  372. int idx = 0;
  373. for (const int &E : points[i].connections) {
  374. cw[idx++] = E;
  375. }
  376. }
  377. path_connections[i] = c;
  378. }
  379. }
  380. {
  381. int *iw = ind.ptrw();
  382. int idx = 0;
  383. for (const Edge &E : edges) {
  384. iw[idx++] = E.points[0];
  385. iw[idx++] = E.points[1];
  386. }
  387. }
  388. d["bounds"] = bounds;
  389. d["points"] = p;
  390. d["penalties"] = penalties;
  391. d["connections"] = path_connections;
  392. d["segments"] = ind;
  393. return d;
  394. }
  395. bool PolygonPathFinder::is_point_inside(const Vector2 &p_point) const {
  396. return _is_point_inside(p_point);
  397. }
  398. Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const {
  399. float closest_dist = 1e20f;
  400. Vector2 closest_point;
  401. for (const Edge &E : edges) {
  402. const Edge &e = E;
  403. const Vector2 segment_a = points[e.points[0]].pos;
  404. const Vector2 segment_b = points[e.points[1]].pos;
  405. Vector2 closest = Geometry2D::get_closest_point_to_segment(p_point, segment_a, segment_b);
  406. float d = p_point.distance_squared_to(closest);
  407. if (d < closest_dist) {
  408. closest_dist = d;
  409. closest_point = closest;
  410. }
  411. }
  412. ERR_FAIL_COND_V(Math::is_equal_approx(closest_dist, 1e20f), Vector2());
  413. return closest_point;
  414. }
  415. Vector<Vector2> PolygonPathFinder::get_intersections(const Vector2 &p_from, const Vector2 &p_to) const {
  416. Vector<Vector2> inters;
  417. for (const Edge &E : edges) {
  418. Vector2 a = points[E.points[0]].pos;
  419. Vector2 b = points[E.points[1]].pos;
  420. Vector2 res;
  421. if (Geometry2D::segment_intersects_segment(a, b, p_from, p_to, &res)) {
  422. inters.push_back(res);
  423. }
  424. }
  425. return inters;
  426. }
  427. Rect2 PolygonPathFinder::get_bounds() const {
  428. return bounds;
  429. }
  430. void PolygonPathFinder::set_point_penalty(int p_point, float p_penalty) {
  431. ERR_FAIL_INDEX(p_point, points.size() - 2);
  432. points.write[p_point].penalty = p_penalty;
  433. }
  434. float PolygonPathFinder::get_point_penalty(int p_point) const {
  435. ERR_FAIL_INDEX_V(p_point, points.size() - 2, 0);
  436. return points[p_point].penalty;
  437. }
  438. void PolygonPathFinder::_bind_methods() {
  439. ClassDB::bind_method(D_METHOD("setup", "points", "connections"), &PolygonPathFinder::setup);
  440. ClassDB::bind_method(D_METHOD("find_path", "from", "to"), &PolygonPathFinder::find_path);
  441. ClassDB::bind_method(D_METHOD("get_intersections", "from", "to"), &PolygonPathFinder::get_intersections);
  442. ClassDB::bind_method(D_METHOD("get_closest_point", "point"), &PolygonPathFinder::get_closest_point);
  443. ClassDB::bind_method(D_METHOD("is_point_inside", "point"), &PolygonPathFinder::is_point_inside);
  444. ClassDB::bind_method(D_METHOD("set_point_penalty", "idx", "penalty"), &PolygonPathFinder::set_point_penalty);
  445. ClassDB::bind_method(D_METHOD("get_point_penalty", "idx"), &PolygonPathFinder::get_point_penalty);
  446. ClassDB::bind_method(D_METHOD("get_bounds"), &PolygonPathFinder::get_bounds);
  447. ClassDB::bind_method(D_METHOD("_set_data", "data"), &PolygonPathFinder::_set_data);
  448. ClassDB::bind_method(D_METHOD("_get_data"), &PolygonPathFinder::_get_data);
  449. ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
  450. }
  451. PolygonPathFinder::PolygonPathFinder() {
  452. }