1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 /** \file
18  * \ingroup bmesh
19  *
20  * Find a path between 2 elements.
21  *
22  * \note All 3 functions are similar, changes to one most likely apply to another.
23  */
24 
25 #include "MEM_guardedalloc.h"
26 
27 #include "BLI_heap_simple.h"
28 #include "BLI_linklist.h"
29 #include "BLI_math.h"
30 
31 #include "bmesh.h"
32 #include "bmesh_path.h" /* own include */
33 
34 #define COST_INIT_MAX FLT_MAX
35 
36 /* -------------------------------------------------------------------- */
37 /** \name Generic Helpers
38  * \{ */
39 
40 /**
41  * Use skip options when we want to start measuring from a boundary.
42  */
step_cost_3_v3_ex(const float v1[3],const float v2[3],const float v3[3],bool skip_12,bool skip_23)43 static float step_cost_3_v3_ex(
44     const float v1[3], const float v2[3], const float v3[3], bool skip_12, bool skip_23)
45 {
46   float d1[3], d2[3];
47 
48   /* The cost is based on the simple sum of the length of the two edges. */
49   sub_v3_v3v3(d1, v2, v1);
50   sub_v3_v3v3(d2, v3, v2);
51   const float cost_12 = normalize_v3(d1);
52   const float cost_23 = normalize_v3(d2);
53   const float cost = ((skip_12 ? 0.0f : cost_12) + (skip_23 ? 0.0f : cost_23));
54 
55   /* But is biased to give higher values to sharp turns, so that it will take paths with
56    * fewer "turns" when selecting between equal-weighted paths between the two edges. */
57   return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v3v3(d1, d2)))));
58 }
59 
step_cost_3_v3(const float v1[3],const float v2[3],const float v3[3])60 static float step_cost_3_v3(const float v1[3], const float v2[3], const float v3[3])
61 {
62   return step_cost_3_v3_ex(v1, v2, v3, false, false);
63 }
64 
65 /** \} */
66 
67 /* -------------------------------------------------------------------- */
68 /** \name BM_mesh_calc_path_vert
69  * \{ */
70 
verttag_add_adjacent(HeapSimple * heap,BMVert * v_a,BMVert ** verts_prev,float * cost,const struct BMCalcPathParams * params)71 static void verttag_add_adjacent(HeapSimple *heap,
72                                  BMVert *v_a,
73                                  BMVert **verts_prev,
74                                  float *cost,
75                                  const struct BMCalcPathParams *params)
76 {
77   const int v_a_index = BM_elem_index_get(v_a);
78 
79   {
80     BMIter eiter;
81     BMEdge *e;
82     /* Loop over faces of face, but do so by first looping over loops. */
83     BM_ITER_ELEM (e, &eiter, v_a, BM_EDGES_OF_VERT) {
84       BMVert *v_b = BM_edge_other_vert(e, v_a);
85       if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
86         /* We know 'v_b' is not visited, check it out! */
87         const int v_b_index = BM_elem_index_get(v_b);
88         const float cost_cut = params->use_topology_distance ? 1.0f : len_v3v3(v_a->co, v_b->co);
89         const float cost_new = cost[v_a_index] + cost_cut;
90 
91         if (cost[v_b_index] > cost_new) {
92           cost[v_b_index] = cost_new;
93           verts_prev[v_b_index] = v_a;
94           BLI_heapsimple_insert(heap, cost_new, v_b);
95         }
96       }
97     }
98   }
99 
100   if (params->use_step_face) {
101     BMIter liter;
102     BMLoop *l;
103     /* Loop over faces of face, but do so by first looping over loops. */
104     BM_ITER_ELEM (l, &liter, v_a, BM_LOOPS_OF_VERT) {
105       if (l->f->len > 3) {
106         /* Skip loops on adjacent edges. */
107         BMLoop *l_iter = l->next->next;
108         do {
109           BMVert *v_b = l_iter->v;
110           if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
111             /* We know 'v_b' is not visited, check it out! */
112             const int v_b_index = BM_elem_index_get(v_b);
113             const float cost_cut = params->use_topology_distance ? 1.0f :
114                                                                    len_v3v3(v_a->co, v_b->co);
115             const float cost_new = cost[v_a_index] + cost_cut;
116 
117             if (cost[v_b_index] > cost_new) {
118               cost[v_b_index] = cost_new;
119               verts_prev[v_b_index] = v_a;
120               BLI_heapsimple_insert(heap, cost_new, v_b);
121             }
122           }
123         } while ((l_iter = l_iter->next) != l->prev);
124       }
125     }
126   }
127 }
128 
BM_mesh_calc_path_vert(BMesh * bm,BMVert * v_src,BMVert * v_dst,const struct BMCalcPathParams * params,bool (* filter_fn)(BMVert *,void * user_data),void * user_data)129 LinkNode *BM_mesh_calc_path_vert(BMesh *bm,
130                                  BMVert *v_src,
131                                  BMVert *v_dst,
132                                  const struct BMCalcPathParams *params,
133                                  bool (*filter_fn)(BMVert *, void *user_data),
134                                  void *user_data)
135 {
136   LinkNode *path = NULL;
137   /* #BM_ELEM_TAG flag is used to store visited edges. */
138   BMVert *v;
139   BMIter viter;
140   HeapSimple *heap;
141   float *cost;
142   BMVert **verts_prev;
143   int i, totvert;
144 
145   /* Note, would pass #BM_EDGE except we are looping over all faces anyway. */
146   // BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); // NOT NEEDED FOR FACETAG
147 
148   BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
149     BM_elem_flag_set(v, BM_ELEM_TAG, !filter_fn(v, user_data));
150     BM_elem_index_set(v, i); /* set_inline */
151   }
152   bm->elem_index_dirty &= ~BM_VERT;
153 
154   /* Allocate. */
155   totvert = bm->totvert;
156   verts_prev = MEM_callocN(sizeof(*verts_prev) * totvert, __func__);
157   cost = MEM_mallocN(sizeof(*cost) * totvert, __func__);
158 
159   copy_vn_fl(cost, totvert, COST_INIT_MAX);
160 
161   /*
162    * Arrays are now filled as follows:
163    *
164    * As the search continues, `verts_prev[n]` will be the previous verts on the shortest
165    * path found so far to face `n`. #BM_ELEM_TAG is used to tag elements we have visited,
166    * `cost[n]` will contain the length of the shortest
167    * path to face n found so far, Finally, heap is a priority heap which is built on the
168    * the same data as the cost array, but inverted: it is a work-list of faces prioritized
169    * by the shortest path found so far to the face.
170    */
171 
172   /* Regular dijkstra shortest path, but over faces instead of vertices. */
173   heap = BLI_heapsimple_new();
174   BLI_heapsimple_insert(heap, 0.0f, v_src);
175   cost[BM_elem_index_get(v_src)] = 0.0f;
176 
177   while (!BLI_heapsimple_is_empty(heap)) {
178     v = BLI_heapsimple_pop_min(heap);
179 
180     if (v == v_dst) {
181       break;
182     }
183 
184     if (!BM_elem_flag_test(v, BM_ELEM_TAG)) {
185       BM_elem_flag_enable(v, BM_ELEM_TAG);
186       verttag_add_adjacent(heap, v, verts_prev, cost, params);
187     }
188   }
189 
190   if (v == v_dst) {
191     do {
192       BLI_linklist_prepend(&path, v);
193     } while ((v = verts_prev[BM_elem_index_get(v)]));
194   }
195 
196   MEM_freeN(verts_prev);
197   MEM_freeN(cost);
198   BLI_heapsimple_free(heap, NULL);
199 
200   return path;
201 }
202 
203 /** \} */
204 
205 /* -------------------------------------------------------------------- */
206 /** \name BM_mesh_calc_path_edge
207  * \{ */
208 
edgetag_cut_cost_vert(BMEdge * e_a,BMEdge * e_b,BMVert * v)209 static float edgetag_cut_cost_vert(BMEdge *e_a, BMEdge *e_b, BMVert *v)
210 {
211   BMVert *v1 = BM_edge_other_vert(e_a, v);
212   BMVert *v2 = BM_edge_other_vert(e_b, v);
213   return step_cost_3_v3(v1->co, v->co, v2->co);
214 }
215 
edgetag_cut_cost_face(BMEdge * e_a,BMEdge * e_b,BMFace * f)216 static float edgetag_cut_cost_face(BMEdge *e_a, BMEdge *e_b, BMFace *f)
217 {
218   float e_a_cent[3], e_b_cent[3], f_cent[3];
219 
220   mid_v3_v3v3(e_a_cent, e_a->v1->co, e_a->v1->co);
221   mid_v3_v3v3(e_b_cent, e_b->v1->co, e_b->v1->co);
222 
223   BM_face_calc_center_median_weighted(f, f_cent);
224 
225   return step_cost_3_v3(e_a_cent, e_b_cent, f_cent);
226 }
227 
edgetag_add_adjacent(HeapSimple * heap,BMEdge * e_a,BMEdge ** edges_prev,float * cost,const struct BMCalcPathParams * params)228 static void edgetag_add_adjacent(HeapSimple *heap,
229                                  BMEdge *e_a,
230                                  BMEdge **edges_prev,
231                                  float *cost,
232                                  const struct BMCalcPathParams *params)
233 {
234   const int e_a_index = BM_elem_index_get(e_a);
235 
236   /* Unlike vert/face, stepping faces disables scanning connected edges
237    * and only steps over faces (selecting a ring of edges instead of a loop). */
238   if (params->use_step_face == false || e_a->l == NULL) {
239     BMIter viter;
240     BMVert *v;
241 
242     BMIter eiter;
243     BMEdge *e_b;
244 
245     BM_ITER_ELEM (v, &viter, e_a, BM_VERTS_OF_EDGE) {
246 
247       /* Don't walk over previous vertex. */
248       if ((edges_prev[e_a_index]) && (BM_vert_in_edge(edges_prev[e_a_index], v))) {
249         continue;
250       }
251 
252       BM_ITER_ELEM (e_b, &eiter, v, BM_EDGES_OF_VERT) {
253         if (!BM_elem_flag_test(e_b, BM_ELEM_TAG)) {
254           /* We know 'e_b' is not visited, check it out! */
255           const int e_b_index = BM_elem_index_get(e_b);
256           const float cost_cut = params->use_topology_distance ?
257                                      1.0f :
258                                      edgetag_cut_cost_vert(e_a, e_b, v);
259           const float cost_new = cost[e_a_index] + cost_cut;
260 
261           if (cost[e_b_index] > cost_new) {
262             cost[e_b_index] = cost_new;
263             edges_prev[e_b_index] = e_a;
264             BLI_heapsimple_insert(heap, cost_new, e_b);
265           }
266         }
267       }
268     }
269   }
270   else {
271     BMLoop *l_first, *l_iter;
272 
273     l_iter = l_first = e_a->l;
274     do {
275       BMLoop *l_cycle_iter, *l_cycle_end;
276 
277       l_cycle_iter = l_iter->next;
278       l_cycle_end = l_iter;
279 
280       /* Good, but we need to allow this otherwise paths may fail to connect at all. */
281 #if 0
282       if (l_iter->f->len > 3) {
283         l_cycle_iter = l_cycle_iter->next;
284         l_cycle_end = l_cycle_end->prev;
285       }
286 #endif
287 
288       do {
289         BMEdge *e_b = l_cycle_iter->e;
290         if (!BM_elem_flag_test(e_b, BM_ELEM_TAG)) {
291           /* We know 'e_b' is not visited, check it out! */
292           const int e_b_index = BM_elem_index_get(e_b);
293           const float cost_cut = params->use_topology_distance ?
294                                      1.0f :
295                                      edgetag_cut_cost_face(e_a, e_b, l_iter->f);
296           const float cost_new = cost[e_a_index] + cost_cut;
297 
298           if (cost[e_b_index] > cost_new) {
299             cost[e_b_index] = cost_new;
300             edges_prev[e_b_index] = e_a;
301             BLI_heapsimple_insert(heap, cost_new, e_b);
302           }
303         }
304       } while ((l_cycle_iter = l_cycle_iter->next) != l_cycle_end);
305     } while ((l_iter = l_iter->radial_next) != l_first);
306   }
307 }
308 
BM_mesh_calc_path_edge(BMesh * bm,BMEdge * e_src,BMEdge * e_dst,const struct BMCalcPathParams * params,bool (* filter_fn)(BMEdge *,void * user_data),void * user_data)309 LinkNode *BM_mesh_calc_path_edge(BMesh *bm,
310                                  BMEdge *e_src,
311                                  BMEdge *e_dst,
312                                  const struct BMCalcPathParams *params,
313                                  bool (*filter_fn)(BMEdge *, void *user_data),
314                                  void *user_data)
315 {
316   LinkNode *path = NULL;
317   /* #BM_ELEM_TAG flag is used to store visited edges. */
318   BMEdge *e;
319   BMIter eiter;
320   HeapSimple *heap;
321   float *cost;
322   BMEdge **edges_prev;
323   int i, totedge;
324 
325   /* Note, would pass #BM_EDGE except we are looping over all edges anyway. */
326   BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */);
327 
328   BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
329     BM_elem_flag_set(e, BM_ELEM_TAG, !filter_fn(e, user_data));
330     BM_elem_index_set(e, i); /* set_inline */
331   }
332   bm->elem_index_dirty &= ~BM_EDGE;
333 
334   /* Allocate. */
335   totedge = bm->totedge;
336   edges_prev = MEM_callocN(sizeof(*edges_prev) * totedge, __func__);
337   cost = MEM_mallocN(sizeof(*cost) * totedge, __func__);
338 
339   copy_vn_fl(cost, totedge, COST_INIT_MAX);
340 
341   /*
342    * Arrays are now filled as follows:
343    *
344    * As the search continues, `edges_prev[n]` will be the previous edge on the shortest
345    * path found so far to edge `n`. #BM_ELEM_TAG is used to tag elements we have visited,
346    * `cost[n]` will contain the length of the shortest
347    * path to edge n found so far, Finally, heap is a priority heap which is built on the
348    * the same data as the cost array, but inverted: it is a work-list of edges prioritized
349    * by the shortest path found so far to the edge.
350    */
351 
352   /* Regular dijkstra shortest path, but over edges instead of vertices. */
353   heap = BLI_heapsimple_new();
354   BLI_heapsimple_insert(heap, 0.0f, e_src);
355   cost[BM_elem_index_get(e_src)] = 0.0f;
356 
357   while (!BLI_heapsimple_is_empty(heap)) {
358     e = BLI_heapsimple_pop_min(heap);
359 
360     if (e == e_dst) {
361       break;
362     }
363 
364     if (!BM_elem_flag_test(e, BM_ELEM_TAG)) {
365       BM_elem_flag_enable(e, BM_ELEM_TAG);
366       edgetag_add_adjacent(heap, e, edges_prev, cost, params);
367     }
368   }
369 
370   if (e == e_dst) {
371     do {
372       BLI_linklist_prepend(&path, e);
373     } while ((e = edges_prev[BM_elem_index_get(e)]));
374   }
375 
376   MEM_freeN(edges_prev);
377   MEM_freeN(cost);
378   BLI_heapsimple_free(heap, NULL);
379 
380   return path;
381 }
382 
383 /** \} */
384 
385 /* -------------------------------------------------------------------- */
386 /** \name BM_mesh_calc_path_face
387  * \{ */
388 
facetag_cut_cost_edge(BMFace * f_a,BMFace * f_b,BMEdge * e,const void * const f_endpoints[2])389 static float facetag_cut_cost_edge(BMFace *f_a,
390                                    BMFace *f_b,
391                                    BMEdge *e,
392                                    const void *const f_endpoints[2])
393 {
394   float f_a_cent[3];
395   float f_b_cent[3];
396   float e_cent[3];
397 
398   BM_face_calc_center_median_weighted(f_a, f_a_cent);
399   BM_face_calc_center_median_weighted(f_b, f_b_cent);
400 #if 0
401   mid_v3_v3v3(e_cent, e->v1->co, e->v2->co);
402 #else
403   /* For triangle fans it gives better results to pick a point on the edge. */
404   {
405     float ix_e[3], ix_f[3];
406     isect_line_line_v3(e->v1->co, e->v2->co, f_a_cent, f_b_cent, ix_e, ix_f);
407     const float factor = line_point_factor_v3(ix_e, e->v1->co, e->v2->co);
408     if (factor < 0.0f) {
409       copy_v3_v3(e_cent, e->v1->co);
410     }
411     else if (factor > 1.0f) {
412       copy_v3_v3(e_cent, e->v2->co);
413     }
414     else {
415       copy_v3_v3(e_cent, ix_e);
416     }
417   }
418 #endif
419 
420   return step_cost_3_v3_ex(
421       f_a_cent, e_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
422 }
423 
facetag_cut_cost_vert(BMFace * f_a,BMFace * f_b,BMVert * v,const void * const f_endpoints[2])424 static float facetag_cut_cost_vert(BMFace *f_a,
425                                    BMFace *f_b,
426                                    BMVert *v,
427                                    const void *const f_endpoints[2])
428 {
429   float f_a_cent[3];
430   float f_b_cent[3];
431 
432   BM_face_calc_center_median_weighted(f_a, f_a_cent);
433   BM_face_calc_center_median_weighted(f_b, f_b_cent);
434 
435   return step_cost_3_v3_ex(
436       f_a_cent, v->co, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
437 }
438 
facetag_add_adjacent(HeapSimple * heap,BMFace * f_a,BMFace ** faces_prev,float * cost,const void * const f_endpoints[2],const struct BMCalcPathParams * params)439 static void facetag_add_adjacent(HeapSimple *heap,
440                                  BMFace *f_a,
441                                  BMFace **faces_prev,
442                                  float *cost,
443                                  const void *const f_endpoints[2],
444                                  const struct BMCalcPathParams *params)
445 {
446   const int f_a_index = BM_elem_index_get(f_a);
447 
448   /* Loop over faces of face, but do so by first looping over loops. */
449   {
450     BMIter liter;
451     BMLoop *l_a;
452 
453     BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
454       BMLoop *l_first, *l_iter;
455 
456       l_iter = l_first = l_a;
457       do {
458         BMFace *f_b = l_iter->f;
459         if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
460           /* We know 'f_b' is not visited, check it out! */
461           const int f_b_index = BM_elem_index_get(f_b);
462           const float cost_cut = params->use_topology_distance ?
463                                      1.0f :
464                                      facetag_cut_cost_edge(f_a, f_b, l_iter->e, f_endpoints);
465           const float cost_new = cost[f_a_index] + cost_cut;
466 
467           if (cost[f_b_index] > cost_new) {
468             cost[f_b_index] = cost_new;
469             faces_prev[f_b_index] = f_a;
470             BLI_heapsimple_insert(heap, cost_new, f_b);
471           }
472         }
473       } while ((l_iter = l_iter->radial_next) != l_first);
474     }
475   }
476 
477   if (params->use_step_face) {
478     BMIter liter;
479     BMLoop *l_a;
480 
481     BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
482       BMIter litersub;
483       BMLoop *l_b;
484       BM_ITER_ELEM (l_b, &litersub, l_a->v, BM_LOOPS_OF_VERT) {
485         if ((l_a != l_b) && !BM_loop_share_edge_check(l_a, l_b)) {
486           BMFace *f_b = l_b->f;
487           if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
488             /* We know 'f_b' is not visited, check it out! */
489             const int f_b_index = BM_elem_index_get(f_b);
490             const float cost_cut = params->use_topology_distance ?
491                                        1.0f :
492                                        facetag_cut_cost_vert(f_a, f_b, l_a->v, f_endpoints);
493             const float cost_new = cost[f_a_index] + cost_cut;
494 
495             if (cost[f_b_index] > cost_new) {
496               cost[f_b_index] = cost_new;
497               faces_prev[f_b_index] = f_a;
498               BLI_heapsimple_insert(heap, cost_new, f_b);
499             }
500           }
501         }
502       }
503     }
504   }
505 }
506 
BM_mesh_calc_path_face(BMesh * bm,BMFace * f_src,BMFace * f_dst,const struct BMCalcPathParams * params,bool (* filter_fn)(BMFace *,void * user_data),void * user_data)507 LinkNode *BM_mesh_calc_path_face(BMesh *bm,
508                                  BMFace *f_src,
509                                  BMFace *f_dst,
510                                  const struct BMCalcPathParams *params,
511                                  bool (*filter_fn)(BMFace *, void *user_data),
512                                  void *user_data)
513 {
514   LinkNode *path = NULL;
515   /* #BM_ELEM_TAG flag is used to store visited edges. */
516   BMFace *f;
517   BMIter fiter;
518   HeapSimple *heap;
519   float *cost;
520   BMFace **faces_prev;
521   int i, totface;
522 
523   /* Start measuring face path at the face edges, ignoring their centers. */
524   const void *const f_endpoints[2] = {f_src, f_dst};
525 
526   /* Note, would pass #BM_EDGE except we are looping over all faces anyway. */
527   // BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); // NOT NEEDED FOR FACETAG
528 
529   BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
530     BM_elem_flag_set(f, BM_ELEM_TAG, !filter_fn(f, user_data));
531     BM_elem_index_set(f, i); /* set_inline */
532   }
533   bm->elem_index_dirty &= ~BM_FACE;
534 
535   /* Allocate. */
536   totface = bm->totface;
537   faces_prev = MEM_callocN(sizeof(*faces_prev) * totface, __func__);
538   cost = MEM_mallocN(sizeof(*cost) * totface, __func__);
539 
540   copy_vn_fl(cost, totface, COST_INIT_MAX);
541 
542   /*
543    * Arrays are now filled as follows:
544    *
545    * As the search continues, `faces_prev[n]` will be the previous face on the shortest
546    * path found so far to face `n`. #BM_ELEM_TAG is used to tag elements we have visited,
547    * `cost[n]` will contain the length of the shortest
548    * path to face n found so far, Finally, heap is a priority heap which is built on the
549    * the same data as the cost array, but inverted: it is a work-list of faces prioritized
550    * by the shortest path found so far to the face.
551    */
552 
553   /* Regular dijkstra shortest path, but over faces instead of vertices. */
554   heap = BLI_heapsimple_new();
555   BLI_heapsimple_insert(heap, 0.0f, f_src);
556   cost[BM_elem_index_get(f_src)] = 0.0f;
557 
558   while (!BLI_heapsimple_is_empty(heap)) {
559     f = BLI_heapsimple_pop_min(heap);
560 
561     if (f == f_dst) {
562       break;
563     }
564 
565     if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
566       BM_elem_flag_enable(f, BM_ELEM_TAG);
567       facetag_add_adjacent(heap, f, faces_prev, cost, f_endpoints, params);
568     }
569   }
570 
571   if (f == f_dst) {
572     do {
573       BLI_linklist_prepend(&path, f);
574     } while ((f = faces_prev[BM_elem_index_get(f)]));
575   }
576 
577   MEM_freeN(faces_prev);
578   MEM_freeN(cost);
579   BLI_heapsimple_free(heap, NULL);
580 
581   return path;
582 }
583 /** \} */
584