Blender V4.3
bmesh_edgenet.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
11#include <climits>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_alloca.h"
16#include "BLI_linklist.h"
17#include "BLI_mempool.h"
18#include "BLI_utildefines.h"
19
20#include "bmesh.hh"
21#include "bmesh_edgenet.hh" /* own include */
22
23#include "BLI_strict_flags.h" /* Keep last. */
24
25/* Struct for storing a path of verts walked over */
27 BMVert *prev; /* previous vertex */
28 int pass; /* path scanning pass value, for internal calculation */
29 int face; /* face index connected to the edge between this and the previous vert */
30 int flag; /* flag */
31};
32
33enum {
35};
36
41{
42 return BM_elem_flag_test(e, BM_ELEM_TAG) && ELEM(e->l, nullptr, e->l->radial_next);
43}
44
45static int bm_edge_face(BMEdge *e)
46{
47 return e->l ? BM_elem_index_get(e->l->f) : -1;
48}
49
54 LinkNode **edge_queue,
55 BLI_mempool *edge_queue_pool)
56{
57 BMEdge *e;
58 BMIter iter;
59
60 while (*edge_queue) {
61 e = static_cast<BMEdge *>(BLI_linklist_pop_pool(edge_queue, edge_queue_pool));
62 if (bm_edge_step_ok(e)) {
63 return e;
64 }
65 }
66
68 if (bm_edge_step_ok(e)) {
69 return e;
70 }
71 }
72
73 return nullptr;
74}
75
83 LinkNode **v_ls,
84 VertNetInfo *vnet_info,
85 BLI_mempool *path_pool)
86{
87 VertNetInfo *vn = &vnet_info[BM_elem_index_get(v)];
88 const int pass = vn->pass;
89 uint v_ls_tot = 0;
90
91 do {
92 BLI_linklist_prepend_pool(v_ls, v, path_pool);
93 v_ls_tot += 1;
94 v = vn->prev;
95 vn = &vnet_info[BM_elem_index_get(v)];
96 } while (vn->pass == pass);
97
98 return v_ls_tot;
99}
100
106{
107 /* vert order doesn't matter */
108 uint v_ls_tot = 0;
109 LinkNode *v_ls = nullptr;
110 BMVert *v_pair[2] = {v1, v2};
111 uint i;
112
113 for (i = 0; i < 2; i++) {
114 BMVert *v = v_pair[i];
115 VertNetInfo *vn = &vnet_info[BM_elem_index_get(v)];
116 const int pass = vn->pass;
117 do {
119 v_ls_tot += 1;
120 v = vn->prev;
121 vn = &vnet_info[BM_elem_index_get(v)];
122 } while (vn->pass == pass);
123 }
124
125 if (v_ls_tot) {
126 BMVert **vert_arr = BLI_array_alloca(vert_arr, v_ls_tot);
127 LinkNode *v_lnk;
128 for (i = 0, v_lnk = v_ls; i < v_ls_tot; v_lnk = v_lnk->next, i++) {
129 vert_arr[i] = static_cast<BMVert *>(v_lnk->link);
130 }
131
132 return BM_face_exists_overlap_subset(vert_arr, int(v_ls_tot));
133 }
134 return false;
135}
136
140static BMFace *bm_edgenet_face_from_path(BMesh *bm, LinkNode *path, const uint path_len)
141{
142 BMFace *f;
143 LinkNode *v_lnk;
144 int i;
145 bool ok;
146
147 BMVert **vert_arr = BLI_array_alloca(vert_arr, path_len);
148 BMEdge **edge_arr = BLI_array_alloca(edge_arr, path_len);
149
150 for (v_lnk = path, i = 0; v_lnk; v_lnk = v_lnk->next, i++) {
151 vert_arr[i] = static_cast<BMVert *>(v_lnk->link);
152 }
153
154 ok = BM_edges_from_verts(edge_arr, vert_arr, i);
155 BLI_assert(ok);
157
158/* no need for this, we do overlap checks before allowing the path to be used */
159#if 0
160 if (BM_face_exists_multi(vert_arr, edge_arr, path_len)) {
161 return nullptr;
162 }
163#endif
164
165 f = BM_face_create(bm, vert_arr, edge_arr, int(path_len), nullptr, BM_CREATE_NOP);
166
167 return f;
168}
169
176 LinkNode **v_ls,
177 VertNetInfo *vnet_info,
178 BLI_mempool *path_pool)
179{
180 const VertNetInfo *vn_curr;
181
182 BMEdge *e;
183 BMIter iter;
184 uint tot;
185 uint v_ls_tot;
186
187begin:
188 tot = 0;
189 v_ls_tot = 0;
190 vn_curr = &vnet_info[BM_elem_index_get(v_curr)];
191
192 BM_ITER_ELEM (e, &iter, v_curr, BM_EDGES_OF_VERT) {
193 BMVert *v_next = BM_edge_other_vert(e, v_curr);
194 if (v_next != vn_curr->prev) {
195 if (bm_edge_step_ok(e)) {
196 VertNetInfo *vn_next = &vnet_info[BM_elem_index_get(v_next)];
197
198 /* check we're not looping back on ourselves */
199 if (vn_curr->pass != vn_next->pass) {
200
201 if (vn_curr->pass == -vn_next->pass) {
202 if ((vn_curr->flag & VNINFO_FLAG_IS_MIXFACE) ||
203 (vn_next->flag & VNINFO_FLAG_IS_MIXFACE))
204 {
205 /* found connecting edge */
206 if (bm_edgenet_path_check_overlap(v_curr, v_next, vnet_info) == false) {
207 return e;
208 }
209 }
210 }
211 else {
212 vn_next->face = bm_edge_face(e);
213 vn_next->pass = vn_curr->pass;
214 vn_next->prev = v_curr;
215
216 /* flush flag down the path */
217 vn_next->flag &= ~VNINFO_FLAG_IS_MIXFACE;
218 if ((vn_curr->flag & VNINFO_FLAG_IS_MIXFACE) || (vn_next->face == -1) ||
219 (vn_next->face != vn_curr->face))
220 {
221 vn_next->flag |= VNINFO_FLAG_IS_MIXFACE;
222 }
223
224 /* add to the list! */
225 BLI_linklist_prepend_pool(v_ls, v_next, path_pool);
226 v_ls_tot += 1;
227 }
228 }
229 }
230 tot += 1;
231 }
232 }
233
234 /* trick to walk along wire-edge paths */
235 if (v_ls_tot == 1 && tot == 1) {
236 v_curr = static_cast<BMVert *>(BLI_linklist_pop_pool(v_ls, path_pool));
237/* avoid recursion, can crash on very large nets */
238#if 0
239 bm_edgenet_path_step(v_curr, v_ls, vnet_info, path_pool);
240#else
241 goto begin;
242#endif
243 }
244
245 return nullptr;
246}
247
254 const int pass_nr,
255 const uint path_cost_max,
256 uint *r_path_len,
257 uint *r_path_cost,
258 VertNetInfo *vnet_info,
259 BLI_mempool *path_pool)
260{
261 VertNetInfo *vn_1, *vn_2;
262 const int f_index = bm_edge_face(e);
263 bool found;
264
265 LinkNode *v_ls_prev = nullptr;
266 LinkNode *v_ls_next = nullptr;
267
268 uint path_cost_accum = 0;
269
271
272 *r_path_len = 0;
273 *r_path_cost = 0;
274
275 vn_1 = &vnet_info[BM_elem_index_get(e->v1)];
276 vn_2 = &vnet_info[BM_elem_index_get(e->v2)];
277
278 vn_1->pass = pass_nr;
279 vn_2->pass = -pass_nr;
280
281 vn_1->prev = e->v2;
282 vn_2->prev = e->v1;
283
284 vn_1->face = vn_2->face = f_index;
285
286 vn_1->flag = vn_2->flag = (f_index == -1) ? VNINFO_FLAG_IS_MIXFACE : 0;
287
288 /* Prime the search-list. */
289 BLI_linklist_prepend_pool(&v_ls_prev, e->v1, path_pool);
290 BLI_linklist_prepend_pool(&v_ls_prev, e->v2, path_pool);
291
292 do {
293 found = false;
294
295 /* no point to continue, we're over budget */
296 if (path_cost_accum >= path_cost_max) {
297 BLI_linklist_free_pool(v_ls_next, nullptr, path_pool);
298 BLI_linklist_free_pool(v_ls_prev, nullptr, path_pool);
299 return nullptr;
300 }
301
302 while (v_ls_prev) {
303 const LinkNode *v_ls_next_old = v_ls_next;
304 BMVert *v = static_cast<BMVert *>(BLI_linklist_pop_pool(&v_ls_prev, path_pool));
305 BMEdge *e_found = bm_edgenet_path_step(v, &v_ls_next, vnet_info, path_pool);
306
307 if (e_found) {
308 LinkNode *path = nullptr;
309 uint path_len;
310 BLI_linklist_free_pool(v_ls_next, nullptr, path_pool);
311 BLI_linklist_free_pool(v_ls_prev, nullptr, path_pool);
312
313 // BLI_assert(BLI_mempool_len(path_pool) == 0);
314
315 path_len = bm_edgenet_path_from_pass(e_found->v1, &path, vnet_info, path_pool);
317 path_len += bm_edgenet_path_from_pass(e_found->v2, &path, vnet_info, path_pool);
318 *r_path_len = path_len;
319 *r_path_cost = path_cost_accum;
320 return path;
321 }
322
323 /* check if a change was made */
324 if (v_ls_next_old != v_ls_next) {
325 found = true;
326 }
327 }
328 BLI_assert(v_ls_prev == nullptr);
329
330 path_cost_accum++;
331
332 /* swap */
333 v_ls_prev = v_ls_next;
334 v_ls_next = nullptr;
335
336 } while (found);
337
338 BLI_assert(v_ls_prev == nullptr);
339 BLI_assert(v_ls_next == nullptr);
340
341 /* tag not to search again */
343
344 return nullptr;
345}
346
352 int *pass_nr,
353 uint path_cost_max,
354 uint *r_path_len,
355 uint *r_path_cost,
356 VertNetInfo *vnet_info,
357 BLI_mempool *path_pool)
358{
359 LinkNode *path;
360 uint path_cost;
361
363 e, *pass_nr, path_cost_max, r_path_len, &path_cost, vnet_info, path_pool);
364 (*pass_nr)++;
365
366 if (path == nullptr) {
367 return nullptr;
368 }
369 if (path_cost < 1) {
370 /* any face that takes 1 iteration to find we consider valid */
371 return path;
372 }
373
374 /* Check every edge to see if any can give a better path.
375 * This avoids very strange/long paths from being created. */
376
377 const uint path_len = *r_path_len;
378 uint i, i_prev;
379 BMVert **vert_arr = BLI_array_alloca(vert_arr, path_len);
380 LinkNode *v_lnk;
381
382 for (v_lnk = path, i = 0; v_lnk; v_lnk = v_lnk->next, i++) {
383 vert_arr[i] = static_cast<BMVert *>(v_lnk->link);
384 }
385
386 i_prev = path_len - 1;
387 for (i = 0; i < path_len; i++) {
388 BMEdge *e_other = BM_edge_exists(vert_arr[i], vert_arr[i_prev]);
389 if (e_other != e) {
390 LinkNode *path_test;
391 uint path_len_test;
392 uint path_cost_test;
393
394 path_test = bm_edgenet_path_calc(
395 e_other, *pass_nr, path_cost, &path_len_test, &path_cost_test, vnet_info, path_pool);
396 (*pass_nr)++;
397
398 if (path_test) {
399 BLI_assert(path_cost_test < path_cost);
400
401 BLI_linklist_free_pool(path, nullptr, path_pool);
402 path = path_test;
403 *r_path_len = path_len_test;
404 *r_path_cost = path_cost_test;
405 path_cost = path_cost_test;
406 }
407 }
408
409 i_prev = i;
410 }
411
412 return path;
413}
414
415void BM_mesh_edgenet(BMesh *bm, const bool use_edge_tag, const bool use_new_face_tag)
416{
417 VertNetInfo *vnet_info = static_cast<VertNetInfo *>(
418 MEM_callocN(sizeof(*vnet_info) * size_t(bm->totvert), __func__));
419 BLI_mempool *edge_queue_pool = BLI_mempool_create(sizeof(LinkNode), 0, 512, BLI_MEMPOOL_NOP);
420 BLI_mempool *path_pool = BLI_mempool_create(sizeof(LinkNode), 0, 512, BLI_MEMPOOL_NOP);
421 LinkNode *edge_queue = nullptr;
422
423 BMEdge *e;
424 BMIter iter;
425
426 int pass_nr = 1;
427
428 if (use_edge_tag == false) {
429 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
431 }
432 }
433
435
436 while (true) {
437 LinkNode *path = nullptr;
438 uint path_len;
439 uint path_cost;
440
441 e = bm_edgenet_edge_get_next(bm, &edge_queue, edge_queue_pool);
442 if (e == nullptr) {
443 break;
444 }
445
446 BLI_assert(bm_edge_step_ok(e) == true);
447
449 e, &pass_nr, UINT_MAX, &path_len, &path_cost, vnet_info, path_pool);
450
451 if (path) {
452 BMFace *f = bm_edgenet_face_from_path(bm, path, path_len);
453 /* queue edges to operate on */
454 BMLoop *l_first, *l_iter;
455 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
456 do {
457 if (bm_edge_step_ok(l_iter->e)) {
458 BLI_linklist_prepend_pool(&edge_queue, l_iter->e, edge_queue_pool);
459 }
460 } while ((l_iter = l_iter->next) != l_first);
461
462 if (use_new_face_tag) {
464 }
465
466 /* the face index only needs to be unique, not kept valid */
467 BM_elem_index_set(f, bm->totface - 1); /* set_dirty */
468 }
469
470 BLI_linklist_free_pool(path, nullptr, path_pool);
471 BLI_assert(BLI_mempool_len(path_pool) == 0);
472 }
473
475
476 BLI_mempool_destroy(edge_queue_pool);
477 BLI_mempool_destroy(path_pool);
478 MEM_freeN(vnet_info);
479}
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_mempool * BLI_mempool_create(unsigned int esize, unsigned int elem_num, unsigned int pchunk, unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
int BLI_mempool_len(const BLI_mempool *pool) ATTR_NONNULL(1)
@ BLI_MEMPOOL_NOP
Definition BLI_mempool.h:86
void BLI_mempool_destroy(BLI_mempool *pool) ATTR_NONNULL(1)
unsigned int uint
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
Read Guarded memory(de)allocation.
@ BM_LOOP
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
bool BM_edges_from_verts(BMEdge **edge_arr, BMVert **vert_arr, const int len)
BMFace * BM_face_create(BMesh *bm, BMVert *const *verts, BMEdge *const *edges, const int len, const BMFace *f_example, const eBMCreateFlag create_flag)
@ BM_CREATE_NOP
Definition bmesh_core.hh:24
static int bm_edge_face(BMEdge *e)
@ VNINFO_FLAG_IS_MIXFACE
static bool bm_edgenet_path_check_overlap(BMVert *v1, BMVert *v2, VertNetInfo *vnet_info)
static LinkNode * bm_edgenet_path_calc_best(BMEdge *e, int *pass_nr, uint path_cost_max, uint *r_path_len, uint *r_path_cost, VertNetInfo *vnet_info, BLI_mempool *path_pool)
void BM_mesh_edgenet(BMesh *bm, const bool use_edge_tag, const bool use_new_face_tag)
static bool bm_edge_step_ok(BMEdge *e)
static BMEdge * bm_edgenet_edge_get_next(BMesh *bm, LinkNode **edge_queue, BLI_mempool *edge_queue_pool)
static BMFace * bm_edgenet_face_from_path(BMesh *bm, LinkNode *path, const uint path_len)
static LinkNode * bm_edgenet_path_calc(BMEdge *e, const int pass_nr, const uint path_cost_max, uint *r_path_len, uint *r_path_cost, VertNetInfo *vnet_info, BLI_mempool *path_pool)
static uint bm_edgenet_path_from_pass(BMVert *v, LinkNode **v_ls, VertNetInfo *vnet_info, BLI_mempool *path_pool)
static BMEdge * bm_edgenet_path_step(BMVert *v_curr, LinkNode **v_ls, VertNetInfo *vnet_info, BLI_mempool *path_pool)
#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(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
@ BM_EDGES_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
#define BM_FACE
#define BM_VERT
bool BM_face_exists_multi(BMVert **varr, BMEdge **earr, int len)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
bool BM_face_exists_overlap_subset(BMVert **varr, const int len)
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define UINT_MAX
Definition hash_md5.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
BMVert * v2
struct BMEdge * e
struct BMLoop * next
int totvert
char elem_index_dirty
int totface
void * link
struct LinkNode * next
BMVert * prev