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