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