Blender V5.0
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
12
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/* -------------------------------------------------------------------- */
28
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
55
56/* -------------------------------------------------------------------- */
59
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
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 = MEM_calloc_arrayN<BMVert *>(totvert, __func__);
146 cost = MEM_malloc_arrayN<float>(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
193
194/* -------------------------------------------------------------------- */
197
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 /* Prevent the path overlapping itself in rare cases, see: #137456. */
245 {
246 /* We know 'e_b' is not visited, check it out! */
247 const int e_b_index = BM_elem_index_get(e_b);
248 const float cost_cut = params->use_topology_distance ?
249 1.0f :
250 edgetag_cut_cost_vert(e_a, e_b, v);
251 const float cost_new = cost[e_a_index] + cost_cut;
252
253 if (cost[e_b_index] > cost_new) {
254 cost[e_b_index] = cost_new;
255 edges_prev[e_b_index] = e_a;
256 BLI_heapsimple_insert(heap, cost_new, e_b);
257 }
258 }
259 }
260 }
261 }
262 else {
263 BMLoop *l_first, *l_iter;
264
265 l_iter = l_first = e_a->l;
266 do {
267 BMLoop *l_cycle_iter, *l_cycle_end;
268
269 l_cycle_iter = l_iter->next;
270 l_cycle_end = l_iter;
271
272/* Good, but we need to allow this otherwise paths may fail to connect at all. */
273#if 0
274 if (l_iter->f->len > 3) {
275 l_cycle_iter = l_cycle_iter->next;
276 l_cycle_end = l_cycle_end->prev;
277 }
278#endif
279
280 do {
281 BMEdge *e_b = l_cycle_iter->e;
282 if (!BM_elem_flag_test(e_b, BM_ELEM_TAG)) {
283 /* We know 'e_b' is not visited, check it out! */
284 const int e_b_index = BM_elem_index_get(e_b);
285 const float cost_cut = params->use_topology_distance ?
286 1.0f :
287 edgetag_cut_cost_face(e_a, e_b, l_iter->f);
288 const float cost_new = cost[e_a_index] + cost_cut;
289
290 if (cost[e_b_index] > cost_new) {
291 cost[e_b_index] = cost_new;
292 edges_prev[e_b_index] = e_a;
293 BLI_heapsimple_insert(heap, cost_new, e_b);
294 }
295 }
296 } while ((l_cycle_iter = l_cycle_iter->next) != l_cycle_end);
297 } while ((l_iter = l_iter->radial_next) != l_first);
298 }
299}
300
302 BMEdge *e_src,
303 BMEdge *e_dst,
305 bool (*filter_fn)(BMEdge *, void *user_data),
306 void *user_data)
307{
308 LinkNode *path = nullptr;
309 /* #BM_ELEM_TAG flag is used to store visited edges. */
310 BMIter iter;
311 HeapSimple *heap;
312 float *cost;
313 BMEdge **edges_prev;
314 int i, totedge;
315
316 {
317 BMVert *v;
320 BM_elem_index_set(v, i); /* set_inline */
321 }
322 bm->elem_index_dirty &= ~BM_VERT;
323 }
324
325 {
326 BMEdge *e;
328 BM_elem_flag_set(e, BM_ELEM_TAG, !filter_fn(e, user_data));
329 BM_elem_index_set(e, i); /* set_inline */
330 }
331 bm->elem_index_dirty &= ~BM_EDGE;
332 }
333
334 /* Allocate. */
335 totedge = bm->totedge;
336 edges_prev = MEM_calloc_arrayN<BMEdge *>(totedge, __func__);
337 cost = MEM_malloc_arrayN<float>(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 BMEdge *e = nullptr;
358 while (!BLI_heapsimple_is_empty(heap)) {
359 e = static_cast<BMEdge *>(BLI_heapsimple_pop_min(heap));
360
361 if (e == e_dst) {
362 break;
363 }
364
367
368 /* Prevent the path overlapping itself in rare cases, see: #137456. */
371
372 edgetag_add_adjacent(heap, e, edges_prev, cost, params);
373 }
374 }
375
376 if (e == e_dst) {
377 do {
378 BLI_linklist_prepend(&path, e);
379 } while ((e = edges_prev[BM_elem_index_get(e)]));
380 }
381
382 MEM_freeN(edges_prev);
383 MEM_freeN(cost);
384 BLI_heapsimple_free(heap, nullptr);
385
386 return path;
387}
388
390
391/* -------------------------------------------------------------------- */
394
396 BMFace *f_b,
397 BMEdge *e,
398 const void *const f_endpoints[2])
399{
400 float f_a_cent[3];
401 float f_b_cent[3];
402 float e_cent[3];
403
406#if 0
407 mid_v3_v3v3(e_cent, e->v1->co, e->v2->co);
408#else
409 /* For triangle fans it gives better results to pick a point on the edge. */
410 {
411 float ix_e[3], ix_f[3];
412 isect_line_line_v3(e->v1->co, e->v2->co, f_a_cent, f_b_cent, ix_e, ix_f);
413 const float factor = line_point_factor_v3(ix_e, e->v1->co, e->v2->co);
414 if (factor < 0.0f) {
415 copy_v3_v3(e_cent, e->v1->co);
416 }
417 else if (factor > 1.0f) {
418 copy_v3_v3(e_cent, e->v2->co);
419 }
420 else {
421 copy_v3_v3(e_cent, ix_e);
422 }
423 }
424#endif
425
426 return step_cost_3_v3_ex(
427 f_a_cent, e_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
428}
429
431 BMFace *f_b,
432 BMVert *v,
433 const void *const f_endpoints[2])
434{
435 float f_a_cent[3];
436 float f_b_cent[3];
437
440
441 return step_cost_3_v3_ex(
442 f_a_cent, v->co, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
443}
444
446 BMFace *f_a,
447 BMFace **faces_prev,
448 float *cost,
449 const void *const f_endpoints[2],
451{
452 const int f_a_index = BM_elem_index_get(f_a);
453
454 /* Loop over faces of face, but do so by first looping over loops. */
455 {
456 BMIter liter;
457 BMLoop *l_a;
458
459 BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
460 BMLoop *l_first, *l_iter;
461
462 l_iter = l_first = l_a;
463 do {
464 BMFace *f_b = l_iter->f;
465 if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
466 /* We know 'f_b' is not visited, check it out! */
467 const int f_b_index = BM_elem_index_get(f_b);
468 const float cost_cut = params->use_topology_distance ?
469 1.0f :
470 facetag_cut_cost_edge(f_a, f_b, l_iter->e, f_endpoints);
471 const float cost_new = cost[f_a_index] + cost_cut;
472
473 if (cost[f_b_index] > cost_new) {
474 cost[f_b_index] = cost_new;
475 faces_prev[f_b_index] = f_a;
476 BLI_heapsimple_insert(heap, cost_new, f_b);
477 }
478 }
479 } while ((l_iter = l_iter->radial_next) != l_first);
480 }
481 }
482
483 if (params->use_step_face) {
484 BMIter liter;
485 BMLoop *l_a;
486
487 BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
488 BMIter litersub;
489 BMLoop *l_b;
490 BM_ITER_ELEM (l_b, &litersub, l_a->v, BM_LOOPS_OF_VERT) {
491 if ((l_a != l_b) && !BM_loop_share_edge_check(l_a, l_b)) {
492 BMFace *f_b = l_b->f;
493 if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
494 /* We know 'f_b' is not visited, check it out! */
495 const int f_b_index = BM_elem_index_get(f_b);
496 const float cost_cut = params->use_topology_distance ?
497 1.0f :
498 facetag_cut_cost_vert(f_a, f_b, l_a->v, f_endpoints);
499 const float cost_new = cost[f_a_index] + cost_cut;
500
501 if (cost[f_b_index] > cost_new) {
502 cost[f_b_index] = cost_new;
503 faces_prev[f_b_index] = f_a;
504 BLI_heapsimple_insert(heap, cost_new, f_b);
505 }
506 }
507 }
508 }
509 }
510 }
511}
512
514 BMFace *f_src,
515 BMFace *f_dst,
517 bool (*filter_fn)(BMFace *, void *user_data),
518 void *user_data)
519{
520 LinkNode *path = nullptr;
521 /* #BM_ELEM_TAG flag is used to store visited edges. */
522 BMFace *f;
523 BMIter fiter;
524 HeapSimple *heap;
525 float *cost;
526 BMFace **faces_prev;
527 int i, totface;
528
529 /* Start measuring face path at the face edges, ignoring their centers. */
530 const void *const f_endpoints[2] = {f_src, f_dst};
531
532 /* NOTE: would pass #BM_EDGE except we are looping over all faces anyway. */
533 // BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); // NOT NEEDED FOR FACETAG
534
535 BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
536 BM_elem_flag_set(f, BM_ELEM_TAG, !filter_fn(f, user_data));
537 BM_elem_index_set(f, i); /* set_inline */
538 }
539 bm->elem_index_dirty &= ~BM_FACE;
540
541 /* Allocate. */
542 totface = bm->totface;
543 faces_prev = MEM_calloc_arrayN<BMFace *>(totface, __func__);
544 cost = MEM_malloc_arrayN<float>(totface, __func__);
545
546 copy_vn_fl(cost, totface, COST_INIT_MAX);
547
548 /*
549 * Arrays are now filled as follows:
550 *
551 * As the search continues, `faces_prev[n]` will be the previous face on the shortest
552 * path found so far to face `n`. #BM_ELEM_TAG is used to tag elements we have visited,
553 * `cost[n]` will contain the length of the shortest
554 * path to face n found so far, Finally, heap is a priority heap which is built on the
555 * the same data as the cost array, but inverted: it is a work-list of faces prioritized
556 * by the shortest path found so far to the face.
557 */
558
559 /* Regular dijkstra shortest path, but over faces instead of vertices. */
560 heap = BLI_heapsimple_new();
561 BLI_heapsimple_insert(heap, 0.0f, f_src);
562 cost[BM_elem_index_get(f_src)] = 0.0f;
563
564 while (!BLI_heapsimple_is_empty(heap)) {
565 f = static_cast<BMFace *>(BLI_heapsimple_pop_min(heap));
566
567 if (f == f_dst) {
568 break;
569 }
570
573 facetag_add_adjacent(heap, f, faces_prev, cost, f_endpoints, params);
574 }
575 }
576
577 if (f == f_dst) {
578 do {
579 BLI_linklist_prepend(&path, f);
580 } while ((f = faces_prev[BM_elem_index_get(f)]));
581 }
582
583 MEM_freeN(faces_prev);
584 MEM_freeN(cost);
585 BLI_heapsimple_free(heap, nullptr);
586
587 return path;
588}
589
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_disable(ele, hflag)
#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
BMesh * bm
#define BM_FACE
#define BM_EDGE
#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
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define fabsf
#define sqrtf
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]
i
Definition text_draw.cc:230