Blender V5.0
bmesh_path_region_uv.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
15
16#include "MEM_guardedalloc.h"
17
18#include "BLI_alloca.h"
19#include "BLI_linklist.h"
20#include "BLI_math_vector.h"
22
23#include "bmesh.hh"
24#include "bmesh_path_region_uv.hh" /* own include */
25
37#define USE_EDGE_CHAIN
38
39#ifdef USE_EDGE_CHAIN
45static bool bm_loop_pair_ends(BMLoop *l_pivot, BMLoop *l_end_pair[2])
46{
47 int j;
48 for (j = 0; j < 2; j++) {
49 BMLoop *l_other = j ? l_pivot->next : l_pivot->prev;
50 while (BM_vert_is_edge_pair_manifold(l_other->v)) {
51 l_other = j ? l_other->next : l_other->prev;
52 if (l_other == l_pivot) {
53 return false;
54 }
55 }
56 l_end_pair[j] = l_other;
57 }
58 BLI_assert(j == 2);
59 return true;
60}
61#endif /* USE_EDGE_CHAIN */
62
63/* -------------------------------------------------------------------- */
66
67static bool bm_loop_region_test(BMLoop *l, int *const depths[2], const int pass)
68{
69 const int index = BM_elem_index_get(l);
70 return (((depths[0][index] != -1) && (depths[1][index] != -1)) &&
71 ((depths[0][index] + depths[1][index]) < pass));
72}
73
74#ifdef USE_EDGE_CHAIN
75static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass)
76{
77 BMLoop *l_end_pair[2];
78 if (bm_loop_region_test(l, depths, pass)) {
79 return true;
80 }
81 if (BM_vert_is_edge_pair_manifold(l->v) && bm_loop_pair_ends(l, l_end_pair) &&
82 bm_loop_region_test(l_end_pair[0], depths, pass) &&
83 bm_loop_region_test(l_end_pair[1], depths, pass))
84 {
85 return true;
86 }
87
88 return false;
89}
90#else
91static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass)
92{
93 return bm_loop_region_test(l, depths, pass);
94}
95#endif
96
98
113 BMElem *ele_src,
114 BMElem *ele_dst,
115 const int cd_loop_uv_offset,
116 const char path_htype)
117{
118 BLI_assert(cd_loop_uv_offset >= 0);
119 int ele_loops_len[2];
120 BMLoop **ele_loops[2];
121
122 /* Get vertices from any `ele_src/ele_dst` elements. */
123 for (int side = 0; side < 2; side++) {
124 BMElem *ele = side ? ele_dst : ele_src;
125 int j = 0;
126
127 if (ele->head.htype == BM_FACE) {
128 BMFace *f = (BMFace *)ele;
129 ele_loops[side] = BLI_array_alloca(ele_loops[side], f->len);
130
131 BMLoop *l_first, *l_iter;
132 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
133 do {
134 ele_loops[side][j++] = l_iter;
135 } while ((l_iter = l_iter->next) != l_first);
136 }
137 else if (ele->head.htype == BM_LOOP) {
138 if (path_htype == BM_EDGE) {
139 BMLoop *l = (BMLoop *)ele;
140 ele_loops[side] = BLI_array_alloca(ele_loops[side], 2);
141 ele_loops[side][j++] = l;
142 ele_loops[side][j++] = l->next;
143 }
144 else if (path_htype == BM_VERT) {
145 BMLoop *l = (BMLoop *)ele;
146 ele_loops[side] = BLI_array_alloca(ele_loops[side], 1);
147
148 ele_loops[side][j++] = l;
149 }
150 else {
151 BLI_assert(0);
152 }
153 }
154 else {
155 BLI_assert(0);
156 }
157 ele_loops_len[side] = j;
158 }
159
160 int *depths[2] = {nullptr};
161 int pass = 0;
162
163 BMLoop **stack = MEM_malloc_arrayN<BMLoop *>(bm->totloop, __func__);
164 BMLoop **stack_other = MEM_malloc_arrayN<BMLoop *>(bm->totloop, __func__);
165
166 STACK_DECLARE(stack);
167 STACK_INIT(stack, bm->totloop);
168
169 STACK_DECLARE(stack_other);
170 STACK_INIT(stack_other, bm->totloop);
171
173
174 /* After exhausting all possible elements, we should have found all elements on the 'side_other'.
175 * otherwise, exit early. */
176 bool found_all = false;
177
178 for (int side = 0; side < 2; side++) {
179 const int side_other = !side;
180
181 /* initialize depths to -1 (un-touched), fill in with the depth as we walk over the edges. */
182 depths[side] = static_cast<int *>(MEM_mallocN(sizeof(*depths[side]) * bm->totloop, __func__));
183 copy_vn_i(depths[side], bm->totloop, -1);
184
185 /* needed for second side */
186 STACK_CLEAR(stack);
187 STACK_CLEAR(stack_other);
188
189 for (int i = 0; i < ele_loops_len[side]; i++) {
190 BMLoop *l = ele_loops[side][i];
191 depths[side][BM_elem_index_get(l)] = 0;
193 STACK_PUSH(stack, l);
194 }
195 }
196
197#ifdef USE_EDGE_CHAIN
198 /* Expand initial state to end-point vertices when they only have 2x edges,
199 * this prevents odd behavior when source or destination are in the middle
200 * of a long chain of edges. */
201 if (ELEM(path_htype, BM_VERT, BM_EDGE)) {
202 for (int i = 0; i < ele_loops_len[side]; i++) {
203 BMLoop *l = ele_loops[side][i];
204 BMLoop *l_end_pair[2];
205 if (BM_vert_is_edge_pair_manifold(l->v) && bm_loop_pair_ends(l, l_end_pair)) {
206 for (int j = 0; j < 2; j++) {
207 const int l_end_index = BM_elem_index_get(l_end_pair[j]);
208 if (depths[side][l_end_index] == -1) {
209 depths[side][l_end_index] = 0;
210 if (!BM_elem_flag_test(l_end_pair[j], BM_ELEM_TAG)) {
211 STACK_PUSH(stack, l_end_pair[j]);
212 }
213 }
214 }
215 }
216 }
217 }
218#endif /* USE_EDGE_CHAIN */
219
220 /* Keep walking over connected geometry until we find all the vertices in
221 * `ele_loops[side_other]`, or exit the loop when there's no connection. */
222 found_all = false;
223 for (pass = 1; (STACK_SIZE(stack) != 0); pass++) {
224 while (STACK_SIZE(stack) != 0) {
225 BMLoop *l_a = STACK_POP(stack);
226 const int l_a_index = BM_elem_index_get(l_a);
227
228 BMIter iter;
229 BMLoop *l_iter;
230
231 BM_ITER_ELEM (l_iter, &iter, l_a->v, BM_LOOPS_OF_VERT) {
232 if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
233 continue;
234 }
235 if (!BM_loop_uv_share_vert_check(l_a, l_iter, cd_loop_uv_offset)) {
236 continue;
237 }
238
239 /* Flush the depth to connected loops (only needed for UVs). */
240 if (depths[side][BM_elem_index_get(l_iter)] == -1) {
241 depths[side][BM_elem_index_get(l_iter)] = depths[side][l_a_index];
242 }
243
244 for (int j = 0; j < 2; j++) {
245 BMLoop *l_b = j ? l_iter->next : l_iter->prev;
246 int l_b_index = BM_elem_index_get(l_b);
247 if (depths[side][l_b_index] == -1) {
248
249#ifdef USE_EDGE_CHAIN
250 /* Walk along the chain, fill in values until we reach a vertex with 3+ edges. */
251 {
253 ((depths[side][l_b_index] == -1) &&
254 /* Don't walk back to the beginning */
255 (l_b != (j ? l_iter->prev : l_iter->next))))
256 {
257 depths[side][l_b_index] = pass;
258
259 l_b = j ? l_b->next : l_b->prev;
260 l_b_index = BM_elem_index_get(l_b);
261 }
262 }
263#endif /* USE_EDGE_CHAIN */
264
265 /* Add the other vertex to the stack, to be traversed in the next pass. */
266 if (depths[side][l_b_index] == -1) {
267#ifdef USE_EDGE_CHAIN
269#endif
270 BLI_assert(pass == depths[side][BM_elem_index_get(l_a)] + 1);
271 depths[side][l_b_index] = pass;
273 STACK_PUSH(stack_other, l_b);
274 }
275 }
276 }
277 }
278 }
279 }
280
281 /* Stop searching once there's none left.
282 * Note that this looks in-efficient, however until the target elements reached,
283 * it will exit immediately.
284 * After that, it takes as many passes as the element has edges to finish off. */
285 found_all = true;
286 for (int i = 0; i < ele_loops_len[side_other]; i++) {
287 if (depths[side][BM_elem_index_get(ele_loops[side_other][i])] == -1) {
288 found_all = false;
289 break;
290 }
291 }
292 if (found_all == true) {
293 pass++;
294 break;
295 }
296
297 STACK_SWAP(stack, stack_other);
298 }
299
300 /* if we have nothing left, and didn't find all elements on the other side,
301 * exit early and don't continue */
302 if (found_all == false) {
303 break;
304 }
305 }
306
307 MEM_freeN(stack);
308 MEM_freeN(stack_other);
309
310 /* Now we have depths recorded from both sides,
311 * select elements that use tagged verts. */
312 LinkNode *path = nullptr;
313
314 if (found_all == false) {
315 /* fail! (do nothing) */
316 }
317 else if (path_htype == BM_FACE) {
318 BMIter fiter;
319 BMFace *f;
320
321 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
323 /* check all verts in face are tagged */
324 BMLoop *l_first, *l_iter;
325 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
326 bool ok = true;
327#if 0
328 do {
329 if (!bm_loop_region_test_chain(l_iter->v, depths, pass)) {
330 ok = false;
331 break;
332 }
333 } while ((l_iter = l_iter->next) != l_first);
334#else
335 /* Allowing a single failure on a face gives fewer 'gaps'.
336 * While correct, in practice they're often part of what
337 * a user would consider the 'region'. */
338 int ok_tests = f->len > 3 ? 1 : 0; /* how many times we may fail */
339 do {
340 if (!bm_loop_region_test_chain(l_iter, depths, pass)) {
341 if (ok_tests == 0) {
342 ok = false;
343 break;
344 }
345 ok_tests--;
346 }
347 } while ((l_iter = l_iter->next) != l_first);
348#endif
349
350 if (ok) {
351 BLI_linklist_prepend(&path, f);
352 }
353 }
354 }
355 }
356 else if (path_htype == BM_EDGE) {
357 BMIter fiter;
358 BMFace *f;
359 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
360 BMIter liter;
361 BMLoop *l;
362 /* Check the current and next loop vertices are in the region. */
363 bool l_in_chain_next = bm_loop_region_test_chain(BM_FACE_FIRST_LOOP(f), depths, pass);
364 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
365 const bool l_in_chain = l_in_chain_next;
366 l_in_chain_next = bm_loop_region_test_chain(l->next, depths, pass);
367 if (l_in_chain && l_in_chain_next) {
368 BLI_linklist_prepend(&path, l);
369 }
370 }
371 }
372 }
373 else if (path_htype == BM_VERT) {
374 BMIter fiter;
375 BMFace *f;
376 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
377 BMIter liter;
378 BMLoop *l;
379 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
380 if (bm_loop_region_test_chain(l, depths, pass)) {
381 BLI_linklist_prepend(&path, l);
382 }
383 }
384 }
385 }
386
387 for (int side = 0; side < 2; side++) {
388 if (depths[side]) {
389 MEM_freeN(depths[side]);
390 }
391 }
392
393 return path;
394}
395
396#undef USE_EDGE_CHAIN
397
398/* -------------------------------------------------------------------- */
401
403 BMElem *ele_src,
404 BMElem *ele_dst,
405 const int cd_loop_uv_offset,
406 bool (*filter_fn)(BMLoop *, void *user_data),
407 void *user_data)
408{
409 LinkNode *path = nullptr;
410 /* BM_ELEM_TAG flag is used to store visited verts */
411 BMFace *f;
412 BMIter fiter;
413 int i = 0;
414
415 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
416 BMIter liter;
417 BMLoop *l;
418 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
419 BM_elem_flag_set(l, BM_ELEM_TAG, !filter_fn(l, user_data));
420 BM_elem_index_set(l, i); /* set_inline */
421 i += 1;
422 }
423 }
424 bm->elem_index_dirty &= ~BM_LOOP;
425
426 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_VERT);
427
428 return path;
429}
430
432 BMElem *ele_src,
433 BMElem *ele_dst,
434 const int cd_loop_uv_offset,
435 bool (*filter_fn)(BMLoop *, void *user_data),
436 void *user_data)
437{
438 LinkNode *path = nullptr;
439 /* BM_ELEM_TAG flag is used to store visited verts */
440 BMFace *f;
441 BMIter fiter;
442 int i = 0;
443
444 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
445 BMIter liter;
446 BMLoop *l;
447 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
448 BM_elem_flag_set(l, BM_ELEM_TAG, !filter_fn(l, user_data));
449 BM_elem_index_set(l, i); /* set_inline */
450 i += 1;
451 }
452 }
453 bm->elem_index_dirty &= ~BM_LOOP;
454
455 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_EDGE);
456
457 return path;
458}
459
461 BMElem *ele_src,
462 BMElem *ele_dst,
463 const int cd_loop_uv_offset,
464 bool (*filter_fn)(BMFace *, void *user_data),
465 void *user_data)
466{
467 LinkNode *path = nullptr;
468 /* BM_ELEM_TAG flag is used to store visited verts */
469 BMFace *f;
470 BMIter fiter;
471 int i;
472
473 /* flush flag to verts */
475
476 BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
477 bool test;
478 BM_elem_flag_set(f, BM_ELEM_TAG, test = !filter_fn(f, user_data));
479
480 /* flush tag to verts */
481 if (test == false) {
482 BMLoop *l_first, *l_iter;
483 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
484 do {
486 } while ((l_iter = l_iter->next) != l_first);
487 }
488 }
489
490 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_FACE);
491
492 return path;
493}
494
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert(a)
Definition BLI_assert.h:46
void copy_vn_i(int *array_tar, int size, int val)
#define ELEM(...)
#define STACK_POP(stack)
#define STACK_CLEAR(stack)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
#define STACK_SWAP(stack_a, stack_b)
Read Guarded memory(de)allocation.
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_TAG
@ BM_LOOP
#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_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_FACE
BMesh * bm
void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
static bool bm_loop_pair_ends(BMLoop *l_pivot, BMLoop *l_end_pair[2])
static LinkNode * mesh_calc_path_region_elem(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, const char path_htype)
LinkNode * BM_mesh_calc_path_uv_region_vert(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, bool(*filter_fn)(BMLoop *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_uv_region_edge(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, bool(*filter_fn)(BMLoop *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_uv_region_face(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, bool(*filter_fn)(BMFace *, void *user_data), void *user_data)
static bool bm_loop_region_test(BMLoop *l, int *const depths[2], const int pass)
static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass)
bool BM_vert_is_edge_pair_manifold(const BMVert *v)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
bool BM_loop_uv_share_vert_check(const BMLoop *l_a, const BMLoop *l_b, const int cd_loop_uv_offset)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
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
BMHeader head
struct BMVert * v
struct BMLoop * prev
struct BMLoop * next
i
Definition text_draw.cc:230