1 /**
2 An implementation of A* to find the shortest paths among connected points in space.
3 
4 Copyright:
5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.  
6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)  
7 Copyright (c) 2017-2018 Godot-D contributors  
8 
9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
10 
11 
12 */
13 module godot.astar;
14 import std.meta : AliasSeq, staticIndexOf;
15 import std.traits : Unqual;
16 import godot.d.traits;
17 import godot.core;
18 import godot.c;
19 import godot.d.bind;
20 import godot.d.reference;
21 import godot.globalenums;
22 import godot.object;
23 import godot.classdb;
24 import godot.reference;
25 /**
26 An implementation of A* to find the shortest paths among connected points in space.
27 
28 A* (A star) is a computer algorithm that is widely used in pathfinding and graph traversal, the process of plotting short paths among vertices (points), passing through a given set of edges (segments). It enjoys widespread use due to its performance and accuracy. Godot's A* implementation uses points in three-dimensional space and Euclidean distances by default.
29 You must add points manually with $(D addPoint) and create segments manually with $(D connectPoints). Then you can test if there is a path between two points with the $(D arePointsConnected) function, get a path containing indices by $(D getIdPath), or one containing actual coordinates with $(D getPointPath).
30 It is also possible to use non-Euclidean distances. To do so, create a class that extends `AStar` and override methods $(D _computeCost) and $(D _estimateCost). Both take two indices and return a length, as is shown in the following example.
31 
32 
33 class MyAStar:
34     extends AStar
35 
36     func _compute_cost(u, v):
37         return abs(u - v)
38 
39     func _estimate_cost(u, v):
40         return min(0, abs(u - v) - 1)
41 
42 
43 $(D _estimateCost) should return a lower bound of the distance, i.e. `_estimate_cost(u, v) <= _compute_cost(u, v)`. This serves as a hint to the algorithm because the custom `_compute_cost` might be computation-heavy. If this is not the case, make $(D _estimateCost) return the same value as $(D _computeCost) to provide the algorithm with the most accurate information.
44 If the default $(D _estimateCost) and $(D _computeCost) methods are used, or if the supplied $(D _estimateCost) method returns a lower bound of the cost, then the paths returned by A* will be the lowest-cost paths. Here, the cost of a path equals the sum of the $(D _computeCost) results of all segments in the path multiplied by the `weight_scale`s of the endpoints of the respective segments. If the default methods are used and the `weight_scale`s of all points are set to `1.0`, then this equals the sum of Euclidean distances of all segments in the path.
45 */
46 @GodotBaseClass struct AStar
47 {
48 	package(godot) enum string _GODOT_internal_name = "AStar";
49 public:
50 @nogc nothrow:
51 	union { /** */ godot_object _godot_object; /** */ Reference _GODOT_base; }
52 	alias _GODOT_base this;
53 	alias BaseClasses = AliasSeq!(typeof(_GODOT_base), typeof(_GODOT_base).BaseClasses);
54 	package(godot) __gshared bool _classBindingInitialized = false;
55 	package(godot) static struct GDNativeClassBinding
56 	{
57 		__gshared:
58 		@GodotName("_compute_cost") GodotMethod!(double, long, long) _computeCost;
59 		@GodotName("_estimate_cost") GodotMethod!(double, long, long) _estimateCost;
60 		@GodotName("add_point") GodotMethod!(void, long, Vector3, double) addPoint;
61 		@GodotName("are_points_connected") GodotMethod!(bool, long, long, bool) arePointsConnected;
62 		@GodotName("clear") GodotMethod!(void) clear;
63 		@GodotName("connect_points") GodotMethod!(void, long, long, bool) connectPoints;
64 		@GodotName("disconnect_points") GodotMethod!(void, long, long, bool) disconnectPoints;
65 		@GodotName("get_available_point_id") GodotMethod!(long) getAvailablePointId;
66 		@GodotName("get_closest_point") GodotMethod!(long, Vector3, bool) getClosestPoint;
67 		@GodotName("get_closest_position_in_segment") GodotMethod!(Vector3, Vector3) getClosestPositionInSegment;
68 		@GodotName("get_id_path") GodotMethod!(PoolIntArray, long, long) getIdPath;
69 		@GodotName("get_point_capacity") GodotMethod!(long) getPointCapacity;
70 		@GodotName("get_point_connections") GodotMethod!(PoolIntArray, long) getPointConnections;
71 		@GodotName("get_point_count") GodotMethod!(long) getPointCount;
72 		@GodotName("get_point_path") GodotMethod!(PoolVector3Array, long, long) getPointPath;
73 		@GodotName("get_point_position") GodotMethod!(Vector3, long) getPointPosition;
74 		@GodotName("get_point_weight_scale") GodotMethod!(double, long) getPointWeightScale;
75 		@GodotName("get_points") GodotMethod!(Array) getPoints;
76 		@GodotName("has_point") GodotMethod!(bool, long) hasPoint;
77 		@GodotName("is_point_disabled") GodotMethod!(bool, long) isPointDisabled;
78 		@GodotName("remove_point") GodotMethod!(void, long) removePoint;
79 		@GodotName("reserve_space") GodotMethod!(void, long) reserveSpace;
80 		@GodotName("set_point_disabled") GodotMethod!(void, long, bool) setPointDisabled;
81 		@GodotName("set_point_position") GodotMethod!(void, long, Vector3) setPointPosition;
82 		@GodotName("set_point_weight_scale") GodotMethod!(void, long, double) setPointWeightScale;
83 	}
84 	/// 
85 	pragma(inline, true) bool opEquals(in AStar other) const
86 	{ return _godot_object.ptr is other._godot_object.ptr; }
87 	/// 
88 	pragma(inline, true) typeof(null) opAssign(typeof(null) n)
89 	{ _godot_object.ptr = n; return null; }
90 	/// 
91 	pragma(inline, true) bool opEquals(typeof(null) n) const
92 	{ return _godot_object.ptr is n; }
93 	/// 
94 	size_t toHash() const @trusted { return cast(size_t)_godot_object.ptr; }
95 	mixin baseCasts;
96 	/// Construct a new instance of AStar.
97 	/// Note: use `memnew!AStar` instead.
98 	static AStar _new()
99 	{
100 		static godot_class_constructor constructor;
101 		if(constructor is null) constructor = _godot_api.godot_get_class_constructor("AStar");
102 		if(constructor is null) return typeof(this).init;
103 		return cast(AStar)(constructor());
104 	}
105 	@disable new(size_t s);
106 	/**
107 	Called when computing the cost between two connected points.
108 	Note that this function is hidden in the default `AStar` class.
109 	*/
110 	double _computeCost(in long from_id, in long to_id)
111 	{
112 		Array _GODOT_args = Array.make();
113 		_GODOT_args.append(from_id);
114 		_GODOT_args.append(to_id);
115 		String _GODOT_method_name = String("_compute_cost");
116 		return this.callv(_GODOT_method_name, _GODOT_args).as!(RefOrT!double);
117 	}
118 	/**
119 	Called when estimating the cost between a point and the path's ending point.
120 	Note that this function is hidden in the default `AStar` class.
121 	*/
122 	double _estimateCost(in long from_id, in long to_id)
123 	{
124 		Array _GODOT_args = Array.make();
125 		_GODOT_args.append(from_id);
126 		_GODOT_args.append(to_id);
127 		String _GODOT_method_name = String("_estimate_cost");
128 		return this.callv(_GODOT_method_name, _GODOT_args).as!(RefOrT!double);
129 	}
130 	/**
131 	Adds a new point at the given position with the given identifier. The `id` must be 0 or larger, and the `weight_scale` must be 1 or larger.
132 	The `weight_scale` is multiplied by the result of $(D _computeCost) when determining the overall cost of traveling across a segment from a neighboring point to this point. Thus, all else being equal, the algorithm prefers points with lower `weight_scale`s to form a path.
133 	
134 	
135 	var astar = AStar.new()
136 	astar.add_point(1, Vector3(1, 0, 0), 4) # Adds the point (1, 0, 0) with weight_scale 4 and id 1
137 	
138 	
139 	If there already exists a point for the given `id`, its position and weight scale are updated to the given values.
140 	*/
141 	void addPoint(in long id, in Vector3 position, in double weight_scale = 1)
142 	{
143 		checkClassBinding!(typeof(this))();
144 		ptrcall!(void)(GDNativeClassBinding.addPoint, _godot_object, id, position, weight_scale);
145 	}
146 	/**
147 	Returns whether the two given points are directly connected by a segment. If `bidirectional` is `false`, returns whether movement from `id` to `to_id` is possible through this segment.
148 	*/
149 	bool arePointsConnected(in long id, in long to_id, in bool bidirectional = true) const
150 	{
151 		checkClassBinding!(typeof(this))();
152 		return ptrcall!(bool)(GDNativeClassBinding.arePointsConnected, _godot_object, id, to_id, bidirectional);
153 	}
154 	/**
155 	Clears all the points and segments.
156 	*/
157 	void clear()
158 	{
159 		checkClassBinding!(typeof(this))();
160 		ptrcall!(void)(GDNativeClassBinding.clear, _godot_object);
161 	}
162 	/**
163 	Creates a segment between the given points. If `bidirectional` is `false`, only movement from `id` to `to_id` is allowed, not the reverse direction.
164 	
165 	
166 	var astar = AStar.new()
167 	astar.add_point(1, Vector3(1, 1, 0))
168 	astar.add_point(2, Vector3(0, 5, 0))
169 	astar.connect_points(1, 2, false)
170 	
171 	
172 	*/
173 	void connectPoints(in long id, in long to_id, in bool bidirectional = true)
174 	{
175 		checkClassBinding!(typeof(this))();
176 		ptrcall!(void)(GDNativeClassBinding.connectPoints, _godot_object, id, to_id, bidirectional);
177 	}
178 	/**
179 	Deletes the segment between the given points. If `bidirectional` is `false`, only movement from `id` to `to_id` is prevented, and a unidirectional segment possibly remains.
180 	*/
181 	void disconnectPoints(in long id, in long to_id, in bool bidirectional = true)
182 	{
183 		checkClassBinding!(typeof(this))();
184 		ptrcall!(void)(GDNativeClassBinding.disconnectPoints, _godot_object, id, to_id, bidirectional);
185 	}
186 	/**
187 	Returns the next available point ID with no point associated to it.
188 	*/
189 	long getAvailablePointId() const
190 	{
191 		checkClassBinding!(typeof(this))();
192 		return ptrcall!(long)(GDNativeClassBinding.getAvailablePointId, _godot_object);
193 	}
194 	/**
195 	Returns the ID of the closest point to `to_position`, optionally taking disabled points into account. Returns `-1` if there are no points in the points pool.
196 	$(B Note:) If several points are the closest to `to_position`, the one with the smallest ID will be returned, ensuring a deterministic result.
197 	*/
198 	long getClosestPoint(in Vector3 to_position, in bool include_disabled = false) const
199 	{
200 		checkClassBinding!(typeof(this))();
201 		return ptrcall!(long)(GDNativeClassBinding.getClosestPoint, _godot_object, to_position, include_disabled);
202 	}
203 	/**
204 	Returns the closest position to `to_position` that resides inside a segment between two connected points.
205 	
206 	
207 	var astar = AStar.new()
208 	astar.add_point(1, Vector3(0, 0, 0))
209 	astar.add_point(2, Vector3(0, 5, 0))
210 	astar.connect_points(1, 2)
211 	var res = astar.get_closest_position_in_segment(Vector3(3, 3, 0)) # Returns (0, 3, 0)
212 	
213 	
214 	The result is in the segment that goes from `y = 0` to `y = 5`. It's the closest position in the segment to the given point.
215 	*/
216 	Vector3 getClosestPositionInSegment(in Vector3 to_position) const
217 	{
218 		checkClassBinding!(typeof(this))();
219 		return ptrcall!(Vector3)(GDNativeClassBinding.getClosestPositionInSegment, _godot_object, to_position);
220 	}
221 	/**
222 	Returns an array with the IDs of the points that form the path found by AStar between the given points. The array is ordered from the starting point to the ending point of the path.
223 	
224 	
225 	var astar = AStar.new()
226 	astar.add_point(1, Vector3(0, 0, 0))
227 	astar.add_point(2, Vector3(0, 1, 0), 1) # Default weight is 1
228 	astar.add_point(3, Vector3(1, 1, 0))
229 	astar.add_point(4, Vector3(2, 0, 0))
230 	
231 	astar.connect_points(1, 2, false)
232 	astar.connect_points(2, 3, false)
233 	astar.connect_points(4, 3, false)
234 	astar.connect_points(1, 4, false)
235 	
236 	var res = astar.get_id_path(1, 3) # Returns $(D 1, 2, 3)
237 	
238 	
239 	If you change the 2nd point's weight to 3, then the result will be `$(D 1, 4, 3)` instead, because now even though the distance is longer, it's "easier" to get through point 4 than through point 2.
240 	*/
241 	PoolIntArray getIdPath(in long from_id, in long to_id)
242 	{
243 		checkClassBinding!(typeof(this))();
244 		return ptrcall!(PoolIntArray)(GDNativeClassBinding.getIdPath, _godot_object, from_id, to_id);
245 	}
246 	/**
247 	Returns the capacity of the structure backing the points, useful in conjunction with `reserve_space`.
248 	*/
249 	long getPointCapacity() const
250 	{
251 		checkClassBinding!(typeof(this))();
252 		return ptrcall!(long)(GDNativeClassBinding.getPointCapacity, _godot_object);
253 	}
254 	/**
255 	Returns an array with the IDs of the points that form the connection with the given point.
256 	
257 	
258 	var astar = AStar.new()
259 	astar.add_point(1, Vector3(0, 0, 0))
260 	astar.add_point(2, Vector3(0, 1, 0))
261 	astar.add_point(3, Vector3(1, 1, 0))
262 	astar.add_point(4, Vector3(2, 0, 0))
263 	
264 	astar.connect_points(1, 2, true)
265 	astar.connect_points(1, 3, true)
266 	
267 	var neighbors = astar.get_point_connections(1) # Returns $(D 2, 3)
268 	
269 	
270 	*/
271 	PoolIntArray getPointConnections(in long id)
272 	{
273 		checkClassBinding!(typeof(this))();
274 		return ptrcall!(PoolIntArray)(GDNativeClassBinding.getPointConnections, _godot_object, id);
275 	}
276 	/**
277 	Returns the number of points currently in the points pool.
278 	*/
279 	long getPointCount() const
280 	{
281 		checkClassBinding!(typeof(this))();
282 		return ptrcall!(long)(GDNativeClassBinding.getPointCount, _godot_object);
283 	}
284 	/**
285 	Returns an array with the points that are in the path found by AStar between the given points. The array is ordered from the starting point to the ending point of the path.
286 	$(B Note:) This method is not thread-safe. If called from a $(D Thread), it will return an empty $(D PoolVector3Array) and will print an error message.
287 	*/
288 	PoolVector3Array getPointPath(in long from_id, in long to_id)
289 	{
290 		checkClassBinding!(typeof(this))();
291 		return ptrcall!(PoolVector3Array)(GDNativeClassBinding.getPointPath, _godot_object, from_id, to_id);
292 	}
293 	/**
294 	Returns the position of the point associated with the given `id`.
295 	*/
296 	Vector3 getPointPosition(in long id) const
297 	{
298 		checkClassBinding!(typeof(this))();
299 		return ptrcall!(Vector3)(GDNativeClassBinding.getPointPosition, _godot_object, id);
300 	}
301 	/**
302 	Returns the weight scale of the point associated with the given `id`.
303 	*/
304 	double getPointWeightScale(in long id) const
305 	{
306 		checkClassBinding!(typeof(this))();
307 		return ptrcall!(double)(GDNativeClassBinding.getPointWeightScale, _godot_object, id);
308 	}
309 	/**
310 	Returns an array of all points.
311 	*/
312 	Array getPoints()
313 	{
314 		checkClassBinding!(typeof(this))();
315 		return ptrcall!(Array)(GDNativeClassBinding.getPoints, _godot_object);
316 	}
317 	/**
318 	Returns whether a point associated with the given `id` exists.
319 	*/
320 	bool hasPoint(in long id) const
321 	{
322 		checkClassBinding!(typeof(this))();
323 		return ptrcall!(bool)(GDNativeClassBinding.hasPoint, _godot_object, id);
324 	}
325 	/**
326 	Returns whether a point is disabled or not for pathfinding. By default, all points are enabled.
327 	*/
328 	bool isPointDisabled(in long id) const
329 	{
330 		checkClassBinding!(typeof(this))();
331 		return ptrcall!(bool)(GDNativeClassBinding.isPointDisabled, _godot_object, id);
332 	}
333 	/**
334 	Removes the point associated with the given `id` from the points pool.
335 	*/
336 	void removePoint(in long id)
337 	{
338 		checkClassBinding!(typeof(this))();
339 		ptrcall!(void)(GDNativeClassBinding.removePoint, _godot_object, id);
340 	}
341 	/**
342 	Reserves space internally for `num_nodes` points, useful if you're adding a known large number of points at once, for a grid for instance. New capacity must be greater or equals to old capacity.
343 	*/
344 	void reserveSpace(in long num_nodes)
345 	{
346 		checkClassBinding!(typeof(this))();
347 		ptrcall!(void)(GDNativeClassBinding.reserveSpace, _godot_object, num_nodes);
348 	}
349 	/**
350 	Disables or enables the specified point for pathfinding. Useful for making a temporary obstacle.
351 	*/
352 	void setPointDisabled(in long id, in bool disabled = true)
353 	{
354 		checkClassBinding!(typeof(this))();
355 		ptrcall!(void)(GDNativeClassBinding.setPointDisabled, _godot_object, id, disabled);
356 	}
357 	/**
358 	Sets the `position` for the point with the given `id`.
359 	*/
360 	void setPointPosition(in long id, in Vector3 position)
361 	{
362 		checkClassBinding!(typeof(this))();
363 		ptrcall!(void)(GDNativeClassBinding.setPointPosition, _godot_object, id, position);
364 	}
365 	/**
366 	Sets the `weight_scale` for the point with the given `id`. The `weight_scale` is multiplied by the result of $(D _computeCost) when determining the overall cost of traveling across a segment from a neighboring point to this point.
367 	*/
368 	void setPointWeightScale(in long id, in double weight_scale)
369 	{
370 		checkClassBinding!(typeof(this))();
371 		ptrcall!(void)(GDNativeClassBinding.setPointWeightScale, _godot_object, id, weight_scale);
372 	}
373 }