1 /*************************************************************************/
2 /*  a_star.cpp                                                           */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
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 #include "a_star.h"
32 
33 #include "core/math/geometry.h"
34 #include "core/script_language.h"
35 #include "scene/scene_string_names.h"
36 
get_available_point_id() const37 int AStar::get_available_point_id() const {
38 
39 	if (points.empty()) {
40 		return 1;
41 	}
42 
43 	// calculate our new next available point id if bigger than before or next id already contained in set of points.
44 	if (points.has(last_free_id)) {
45 		int cur_new_id = last_free_id;
46 		while (points.has(cur_new_id)) {
47 			cur_new_id++;
48 		}
49 		int &non_const = const_cast<int &>(last_free_id);
50 		non_const = cur_new_id;
51 	}
52 
53 	return last_free_id;
54 }
55 
add_point(int p_id,const Vector3 & p_pos,real_t p_weight_scale)56 void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
57 
58 	ERR_FAIL_COND(p_id < 0);
59 	ERR_FAIL_COND(p_weight_scale < 1);
60 
61 	Point *found_pt;
62 	bool p_exists = points.lookup(p_id, found_pt);
63 
64 	if (!p_exists) {
65 		Point *pt = memnew(Point);
66 		pt->id = p_id;
67 		pt->pos = p_pos;
68 		pt->weight_scale = p_weight_scale;
69 		pt->prev_point = NULL;
70 		pt->open_pass = 0;
71 		pt->closed_pass = 0;
72 		pt->enabled = true;
73 		points.set(p_id, pt);
74 	} else {
75 		found_pt->pos = p_pos;
76 		found_pt->weight_scale = p_weight_scale;
77 	}
78 }
79 
get_point_position(int p_id) const80 Vector3 AStar::get_point_position(int p_id) const {
81 
82 	Point *p;
83 	bool p_exists = points.lookup(p_id, p);
84 	ERR_FAIL_COND_V(!p_exists, Vector3());
85 
86 	return p->pos;
87 }
88 
set_point_position(int p_id,const Vector3 & p_pos)89 void AStar::set_point_position(int p_id, const Vector3 &p_pos) {
90 
91 	Point *p;
92 	bool p_exists = points.lookup(p_id, p);
93 	ERR_FAIL_COND(!p_exists);
94 
95 	p->pos = p_pos;
96 }
97 
get_point_weight_scale(int p_id) const98 real_t AStar::get_point_weight_scale(int p_id) const {
99 
100 	Point *p;
101 	bool p_exists = points.lookup(p_id, p);
102 	ERR_FAIL_COND_V(!p_exists, 0);
103 
104 	return p->weight_scale;
105 }
106 
set_point_weight_scale(int p_id,real_t p_weight_scale)107 void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) {
108 
109 	Point *p;
110 	bool p_exists = points.lookup(p_id, p);
111 	ERR_FAIL_COND(!p_exists);
112 	ERR_FAIL_COND(p_weight_scale < 1);
113 
114 	p->weight_scale = p_weight_scale;
115 }
116 
remove_point(int p_id)117 void AStar::remove_point(int p_id) {
118 
119 	Point *p;
120 	bool p_exists = points.lookup(p_id, p);
121 	ERR_FAIL_COND(!p_exists);
122 
123 	for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
124 
125 		Segment s(p_id, (*it.key));
126 		segments.erase(s);
127 
128 		(*it.value)->neighbours.remove(p->id);
129 		(*it.value)->unlinked_neighbours.remove(p->id);
130 	}
131 
132 	for (OAHashMap<int, Point *>::Iterator it = p->unlinked_neighbours.iter(); it.valid; it = p->unlinked_neighbours.next_iter(it)) {
133 
134 		Segment s(p_id, (*it.key));
135 		segments.erase(s);
136 
137 		(*it.value)->neighbours.remove(p->id);
138 		(*it.value)->unlinked_neighbours.remove(p->id);
139 	}
140 
141 	memdelete(p);
142 	points.remove(p_id);
143 	last_free_id = p_id;
144 }
145 
connect_points(int p_id,int p_with_id,bool bidirectional)146 void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
147 
148 	ERR_FAIL_COND(p_id == p_with_id);
149 
150 	Point *a;
151 	bool from_exists = points.lookup(p_id, a);
152 	ERR_FAIL_COND(!from_exists);
153 
154 	Point *b;
155 	bool to_exists = points.lookup(p_with_id, b);
156 	ERR_FAIL_COND(!to_exists);
157 
158 	a->neighbours.set(b->id, b);
159 
160 	if (bidirectional) {
161 		b->neighbours.set(a->id, a);
162 	} else {
163 		b->unlinked_neighbours.set(a->id, a);
164 	}
165 
166 	Segment s(p_id, p_with_id);
167 	if (bidirectional) s.direction = Segment::BIDIRECTIONAL;
168 
169 	Set<Segment>::Element *element = segments.find(s);
170 	if (element != NULL) {
171 		s.direction |= element->get().direction;
172 		if (s.direction == Segment::BIDIRECTIONAL) {
173 			// Both are neighbours of each other now
174 			a->unlinked_neighbours.remove(b->id);
175 			b->unlinked_neighbours.remove(a->id);
176 		}
177 		segments.erase(element);
178 	}
179 
180 	segments.insert(s);
181 }
182 
disconnect_points(int p_id,int p_with_id,bool bidirectional)183 void AStar::disconnect_points(int p_id, int p_with_id, bool bidirectional) {
184 
185 	Point *a;
186 	bool a_exists = points.lookup(p_id, a);
187 	ERR_FAIL_COND(!a_exists);
188 
189 	Point *b;
190 	bool b_exists = points.lookup(p_with_id, b);
191 	ERR_FAIL_COND(!b_exists);
192 
193 	Segment s(p_id, p_with_id);
194 	int remove_direction = bidirectional ? (int)Segment::BIDIRECTIONAL : s.direction;
195 
196 	Set<Segment>::Element *element = segments.find(s);
197 	if (element != NULL) {
198 		// s is the new segment
199 		// Erase the directions to be removed
200 		s.direction = (element->get().direction & ~remove_direction);
201 
202 		a->neighbours.remove(b->id);
203 		if (bidirectional) {
204 			b->neighbours.remove(a->id);
205 			if (element->get().direction != Segment::BIDIRECTIONAL) {
206 				a->unlinked_neighbours.remove(b->id);
207 				b->unlinked_neighbours.remove(a->id);
208 			}
209 		} else {
210 			if (s.direction == Segment::NONE)
211 				b->unlinked_neighbours.remove(a->id);
212 			else
213 				a->unlinked_neighbours.set(b->id, b);
214 		}
215 
216 		segments.erase(element);
217 		if (s.direction != Segment::NONE)
218 			segments.insert(s);
219 	}
220 }
221 
has_point(int p_id) const222 bool AStar::has_point(int p_id) const {
223 
224 	return points.has(p_id);
225 }
226 
get_points()227 Array AStar::get_points() {
228 
229 	Array point_list;
230 
231 	for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
232 		point_list.push_back(*(it.key));
233 	}
234 
235 	return point_list;
236 }
237 
get_point_connections(int p_id)238 PoolVector<int> AStar::get_point_connections(int p_id) {
239 
240 	Point *p;
241 	bool p_exists = points.lookup(p_id, p);
242 	ERR_FAIL_COND_V(!p_exists, PoolVector<int>());
243 
244 	PoolVector<int> point_list;
245 
246 	for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
247 		point_list.push_back((*it.key));
248 	}
249 
250 	return point_list;
251 }
252 
are_points_connected(int p_id,int p_with_id,bool bidirectional) const253 bool AStar::are_points_connected(int p_id, int p_with_id, bool bidirectional) const {
254 
255 	Segment s(p_id, p_with_id);
256 	const Set<Segment>::Element *element = segments.find(s);
257 
258 	return element != NULL &&
259 		   (bidirectional || (element->get().direction & s.direction) == s.direction);
260 }
261 
clear()262 void AStar::clear() {
263 
264 	last_free_id = 0;
265 	for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
266 		memdelete(*(it.value));
267 	}
268 	segments.clear();
269 	points.clear();
270 }
271 
get_point_count() const272 int AStar::get_point_count() const {
273 	return points.get_num_elements();
274 }
275 
get_point_capacity() const276 int AStar::get_point_capacity() const {
277 	return points.get_capacity();
278 }
279 
reserve_space(int p_num_nodes)280 void AStar::reserve_space(int p_num_nodes) {
281 	ERR_FAIL_COND_MSG(p_num_nodes <= 0, "New capacity must be greater than 0, was: " + itos(p_num_nodes) + ".");
282 	ERR_FAIL_COND_MSG((uint32_t)p_num_nodes < points.get_capacity(), "New capacity must be greater than current capacity: " + itos(points.get_capacity()) + ", new was: " + itos(p_num_nodes) + ".");
283 	points.reserve(p_num_nodes);
284 }
285 
get_closest_point(const Vector3 & p_point,bool p_include_disabled) const286 int AStar::get_closest_point(const Vector3 &p_point, bool p_include_disabled) const {
287 
288 	int closest_id = -1;
289 	real_t closest_dist = 1e20;
290 
291 	for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
292 
293 		if (!p_include_disabled && !(*it.value)->enabled) continue; // Disabled points should not be considered.
294 
295 		// Keep the closest point's ID, and in case of multiple closest IDs,
296 		// the smallest one (makes it deterministic).
297 		real_t d = p_point.distance_squared_to((*it.value)->pos);
298 		int id = *(it.key);
299 		if (d <= closest_dist) {
300 			if (d == closest_dist && id > closest_id) { // Keep lowest ID.
301 				continue;
302 			}
303 			closest_dist = d;
304 			closest_id = id;
305 		}
306 	}
307 
308 	return closest_id;
309 }
310 
get_closest_position_in_segment(const Vector3 & p_point) const311 Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const {
312 
313 	real_t closest_dist = 1e20;
314 	Vector3 closest_point;
315 
316 	for (const Set<Segment>::Element *E = segments.front(); E; E = E->next()) {
317 
318 		Point *from_point = nullptr, *to_point = nullptr;
319 		points.lookup(E->get().u, from_point);
320 		points.lookup(E->get().v, to_point);
321 
322 		if (!(from_point->enabled && to_point->enabled)) {
323 			continue;
324 		}
325 
326 		Vector3 segment[2] = {
327 			from_point->pos,
328 			to_point->pos,
329 		};
330 
331 		Vector3 p = Geometry::get_closest_point_to_segment(p_point, segment);
332 		real_t d = p_point.distance_squared_to(p);
333 		if (d < closest_dist) {
334 
335 			closest_point = p;
336 			closest_dist = d;
337 		}
338 	}
339 
340 	return closest_point;
341 }
342 
_solve(Point * begin_point,Point * end_point)343 bool AStar::_solve(Point *begin_point, Point *end_point) {
344 
345 	pass++;
346 
347 	if (!end_point->enabled) return false;
348 
349 	bool found_route = false;
350 
351 	Vector<Point *> open_list;
352 	SortArray<Point *, SortPoints> sorter;
353 
354 	begin_point->g_score = 0;
355 	begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
356 	open_list.push_back(begin_point);
357 
358 	while (!open_list.empty()) {
359 
360 		Point *p = open_list[0]; // The currently processed point
361 
362 		if (p == end_point) {
363 			found_route = true;
364 			break;
365 		}
366 
367 		sorter.pop_heap(0, open_list.size(), open_list.ptrw()); // Remove the current point from the open list
368 		open_list.remove(open_list.size() - 1);
369 		p->closed_pass = pass; // Mark the point as closed
370 
371 		for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
372 
373 			Point *e = *(it.value); // The neighbour point
374 
375 			if (!e->enabled || e->closed_pass == pass) {
376 				continue;
377 			}
378 
379 			real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale;
380 
381 			bool new_point = false;
382 
383 			if (e->open_pass != pass) { // The point wasn't inside the open list.
384 				e->open_pass = pass;
385 				open_list.push_back(e);
386 				new_point = true;
387 			} else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous.
388 				continue;
389 			}
390 
391 			e->prev_point = p;
392 			e->g_score = tentative_g_score;
393 			e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
394 
395 			if (new_point) { // The position of the new points is already known.
396 				sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw());
397 			} else {
398 				sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw());
399 			}
400 		}
401 	}
402 
403 	return found_route;
404 }
405 
_estimate_cost(int p_from_id,int p_to_id)406 real_t AStar::_estimate_cost(int p_from_id, int p_to_id) {
407 
408 	if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost))
409 		return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id);
410 
411 	Point *from_point;
412 	bool from_exists = points.lookup(p_from_id, from_point);
413 	ERR_FAIL_COND_V(!from_exists, 0);
414 
415 	Point *to_point;
416 	bool to_exists = points.lookup(p_to_id, to_point);
417 	ERR_FAIL_COND_V(!to_exists, 0);
418 
419 	return from_point->pos.distance_to(to_point->pos);
420 }
421 
_compute_cost(int p_from_id,int p_to_id)422 real_t AStar::_compute_cost(int p_from_id, int p_to_id) {
423 
424 	if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost))
425 		return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id);
426 
427 	Point *from_point;
428 	bool from_exists = points.lookup(p_from_id, from_point);
429 	ERR_FAIL_COND_V(!from_exists, 0);
430 
431 	Point *to_point;
432 	bool to_exists = points.lookup(p_to_id, to_point);
433 	ERR_FAIL_COND_V(!to_exists, 0);
434 
435 	return from_point->pos.distance_to(to_point->pos);
436 }
437 
get_point_path(int p_from_id,int p_to_id)438 PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
439 
440 	Point *a;
441 	bool from_exists = points.lookup(p_from_id, a);
442 	ERR_FAIL_COND_V(!from_exists, PoolVector<Vector3>());
443 
444 	Point *b;
445 	bool to_exists = points.lookup(p_to_id, b);
446 	ERR_FAIL_COND_V(!to_exists, PoolVector<Vector3>());
447 
448 	if (a == b) {
449 		PoolVector<Vector3> ret;
450 		ret.push_back(a->pos);
451 		return ret;
452 	}
453 
454 	Point *begin_point = a;
455 	Point *end_point = b;
456 
457 	bool found_route = _solve(begin_point, end_point);
458 	if (!found_route) return PoolVector<Vector3>();
459 
460 	Point *p = end_point;
461 	int pc = 1; // Begin point
462 	while (p != begin_point) {
463 		pc++;
464 		p = p->prev_point;
465 	}
466 
467 	PoolVector<Vector3> path;
468 	path.resize(pc);
469 
470 	{
471 		PoolVector<Vector3>::Write w = path.write();
472 
473 		Point *p2 = end_point;
474 		int idx = pc - 1;
475 		while (p2 != begin_point) {
476 			w[idx--] = p2->pos;
477 			p2 = p2->prev_point;
478 		}
479 
480 		w[0] = p2->pos; // Assign first
481 	}
482 
483 	return path;
484 }
485 
get_id_path(int p_from_id,int p_to_id)486 PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
487 
488 	Point *a;
489 	bool from_exists = points.lookup(p_from_id, a);
490 	ERR_FAIL_COND_V(!from_exists, PoolVector<int>());
491 
492 	Point *b;
493 	bool to_exists = points.lookup(p_to_id, b);
494 	ERR_FAIL_COND_V(!to_exists, PoolVector<int>());
495 
496 	if (a == b) {
497 		PoolVector<int> ret;
498 		ret.push_back(a->id);
499 		return ret;
500 	}
501 
502 	Point *begin_point = a;
503 	Point *end_point = b;
504 
505 	bool found_route = _solve(begin_point, end_point);
506 	if (!found_route) return PoolVector<int>();
507 
508 	Point *p = end_point;
509 	int pc = 1; // Begin point
510 	while (p != begin_point) {
511 		pc++;
512 		p = p->prev_point;
513 	}
514 
515 	PoolVector<int> path;
516 	path.resize(pc);
517 
518 	{
519 		PoolVector<int>::Write w = path.write();
520 
521 		p = end_point;
522 		int idx = pc - 1;
523 		while (p != begin_point) {
524 			w[idx--] = p->id;
525 			p = p->prev_point;
526 		}
527 
528 		w[0] = p->id; // Assign first
529 	}
530 
531 	return path;
532 }
533 
set_point_disabled(int p_id,bool p_disabled)534 void AStar::set_point_disabled(int p_id, bool p_disabled) {
535 
536 	Point *p;
537 	bool p_exists = points.lookup(p_id, p);
538 	ERR_FAIL_COND(!p_exists);
539 
540 	p->enabled = !p_disabled;
541 }
542 
is_point_disabled(int p_id) const543 bool AStar::is_point_disabled(int p_id) const {
544 
545 	Point *p;
546 	bool p_exists = points.lookup(p_id, p);
547 	ERR_FAIL_COND_V(!p_exists, false);
548 
549 	return !p->enabled;
550 }
551 
_bind_methods()552 void AStar::_bind_methods() {
553 
554 	ClassDB::bind_method(D_METHOD("get_available_point_id"), &AStar::get_available_point_id);
555 	ClassDB::bind_method(D_METHOD("add_point", "id", "position", "weight_scale"), &AStar::add_point, DEFVAL(1.0));
556 	ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStar::get_point_position);
557 	ClassDB::bind_method(D_METHOD("set_point_position", "id", "position"), &AStar::set_point_position);
558 	ClassDB::bind_method(D_METHOD("get_point_weight_scale", "id"), &AStar::get_point_weight_scale);
559 	ClassDB::bind_method(D_METHOD("set_point_weight_scale", "id", "weight_scale"), &AStar::set_point_weight_scale);
560 	ClassDB::bind_method(D_METHOD("remove_point", "id"), &AStar::remove_point);
561 	ClassDB::bind_method(D_METHOD("has_point", "id"), &AStar::has_point);
562 	ClassDB::bind_method(D_METHOD("get_point_connections", "id"), &AStar::get_point_connections);
563 	ClassDB::bind_method(D_METHOD("get_points"), &AStar::get_points);
564 
565 	ClassDB::bind_method(D_METHOD("set_point_disabled", "id", "disabled"), &AStar::set_point_disabled, DEFVAL(true));
566 	ClassDB::bind_method(D_METHOD("is_point_disabled", "id"), &AStar::is_point_disabled);
567 
568 	ClassDB::bind_method(D_METHOD("connect_points", "id", "to_id", "bidirectional"), &AStar::connect_points, DEFVAL(true));
569 	ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id", "bidirectional"), &AStar::disconnect_points, DEFVAL(true));
570 	ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id", "bidirectional"), &AStar::are_points_connected, DEFVAL(true));
571 
572 	ClassDB::bind_method(D_METHOD("get_point_count"), &AStar::get_point_count);
573 	ClassDB::bind_method(D_METHOD("get_point_capacity"), &AStar::get_point_capacity);
574 	ClassDB::bind_method(D_METHOD("reserve_space", "num_nodes"), &AStar::reserve_space);
575 	ClassDB::bind_method(D_METHOD("clear"), &AStar::clear);
576 
577 	ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar::get_closest_point, DEFVAL(false));
578 	ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar::get_closest_position_in_segment);
579 
580 	ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar::get_point_path);
581 	ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar::get_id_path);
582 
583 	BIND_VMETHOD(MethodInfo(Variant::REAL, "_estimate_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id")));
584 	BIND_VMETHOD(MethodInfo(Variant::REAL, "_compute_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id")));
585 }
586 
AStar()587 AStar::AStar() {
588 	last_free_id = 0;
589 	pass = 1;
590 }
591 
~AStar()592 AStar::~AStar() {
593 	clear();
594 }
595 
596 /////////////////////////////////////////////////////////////
597 
get_available_point_id() const598 int AStar2D::get_available_point_id() const {
599 	return astar.get_available_point_id();
600 }
601 
add_point(int p_id,const Vector2 & p_pos,real_t p_weight_scale)602 void AStar2D::add_point(int p_id, const Vector2 &p_pos, real_t p_weight_scale) {
603 	astar.add_point(p_id, Vector3(p_pos.x, p_pos.y, 0), p_weight_scale);
604 }
605 
get_point_position(int p_id) const606 Vector2 AStar2D::get_point_position(int p_id) const {
607 	Vector3 p = astar.get_point_position(p_id);
608 	return Vector2(p.x, p.y);
609 }
610 
set_point_position(int p_id,const Vector2 & p_pos)611 void AStar2D::set_point_position(int p_id, const Vector2 &p_pos) {
612 	astar.set_point_position(p_id, Vector3(p_pos.x, p_pos.y, 0));
613 }
614 
get_point_weight_scale(int p_id) const615 real_t AStar2D::get_point_weight_scale(int p_id) const {
616 	return astar.get_point_weight_scale(p_id);
617 }
618 
set_point_weight_scale(int p_id,real_t p_weight_scale)619 void AStar2D::set_point_weight_scale(int p_id, real_t p_weight_scale) {
620 	astar.set_point_weight_scale(p_id, p_weight_scale);
621 }
622 
remove_point(int p_id)623 void AStar2D::remove_point(int p_id) {
624 	astar.remove_point(p_id);
625 }
626 
has_point(int p_id) const627 bool AStar2D::has_point(int p_id) const {
628 	return astar.has_point(p_id);
629 }
630 
get_point_connections(int p_id)631 PoolVector<int> AStar2D::get_point_connections(int p_id) {
632 	return astar.get_point_connections(p_id);
633 }
634 
get_points()635 Array AStar2D::get_points() {
636 	return astar.get_points();
637 }
638 
set_point_disabled(int p_id,bool p_disabled)639 void AStar2D::set_point_disabled(int p_id, bool p_disabled) {
640 	astar.set_point_disabled(p_id, p_disabled);
641 }
642 
is_point_disabled(int p_id) const643 bool AStar2D::is_point_disabled(int p_id) const {
644 	return astar.is_point_disabled(p_id);
645 }
646 
connect_points(int p_id,int p_with_id,bool p_bidirectional)647 void AStar2D::connect_points(int p_id, int p_with_id, bool p_bidirectional) {
648 	astar.connect_points(p_id, p_with_id, p_bidirectional);
649 }
650 
disconnect_points(int p_id,int p_with_id)651 void AStar2D::disconnect_points(int p_id, int p_with_id) {
652 	astar.disconnect_points(p_id, p_with_id);
653 }
654 
are_points_connected(int p_id,int p_with_id) const655 bool AStar2D::are_points_connected(int p_id, int p_with_id) const {
656 	return astar.are_points_connected(p_id, p_with_id);
657 }
658 
get_point_count() const659 int AStar2D::get_point_count() const {
660 	return astar.get_point_count();
661 }
662 
get_point_capacity() const663 int AStar2D::get_point_capacity() const {
664 	return astar.get_point_capacity();
665 }
666 
clear()667 void AStar2D::clear() {
668 	astar.clear();
669 }
670 
reserve_space(int p_num_nodes)671 void AStar2D::reserve_space(int p_num_nodes) {
672 	astar.reserve_space(p_num_nodes);
673 }
674 
get_closest_point(const Vector2 & p_point,bool p_include_disabled) const675 int AStar2D::get_closest_point(const Vector2 &p_point, bool p_include_disabled) const {
676 	return astar.get_closest_point(Vector3(p_point.x, p_point.y, 0), p_include_disabled);
677 }
678 
get_closest_position_in_segment(const Vector2 & p_point) const679 Vector2 AStar2D::get_closest_position_in_segment(const Vector2 &p_point) const {
680 	Vector3 p = astar.get_closest_position_in_segment(Vector3(p_point.x, p_point.y, 0));
681 	return Vector2(p.x, p.y);
682 }
683 
_estimate_cost(int p_from_id,int p_to_id)684 real_t AStar2D::_estimate_cost(int p_from_id, int p_to_id) {
685 
686 	if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost))
687 		return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id);
688 
689 	AStar::Point *from_point;
690 	bool from_exists = astar.points.lookup(p_from_id, from_point);
691 	ERR_FAIL_COND_V(!from_exists, 0);
692 
693 	AStar::Point *to_point;
694 	bool to_exists = astar.points.lookup(p_to_id, to_point);
695 	ERR_FAIL_COND_V(!to_exists, 0);
696 
697 	return from_point->pos.distance_to(to_point->pos);
698 }
699 
_compute_cost(int p_from_id,int p_to_id)700 real_t AStar2D::_compute_cost(int p_from_id, int p_to_id) {
701 
702 	if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost))
703 		return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id);
704 
705 	AStar::Point *from_point;
706 	bool from_exists = astar.points.lookup(p_from_id, from_point);
707 	ERR_FAIL_COND_V(!from_exists, 0);
708 
709 	AStar::Point *to_point;
710 	bool to_exists = astar.points.lookup(p_to_id, to_point);
711 	ERR_FAIL_COND_V(!to_exists, 0);
712 
713 	return from_point->pos.distance_to(to_point->pos);
714 }
715 
get_point_path(int p_from_id,int p_to_id)716 PoolVector<Vector2> AStar2D::get_point_path(int p_from_id, int p_to_id) {
717 
718 	AStar::Point *a;
719 	bool from_exists = astar.points.lookup(p_from_id, a);
720 	ERR_FAIL_COND_V(!from_exists, PoolVector<Vector2>());
721 
722 	AStar::Point *b;
723 	bool to_exists = astar.points.lookup(p_to_id, b);
724 	ERR_FAIL_COND_V(!to_exists, PoolVector<Vector2>());
725 
726 	if (a == b) {
727 		PoolVector<Vector2> ret;
728 		ret.push_back(Vector2(a->pos.x, a->pos.y));
729 		return ret;
730 	}
731 
732 	AStar::Point *begin_point = a;
733 	AStar::Point *end_point = b;
734 
735 	bool found_route = _solve(begin_point, end_point);
736 	if (!found_route) return PoolVector<Vector2>();
737 
738 	AStar::Point *p = end_point;
739 	int pc = 1; // Begin point
740 	while (p != begin_point) {
741 		pc++;
742 		p = p->prev_point;
743 	}
744 
745 	PoolVector<Vector2> path;
746 	path.resize(pc);
747 
748 	{
749 		PoolVector<Vector2>::Write w = path.write();
750 
751 		AStar::Point *p2 = end_point;
752 		int idx = pc - 1;
753 		while (p2 != begin_point) {
754 			w[idx--] = Vector2(p2->pos.x, p2->pos.y);
755 			p2 = p2->prev_point;
756 		}
757 
758 		w[0] = Vector2(p2->pos.x, p2->pos.y); // Assign first
759 	}
760 
761 	return path;
762 }
763 
get_id_path(int p_from_id,int p_to_id)764 PoolVector<int> AStar2D::get_id_path(int p_from_id, int p_to_id) {
765 
766 	AStar::Point *a;
767 	bool from_exists = astar.points.lookup(p_from_id, a);
768 	ERR_FAIL_COND_V(!from_exists, PoolVector<int>());
769 
770 	AStar::Point *b;
771 	bool to_exists = astar.points.lookup(p_to_id, b);
772 	ERR_FAIL_COND_V(!to_exists, PoolVector<int>());
773 
774 	if (a == b) {
775 		PoolVector<int> ret;
776 		ret.push_back(a->id);
777 		return ret;
778 	}
779 
780 	AStar::Point *begin_point = a;
781 	AStar::Point *end_point = b;
782 
783 	bool found_route = _solve(begin_point, end_point);
784 	if (!found_route) return PoolVector<int>();
785 
786 	AStar::Point *p = end_point;
787 	int pc = 1; // Begin point
788 	while (p != begin_point) {
789 		pc++;
790 		p = p->prev_point;
791 	}
792 
793 	PoolVector<int> path;
794 	path.resize(pc);
795 
796 	{
797 		PoolVector<int>::Write w = path.write();
798 
799 		p = end_point;
800 		int idx = pc - 1;
801 		while (p != begin_point) {
802 			w[idx--] = p->id;
803 			p = p->prev_point;
804 		}
805 
806 		w[0] = p->id; // Assign first
807 	}
808 
809 	return path;
810 }
811 
_solve(AStar::Point * begin_point,AStar::Point * end_point)812 bool AStar2D::_solve(AStar::Point *begin_point, AStar::Point *end_point) {
813 
814 	astar.pass++;
815 
816 	if (!end_point->enabled) return false;
817 
818 	bool found_route = false;
819 
820 	Vector<AStar::Point *> open_list;
821 	SortArray<AStar::Point *, AStar::SortPoints> sorter;
822 
823 	begin_point->g_score = 0;
824 	begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
825 	open_list.push_back(begin_point);
826 
827 	while (!open_list.empty()) {
828 
829 		AStar::Point *p = open_list[0]; // The currently processed point
830 
831 		if (p == end_point) {
832 			found_route = true;
833 			break;
834 		}
835 
836 		sorter.pop_heap(0, open_list.size(), open_list.ptrw()); // Remove the current point from the open list
837 		open_list.remove(open_list.size() - 1);
838 		p->closed_pass = astar.pass; // Mark the point as closed
839 
840 		for (OAHashMap<int, AStar::Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
841 
842 			AStar::Point *e = *(it.value); // The neighbour point
843 
844 			if (!e->enabled || e->closed_pass == astar.pass) {
845 				continue;
846 			}
847 
848 			real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale;
849 
850 			bool new_point = false;
851 
852 			if (e->open_pass != astar.pass) { // The point wasn't inside the open list.
853 				e->open_pass = astar.pass;
854 				open_list.push_back(e);
855 				new_point = true;
856 			} else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous.
857 				continue;
858 			}
859 
860 			e->prev_point = p;
861 			e->g_score = tentative_g_score;
862 			e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
863 
864 			if (new_point) { // The position of the new points is already known.
865 				sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw());
866 			} else {
867 				sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw());
868 			}
869 		}
870 	}
871 
872 	return found_route;
873 }
874 
_bind_methods()875 void AStar2D::_bind_methods() {
876 
877 	ClassDB::bind_method(D_METHOD("get_available_point_id"), &AStar2D::get_available_point_id);
878 	ClassDB::bind_method(D_METHOD("add_point", "id", "position", "weight_scale"), &AStar2D::add_point, DEFVAL(1.0));
879 	ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStar2D::get_point_position);
880 	ClassDB::bind_method(D_METHOD("set_point_position", "id", "position"), &AStar2D::set_point_position);
881 	ClassDB::bind_method(D_METHOD("get_point_weight_scale", "id"), &AStar2D::get_point_weight_scale);
882 	ClassDB::bind_method(D_METHOD("set_point_weight_scale", "id", "weight_scale"), &AStar2D::set_point_weight_scale);
883 	ClassDB::bind_method(D_METHOD("remove_point", "id"), &AStar2D::remove_point);
884 	ClassDB::bind_method(D_METHOD("has_point", "id"), &AStar2D::has_point);
885 	ClassDB::bind_method(D_METHOD("get_point_connections", "id"), &AStar2D::get_point_connections);
886 	ClassDB::bind_method(D_METHOD("get_points"), &AStar2D::get_points);
887 
888 	ClassDB::bind_method(D_METHOD("set_point_disabled", "id", "disabled"), &AStar2D::set_point_disabled, DEFVAL(true));
889 	ClassDB::bind_method(D_METHOD("is_point_disabled", "id"), &AStar2D::is_point_disabled);
890 
891 	ClassDB::bind_method(D_METHOD("connect_points", "id", "to_id", "bidirectional"), &AStar2D::connect_points, DEFVAL(true));
892 	ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id"), &AStar2D::disconnect_points);
893 	ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id"), &AStar2D::are_points_connected);
894 
895 	ClassDB::bind_method(D_METHOD("get_point_count"), &AStar2D::get_point_count);
896 	ClassDB::bind_method(D_METHOD("get_point_capacity"), &AStar2D::get_point_capacity);
897 	ClassDB::bind_method(D_METHOD("reserve_space", "num_nodes"), &AStar2D::reserve_space);
898 	ClassDB::bind_method(D_METHOD("clear"), &AStar2D::clear);
899 
900 	ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar2D::get_closest_point, DEFVAL(false));
901 	ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar2D::get_closest_position_in_segment);
902 
903 	ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::get_point_path);
904 	ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::get_id_path);
905 
906 	BIND_VMETHOD(MethodInfo(Variant::REAL, "_estimate_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id")));
907 	BIND_VMETHOD(MethodInfo(Variant::REAL, "_compute_cost", PropertyInfo(Variant::INT, "from_id"), PropertyInfo(Variant::INT, "to_id")));
908 }
909 
AStar2D()910 AStar2D::AStar2D() {
911 }
912 
~AStar2D()913 AStar2D::~AStar2D() {
914 }
915