Blender  V2.93
bmesh_path_region.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 
24 #include "MEM_guardedalloc.h"
25 
26 #include "BLI_alloca.h"
27 #include "BLI_linklist.h"
28 #include "BLI_math.h"
29 #include "BLI_utildefines_stack.h"
30 
31 #include "bmesh.h"
32 #include "bmesh_path_region.h" /* own include */
33 
45 #define USE_EDGE_CHAIN
46 
47 #ifdef USE_EDGE_CHAIN
53 static bool bm_vert_pair_ends(BMVert *v_pivot, BMVert *v_end_pair[2])
54 {
55  BMEdge *e = v_pivot->e;
56  int j = 0;
57  do {
58  BMEdge *e_chain = e;
59  BMVert *v_other = BM_edge_other_vert(e_chain, v_pivot);
60  while (BM_vert_is_edge_pair_manifold(v_other)) {
61  BMEdge *e_chain_next = BM_DISK_EDGE_NEXT(e_chain, v_other);
62  BLI_assert(BM_DISK_EDGE_NEXT(e_chain_next, v_other) == e_chain);
63  v_other = BM_edge_other_vert(e_chain_next, v_other);
64  if (v_other == v_pivot) {
65  return false;
66  }
67  e_chain = e_chain_next;
68  }
69  v_end_pair[j++] = v_other;
70  } while ((e = BM_DISK_EDGE_NEXT(e, v_pivot)) != v_pivot->e);
71 
72  BLI_assert(j == 2);
73  return true;
74 }
75 #endif /* USE_EDGE_CHAIN */
76 
77 /* -------------------------------------------------------------------- */
81 static bool bm_vert_region_test(BMVert *v, int *const depths[2], const int pass)
82 {
83  const int index = BM_elem_index_get(v);
84  return (((depths[0][index] != -1) && (depths[1][index] != -1)) &&
85  ((depths[0][index] + depths[1][index]) < pass));
86 }
87 
88 #ifdef USE_EDGE_CHAIN
89 static bool bm_vert_region_test_chain(BMVert *v, int *const depths[2], const int pass)
90 {
91  BMVert *v_end_pair[2];
92  if (bm_vert_region_test(v, depths, pass)) {
93  return true;
94  }
95  if (BM_vert_is_edge_pair_manifold(v) && bm_vert_pair_ends(v, v_end_pair) &&
96  bm_vert_region_test(v_end_pair[0], depths, pass) &&
97  bm_vert_region_test(v_end_pair[1], depths, pass)) {
98  return true;
99  }
100 
101  return false;
102 }
103 #else
104 static bool bm_vert_region_test_chain(BMVert *v, int *const depths[2], const int pass)
105 {
106  return bm_vert_region_test(v, depths, pass);
107 }
108 #endif
109 
126  BMElem *ele_src,
127  BMElem *ele_dst,
128  const char path_htype)
129 {
130  int ele_verts_len[2];
131  BMVert **ele_verts[2];
132 
133  /* Get vertices from any `ele_src/ele_dst` elements. */
134  for (int side = 0; side < 2; side++) {
135  BMElem *ele = side ? ele_dst : ele_src;
136  int j = 0;
137 
138  if (ele->head.htype == BM_FACE) {
139  BMFace *f = (BMFace *)ele;
140  ele_verts[side] = BLI_array_alloca(ele_verts[side], f->len);
141 
142  BMLoop *l_first, *l_iter;
143  l_iter = l_first = BM_FACE_FIRST_LOOP(f);
144  do {
145  ele_verts[side][j++] = l_iter->v;
146  } while ((l_iter = l_iter->next) != l_first);
147  }
148  else if (ele->head.htype == BM_EDGE) {
149  BMEdge *e = (BMEdge *)ele;
150  ele_verts[side] = BLI_array_alloca(ele_verts[side], 2);
151 
152  ele_verts[side][j++] = e->v1;
153  ele_verts[side][j++] = e->v2;
154  }
155  else if (ele->head.htype == BM_VERT) {
156  BMVert *v = (BMVert *)ele;
157  ele_verts[side] = BLI_array_alloca(ele_verts[side], 1);
158 
159  ele_verts[side][j++] = v;
160  }
161  else {
162  BLI_assert(0);
163  }
164  ele_verts_len[side] = j;
165  }
166 
167  int *depths[2] = {NULL};
168  int pass = 0;
169 
170  BMVert **stack = MEM_mallocN(sizeof(*stack) * bm->totvert, __func__);
171  BMVert **stack_other = MEM_mallocN(sizeof(*stack_other) * bm->totvert, __func__);
172 
173  STACK_DECLARE(stack);
174  STACK_INIT(stack, bm->totvert);
175 
176  STACK_DECLARE(stack_other);
177  STACK_INIT(stack_other, bm->totvert);
178 
180 
181  /* After exhausting all possible elements, we should have found all elements on the 'side_other'.
182  * otherwise, exit early. */
183  bool found_all = false;
184 
185  for (int side = 0; side < 2; side++) {
186  const int side_other = !side;
187 
188  /* initialize depths to -1 (un-touched), fill in with the depth as we walk over the edges. */
189  depths[side] = MEM_mallocN(sizeof(*depths[side]) * bm->totvert, __func__);
190  copy_vn_i(depths[side], bm->totvert, -1);
191 
192  /* needed for second side */
193  STACK_CLEAR(stack);
194  STACK_CLEAR(stack_other);
195 
196  for (int i = 0; i < ele_verts_len[side]; i++) {
197  BMVert *v = ele_verts[side][i];
198  depths[side][BM_elem_index_get(v)] = 0;
199  if (v->e && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
200  STACK_PUSH(stack, v);
201  }
202  }
203 
204 #ifdef USE_EDGE_CHAIN
205  /* Expand initial state to end-point vertices when they only have 2x edges,
206  * this prevents odd behavior when source or destination are in the middle
207  * of a long chain of edges. */
208  if (ELEM(path_htype, BM_VERT, BM_EDGE)) {
209  for (int i = 0; i < ele_verts_len[side]; i++) {
210  BMVert *v = ele_verts[side][i];
211  BMVert *v_end_pair[2];
212  if (BM_vert_is_edge_pair_manifold(v) && bm_vert_pair_ends(v, v_end_pair)) {
213  for (int j = 0; j < 2; j++) {
214  const int v_end_index = BM_elem_index_get(v_end_pair[j]);
215  if (depths[side][v_end_index] == -1) {
216  depths[side][v_end_index] = 0;
217  if (!BM_elem_flag_test(v_end_pair[j], BM_ELEM_TAG)) {
218  STACK_PUSH(stack, v_end_pair[j]);
219  }
220  }
221  }
222  }
223  }
224  }
225 #endif /* USE_EDGE_CHAIN */
226 
227  /* Keep walking over connected geometry until we find all the vertices in
228  * `ele_verts[side_other]`, or exit the loop when there's no connection. */
229  found_all = false;
230  for (pass = 1; (STACK_SIZE(stack) != 0); pass++) {
231  while (STACK_SIZE(stack) != 0) {
232  BMVert *v_a = STACK_POP(stack);
233  // const int v_a_index = BM_elem_index_get(v_a); /* only for assert */
234  BMEdge *e = v_a->e;
235 
236  do {
237  BMVert *v_b = BM_edge_other_vert(e, v_a);
238  int v_b_index = BM_elem_index_get(v_b);
239  if (depths[side][v_b_index] == -1) {
240 
241 #ifdef USE_EDGE_CHAIN
242  /* Walk along the chain, fill in values until we reach a vertex with 3+ edges. */
243  {
244  BMEdge *e_chain = e;
245  while (BM_vert_is_edge_pair_manifold(v_b) && ((depths[side][v_b_index] == -1))) {
246  depths[side][v_b_index] = pass;
247 
248  BMEdge *e_chain_next = BM_DISK_EDGE_NEXT(e_chain, v_b);
249  BLI_assert(BM_DISK_EDGE_NEXT(e_chain_next, v_b) == e_chain);
250  v_b = BM_edge_other_vert(e_chain_next, v_b);
251  v_b_index = BM_elem_index_get(v_b);
252  e_chain = e_chain_next;
253  }
254  }
255 #endif /* USE_EDGE_CHAIN */
256 
257  /* Add the other vertex to the stack, to be traversed in the next pass. */
258  if (depths[side][v_b_index] == -1) {
259 #ifdef USE_EDGE_CHAIN
261 #endif
262  BLI_assert(pass == depths[side][BM_elem_index_get(v_a)] + 1);
263  depths[side][v_b_index] = pass;
264  if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
265  STACK_PUSH(stack_other, v_b);
266  }
267  }
268  }
269  } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != v_a->e);
270  }
271 
272  /* Stop searching once there's none left.
273  * Note that this looks in-efficient, however until the target elements reached,
274  * it will exit immediately.
275  * After that, it takes as many passes as the element has edges to finish off. */
276  found_all = true;
277  for (int i = 0; i < ele_verts_len[side_other]; i++) {
278  if (depths[side][BM_elem_index_get(ele_verts[side_other][i])] == -1) {
279  found_all = false;
280  break;
281  }
282  }
283  if (found_all == true) {
284  pass++;
285  break;
286  }
287 
288  STACK_SWAP(stack, stack_other);
289  }
290 
291  /* if we have nothing left, and didn't find all elements on the other side,
292  * exit early and don't continue */
293  if (found_all == false) {
294  break;
295  }
296  }
297 
298  MEM_freeN(stack);
299  MEM_freeN(stack_other);
300 
301  /* Now we have depths recorded from both sides,
302  * select elements that use tagged verts. */
303  LinkNode *path = NULL;
304 
305  if (found_all == false) {
306  /* fail! (do nothing) */
307  }
308  else if (path_htype == BM_FACE) {
309  BMIter fiter;
310  BMFace *f;
311 
312  BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
313  if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
314  /* check all verts in face are tagged */
315  BMLoop *l_first, *l_iter;
316  l_iter = l_first = BM_FACE_FIRST_LOOP(f);
317  bool ok = true;
318 #if 0
319  do {
320  if (!bm_vert_region_test_chain(l_iter->v, depths, pass)) {
321  ok = false;
322  break;
323  }
324  } while ((l_iter = l_iter->next) != l_first);
325 #else
326  /* Allowing a single failure on a face gives fewer 'gaps'.
327  * While correct, in practice they're often part of what
328  * a user would consider the 'region'. */
329  int ok_tests = f->len > 3 ? 1 : 0; /* how many times we may fail */
330  do {
331  if (!bm_vert_region_test_chain(l_iter->v, depths, pass)) {
332  if (ok_tests == 0) {
333  ok = false;
334  break;
335  }
336  ok_tests--;
337  }
338  } while ((l_iter = l_iter->next) != l_first);
339 #endif
340 
341  if (ok) {
342  BLI_linklist_prepend(&path, f);
343  }
344  }
345  }
346  }
347  else if (path_htype == BM_EDGE) {
348  BMIter eiter;
349  BMEdge *e;
350 
351  BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
353  /* check all verts in edge are tagged */
354  bool ok = true;
355  for (int j = 0; j < 2; j++) {
356  if (!bm_vert_region_test_chain(*((&e->v1) + j), depths, pass)) {
357  ok = false;
358  break;
359  }
360  }
361 
362  if (ok) {
363  BLI_linklist_prepend(&path, e);
364  }
365  }
366  }
367  }
368  else if (path_htype == BM_VERT) {
369  BMIter viter;
370  BMVert *v;
371 
372  BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
373  if (bm_vert_region_test_chain(v, depths, pass)) {
374  BLI_linklist_prepend(&path, v);
375  }
376  }
377  }
378 
379  for (int side = 0; side < 2; side++) {
380  if (depths[side]) {
381  MEM_freeN(depths[side]);
382  }
383  }
384 
385  return path;
386 }
387 
388 #undef USE_EDGE_CHAIN
389 
390 /* -------------------------------------------------------------------- */
395  BMElem *ele_src,
396  BMElem *ele_dst,
397  bool (*filter_fn)(BMVert *, void *user_data),
398  void *user_data)
399 {
400  LinkNode *path = NULL;
401  /* BM_ELEM_TAG flag is used to store visited verts */
402  BMVert *v;
403  BMIter viter;
404  int i;
405 
406  BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
407  BM_elem_flag_set(v, BM_ELEM_TAG, !filter_fn(v, user_data));
408  BM_elem_index_set(v, i); /* set_inline */
409  }
411 
412  path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_VERT);
413 
414  return path;
415 }
416 
418  BMElem *ele_src,
419  BMElem *ele_dst,
420  bool (*filter_fn)(BMEdge *, void *user_data),
421  void *user_data)
422 {
423  LinkNode *path = NULL;
424  /* BM_ELEM_TAG flag is used to store visited verts */
425  BMEdge *e;
426  BMIter eiter;
427  int i;
428 
429  /* flush flag to verts */
431 
432  BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
433  bool test;
434  BM_elem_flag_set(e, BM_ELEM_TAG, test = !filter_fn(e, user_data));
435 
436  /* flush tag to verts */
437  if (test == false) {
438  for (int j = 0; j < 2; j++) {
439  BM_elem_flag_disable(*((&e->v1) + j), BM_ELEM_TAG);
440  }
441  }
442  }
443 
444  path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_EDGE);
445 
446  return path;
447 }
448 
450  BMElem *ele_src,
451  BMElem *ele_dst,
452  bool (*filter_fn)(BMFace *, void *user_data),
453  void *user_data)
454 {
455  LinkNode *path = NULL;
456  /* BM_ELEM_TAG flag is used to store visited verts */
457  BMFace *f;
458  BMIter fiter;
459  int i;
460 
461  /* flush flag to verts */
463 
464  BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
465  bool test;
466  BM_elem_flag_set(f, BM_ELEM_TAG, test = !filter_fn(f, user_data));
467 
468  /* flush tag to verts */
469  if (test == false) {
470  BMLoop *l_first, *l_iter;
471  l_iter = l_first = BM_FACE_FIRST_LOOP(f);
472  do {
474  } while ((l_iter = l_iter->next) != l_first);
475  }
476  }
477 
478  path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_FACE);
479 
480  return path;
481 }
482 
#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
#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.
#define BM_DISK_EDGE_NEXT(e, v)
Definition: bmesh_class.h:556
@ 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_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
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_vert_region_test(BMVert *v, int *const depths[2], const int pass)
static bool bm_vert_pair_ends(BMVert *v_pivot, BMVert *v_end_pair[2])
static bool bm_vert_region_test_chain(BMVert *v, int *const depths[2], const int pass)
LinkNode * BM_mesh_calc_path_region_edge(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, bool(*filter_fn)(BMEdge *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_region_vert(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, bool(*filter_fn)(BMVert *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_region_face(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, bool(*filter_fn)(BMFace *, void *user_data), void *user_data)
static LinkNode * mesh_calc_path_region_elem(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const char path_htype)
bool BM_vert_is_edge_pair_manifold(const BMVert *v)
Definition: bmesh_query.c:785
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
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 * next
Definition: bmesh_class.h:245
struct BMEdge * e
Definition: bmesh_class.h:109
int totvert
Definition: bmesh_class.h:297
char elem_index_dirty
Definition: bmesh_class.h:305