tcod/
pathfinding.rs

1use bindings::ffi;
2use bindings::{AsNative, c_bool, c_float, c_int, c_void};
3use map::Map;
4
5enum PathInnerData<'a> {
6    Map(Map),
7    Callback(Box<FnMut((i32, i32), (i32, i32)) -> f32+'a>),
8}
9
10pub struct AStar<'a>{
11    tcod_path: ffi::TCOD_path_t,
12    #[allow(dead_code)]
13    inner: PathInnerData<'a>,
14    width: i32,
15    height: i32,
16}
17
18impl<'a> AsNative<ffi::TCOD_path_t> for AStar<'a> {
19    unsafe fn as_native(&self) -> &ffi::TCOD_path_t {
20        &self.tcod_path
21    }
22    
23    unsafe fn as_native_mut(&mut self) -> &mut ffi::TCOD_path_t {
24        &mut self.tcod_path
25    }
26}
27
28impl<'a> Drop for AStar<'a> {
29    fn drop(&mut self) {
30        unsafe {
31            ffi::TCOD_path_delete(self.tcod_path);
32        }
33    }
34}
35
36extern "C" fn c_path_callback<T: FnMut((i32, i32), (i32, i32)) -> f32>(xf: c_int, yf: c_int,
37                          xt: c_int, yt: c_int,
38                          user_data: *mut c_void) -> c_float {
39    let callback_ptr = user_data as *mut T;
40    let cb: &mut T = unsafe {
41        &mut *callback_ptr
42    };
43    cb((xf, yf), (xt, yt))
44}
45
46impl<'a> AStar<'a> {
47    pub fn new_from_callback<T: 'a+FnMut((i32, i32), (i32, i32)) -> f32>(
48        width: i32, height: i32, path_callback: T,
49        diagonal_cost: f32) -> AStar<'a> {
50        let callback = Box::new(path_callback);
51        let user_data = &*callback as *const T as *mut c_void;
52        unsafe {
53            let tcod_path = ffi::TCOD_path_new_using_function(width, height,
54                                                              Some(c_path_callback::<T>),
55                                                              user_data,
56                                                              diagonal_cost);
57            AStar {
58                tcod_path: tcod_path,
59                // We need to keep user_closure around, otherwise it
60                // would get deallocated at the end of this function.
61                inner: PathInnerData::Callback(callback),
62                width: width,
63                height: height,
64            }
65        }
66    }
67
68    pub fn new_from_map(map: Map, diagonal_cost: f32) -> AStar<'static> {
69        let tcod_path = unsafe {
70            ffi::TCOD_path_new_using_map(*map.as_native(), diagonal_cost)
71        };
72        let (w, h) = map.size();
73        AStar {
74            tcod_path: tcod_path,
75            inner: PathInnerData::Map(map),
76            width: w,
77            height: h,
78        }
79    }
80
81    pub fn find(&mut self,
82                from: (i32, i32),
83                to: (i32, i32)) -> bool {
84        let (from_x, from_y) = from;
85        let (to_x, to_y) = to;
86        assert!(from_x >= 0 && from_y >= 0 && to_x >= 0 && to_y >= 0);
87        assert!(from_x < self.width && from_y < self.height && to_x < self.width && to_y < self.height);
88        unsafe {
89            ffi::TCOD_path_compute(self.tcod_path,
90                                   from_x, from_y,
91                                   to_x, to_y) != 0
92        }
93    }
94
95    pub fn iter(&'a self) -> AStarPathIter<'a> {
96        AStarPathIter { current: -1, path: self }
97    }
98
99    pub fn walk(&mut self) -> AStarIterator {
100        AStarIterator{tcod_path: self.tcod_path, recalculate: false}
101    }
102
103    pub fn walk_recalculate(&mut self) -> AStarIterator {
104        AStarIterator{tcod_path: self.tcod_path, recalculate: true}
105    }
106
107    pub fn walk_one_step(&mut self, recalculate_when_needed: bool) -> Option<(i32, i32)> {
108        unsafe {
109            let mut x: c_int = 0;
110            let mut y: c_int = 0;
111            match ffi::TCOD_path_walk(self.tcod_path, &mut x, &mut y,
112                                      recalculate_when_needed as c_bool) != 0 {
113                true => Some((x, y)),
114                false => None,
115            }
116        }
117    }
118
119    pub fn reverse(&mut self) {
120        unsafe {
121            ffi::TCOD_path_reverse(self.tcod_path)
122        }
123    }
124
125    pub fn origin(&self) -> (isize, isize) {
126        unsafe {
127            let mut x: c_int = 0;
128            let mut y: c_int = 0;
129            ffi::TCOD_path_get_origin(self.tcod_path, &mut x, &mut y);
130            (x as isize, y as isize)
131        }
132    }
133
134    pub fn destination(&self) -> (isize, isize) {
135        unsafe {
136            let mut x: c_int = 0;
137            let mut y: c_int = 0;
138            ffi::TCOD_path_get_destination(self.tcod_path, &mut x, &mut y);
139            (x as isize, y as isize)
140        }
141    }
142
143    pub fn get(&self, index: i32) -> Option<(i32, i32)> {
144        if index < 0 || index >= self.len() {
145            return None;
146        }
147        unsafe {
148            let mut x: c_int = 0;
149            let mut y: c_int = 0;
150            ffi::TCOD_path_get(self.tcod_path, index, &mut x, &mut y);
151            (Some((x, y)))
152        }
153    }
154
155    pub fn is_empty(&self) -> bool {
156        unsafe {
157            ffi::TCOD_path_is_empty(self.tcod_path) != 0
158        }
159    }
160
161    pub fn len(&self) -> i32 {
162        unsafe {
163            ffi::TCOD_path_size(self.tcod_path)
164        }
165    }
166}
167
168pub struct Dijkstra<'a> {
169    tcod_path: ffi::TCOD_dijkstra_t,
170    #[allow(dead_code)]
171    inner: PathInnerData<'a>,
172    width: i32,
173    height: i32,
174}
175
176impl<'a> AsNative<ffi::TCOD_path_t> for Dijkstra<'a> {
177    unsafe fn as_native(&self) -> &ffi::TCOD_dijkstra_t {
178        &self.tcod_path
179    }
180    
181    unsafe fn as_native_mut(&mut self) -> &mut ffi::TCOD_dijkstra_t {
182        &mut self.tcod_path
183    }
184}
185
186impl<'a> Drop for Dijkstra<'a> {
187    fn drop(&mut self) {
188        unsafe {
189            ffi::TCOD_dijkstra_delete(self.tcod_path);
190        }
191    }
192}
193
194impl<'a> Dijkstra<'a> {
195    pub fn new_from_callback<T: 'a+FnMut((i32, i32), (i32, i32)) -> f32>(
196        width: i32, height: i32,
197        path_callback: T,
198        diagonal_cost: f32) -> Dijkstra<'a> {
199        let callback = Box::new(path_callback);
200        let user_data = &*callback as *const T as *mut c_void;
201        unsafe {
202            let tcod_path = ffi::TCOD_dijkstra_new_using_function(width,
203                                                                  height,
204                                                                  Some(c_path_callback::<T>),
205                                                                  user_data,
206                                                                  diagonal_cost);
207            Dijkstra {
208                tcod_path: tcod_path,
209                inner: PathInnerData::Callback(callback),
210                width: width,
211                height: height,
212            }
213        }
214    }
215
216    pub fn new_from_map(map: Map, diagonal_cost: f32) -> Dijkstra<'static> {
217        let tcod_path = unsafe {
218            ffi::TCOD_dijkstra_new(*map.as_native(), diagonal_cost)
219        };
220        let (w, h) = map.size();
221        Dijkstra {
222            tcod_path: tcod_path,
223            inner: PathInnerData::Map(map),
224            width: w,
225            height: h,
226        }
227    }
228
229    pub fn compute_grid(&mut self, root: (i32, i32)) {
230        let (x, y) = root;
231        assert!(x >= 0 && y >= 0 && x < self.width && y < self.height);
232        unsafe {
233            ffi::TCOD_dijkstra_compute(self.tcod_path, x, y);
234        }
235    }
236
237    pub fn find(&mut self, destination: (i32, i32)) -> bool {
238        let (x, y) = destination;
239        if x >= 0 && y >= 0 && x < self.width && y < self.height {
240            unsafe {
241                ffi::TCOD_dijkstra_path_set(self.tcod_path, x, y) != 0
242            }
243        } else {
244            false
245        }
246    }
247
248    pub fn iter(&'a self) -> DijkstraPathIter<'a> {
249        DijkstraPathIter { current: -1, path: self }
250    }
251
252    pub fn walk(&mut self) -> DijkstraIterator {
253        DijkstraIterator{tcod_path: self.tcod_path}
254    }
255
256    pub fn walk_one_step(&mut self) -> Option<(i32, i32)> {
257        unsafe {
258            let mut x: c_int = 0;
259            let mut y: c_int = 0;
260            match ffi::TCOD_dijkstra_path_walk(self.tcod_path, &mut x, &mut y) != 0 {
261                true => Some((x, y)),
262                false => None,
263            }
264        }
265    }
266
267
268    pub fn distance_from_root(&self, point: (i32, i32)) -> Option<f32> {
269        let (x, y) = point;
270        let result = unsafe {
271            ffi::TCOD_dijkstra_get_distance(self.tcod_path, x, y)
272        };
273        if result == -1.0 {
274            None
275        } else {
276            Some(result)
277        }
278    }
279
280    pub fn reverse(&mut self) {
281        unsafe {
282            ffi::TCOD_dijkstra_reverse(self.tcod_path);
283        }
284    }
285
286    pub fn get(&self, index: i32) -> Option<(i32, i32)> {
287        if index < 0 || index >= self.len() {
288            return None;
289        }
290        unsafe {
291            let mut x: c_int = 0;
292            let mut y: c_int = 0;
293            ffi::TCOD_dijkstra_get(self.tcod_path, index, &mut x, &mut y);
294            Some((x, y))
295        }
296    }
297
298    pub fn is_empty(&self) -> bool {
299        unsafe {
300            ffi::TCOD_dijkstra_is_empty(self.tcod_path) != 0
301        }
302    }
303
304    pub fn len(&self) -> i32 {
305        unsafe {
306            ffi::TCOD_dijkstra_size(self.tcod_path)
307        }
308    }
309}
310
311pub struct AStarIterator {
312    tcod_path: ffi::TCOD_path_t,
313    recalculate: bool,
314}
315
316impl Iterator for AStarIterator {
317    type Item = (i32, i32);
318
319    fn next(&mut self) -> Option<(i32, i32)> {
320        unsafe {
321            let mut x: c_int = 0;
322            let mut y: c_int = 0;
323            match ffi::TCOD_path_walk(self.tcod_path, &mut x, &mut y,
324                                      self.recalculate as c_bool) != 0 {
325                true => Some((x, y)),
326                false => None,
327            }
328        }
329    }
330}
331
332pub struct DijkstraIterator {
333    tcod_path: ffi::TCOD_path_t,
334}
335
336impl Iterator for DijkstraIterator {
337    type Item = (i32, i32);
338
339    fn next(&mut self) -> Option<(i32, i32)> {
340        unsafe {
341            let mut x: c_int = 0;
342            let mut y: c_int = 0;
343            match ffi::TCOD_dijkstra_path_walk(self.tcod_path, &mut x, &mut y) != 0 {
344                true => Some((x, y)),
345                false => None,
346            }
347        }
348    }
349}
350
351pub struct AStarPathIter<'a> {
352    current: i32,
353    path: &'a AStar<'a>,
354}
355
356impl<'a> Iterator for AStarPathIter<'a> {
357    type Item = (i32, i32);
358
359    fn next(&mut self) -> Option<Self::Item> {
360        if self.current == self.path.len() - 1 {
361            None
362        } else {
363            self.current += 1;
364            unsafe {
365                let mut x: c_int = 0;
366                let mut y: c_int = 0;
367                ffi::TCOD_path_get(*self.path.as_native(), self.current, &mut x, &mut y);
368                Some((x, y))
369            }
370        }
371    }
372}
373
374pub struct DijkstraPathIter<'a> {
375    current: i32,
376    path: &'a Dijkstra<'a>,
377}
378
379impl<'a> Iterator for DijkstraPathIter<'a> {
380    type Item = (i32, i32);
381
382    fn next(&mut self) -> Option<Self::Item> {
383        if self.current == self.path.len() - 1 {
384            None
385        } else {
386            self.current += 1;
387            unsafe {
388                let mut x: c_int = 0;
389                let mut y: c_int = 0;
390                ffi::TCOD_dijkstra_get(*self.path.as_native(), self.current, &mut x, &mut y);
391                Some((x, y))
392            }
393        }
394    }
395}