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 
17 /** \file
18  * \ingroup bmesh
19  *
20  * Find the region defined by the path(s) between 2 elements.
21  * (path isn't ordered).
22  */
23 
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 
34 /**
35  * Special handling of vertices with 2 edges
36  * (act as if the edge-chain is a single edge).
37  *
38  * \note Regarding manifold edge stepping: #BM_vert_is_edge_pair_manifold usage.
39  * Logic to skip a chain of vertices is not applied at boundaries because it gives
40  * strange behavior from a user perspective especially with boundary quads, see: T52701
41  *
42  * Restrict walking over a vertex chain to cases where the edges share the same faces.
43  * This is more typical of what a user would consider a vertex chain.
44  */
45 #define USE_EDGE_CHAIN
46 
47 #ifdef USE_EDGE_CHAIN
48 /**
49  * Takes a vertex with 2 edge users and assigns the vertices at each end-point,
50  *
51  * \return Success when \a v_end_pair values are set or false if the edges loop back on themselves.
52  */
bm_vert_pair_ends(BMVert * v_pivot,BMVert * v_end_pair[2])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 /** \name Vertex in Region Checks
78  * \{ */
79 
bm_vert_region_test(BMVert * v,int * const depths[2],const int pass)80 static bool bm_vert_region_test(BMVert *v, int *const depths[2], const int pass)
81 {
82   const int index = BM_elem_index_get(v);
83   return (((depths[0][index] != -1) && (depths[1][index] != -1)) &&
84           ((depths[0][index] + depths[1][index]) < pass));
85 }
86 
87 #ifdef USE_EDGE_CHAIN
bm_vert_region_test_chain(BMVert * v,int * const depths[2],const int pass)88 static bool bm_vert_region_test_chain(BMVert *v, int *const depths[2], const int pass)
89 {
90   BMVert *v_end_pair[2];
91   if (bm_vert_region_test(v, depths, pass)) {
92     return true;
93   }
94   if (BM_vert_is_edge_pair_manifold(v) && bm_vert_pair_ends(v, v_end_pair) &&
95       bm_vert_region_test(v_end_pair[0], depths, pass) &&
96       bm_vert_region_test(v_end_pair[1], depths, pass)) {
97     return true;
98   }
99 
100   return false;
101 }
102 #else
bm_vert_region_test_chain(BMVert * v,int * const depths[2],const int pass)103 static bool bm_vert_region_test_chain(BMVert *v, int *const depths[2], const int pass)
104 {
105   return bm_vert_region_test(v, depths, pass);
106 }
107 #endif
108 
109 /** \} */
110 
111 /**
112  * Main logic for calculating region between 2 elements.
113  *
114  * This method works walking (breadth first) over all vertices,
115  * keeping track of topological distance from the source.
116  *
117  * This is done in both directions, after that each vertices 'depth' is added to check
118  * if its less than the number of passes needed to complete the search.
119  * When it is, we know the path is one of possible paths
120  * that have the minimum topological distance.
121  *
122  * \note Only verts without BM_ELEM_TAG will be walked over.
123  */
mesh_calc_path_region_elem(BMesh * bm,BMElem * ele_src,BMElem * ele_dst,const char path_htype)124 static LinkNode *mesh_calc_path_region_elem(BMesh *bm,
125                                             BMElem *ele_src,
126                                             BMElem *ele_dst,
127                                             const char path_htype)
128 {
129   int ele_verts_len[2];
130   BMVert **ele_verts[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_verts[side] = BLI_array_alloca(ele_verts[side], f->len);
140 
141       BMLoop *l_first, *l_iter;
142       l_iter = l_first = BM_FACE_FIRST_LOOP(f);
143       do {
144         ele_verts[side][j++] = l_iter->v;
145       } while ((l_iter = l_iter->next) != l_first);
146     }
147     else if (ele->head.htype == BM_EDGE) {
148       BMEdge *e = (BMEdge *)ele;
149       ele_verts[side] = BLI_array_alloca(ele_verts[side], 2);
150 
151       ele_verts[side][j++] = e->v1;
152       ele_verts[side][j++] = e->v2;
153     }
154     else if (ele->head.htype == BM_VERT) {
155       BMVert *v = (BMVert *)ele;
156       ele_verts[side] = BLI_array_alloca(ele_verts[side], 1);
157 
158       ele_verts[side][j++] = v;
159     }
160     else {
161       BLI_assert(0);
162     }
163     ele_verts_len[side] = j;
164   }
165 
166   int *depths[2] = {NULL};
167   int pass = 0;
168 
169   BMVert **stack = MEM_mallocN(sizeof(*stack) * bm->totvert, __func__);
170   BMVert **stack_other = MEM_mallocN(sizeof(*stack_other) * bm->totvert, __func__);
171 
172   STACK_DECLARE(stack);
173   STACK_INIT(stack, bm->totvert);
174 
175   STACK_DECLARE(stack_other);
176   STACK_INIT(stack_other, bm->totvert);
177 
178   BM_mesh_elem_index_ensure(bm, BM_VERT);
179 
180   /* After exhausting all possible elements, we should have found all elements on the 'side_other'.
181    * otherwise, exit early. */
182   bool found_all = false;
183 
184   for (int side = 0; side < 2; side++) {
185     const int side_other = !side;
186 
187     /* initialize depths to -1 (un-touched), fill in with the depth as we walk over the edges. */
188     depths[side] = MEM_mallocN(sizeof(*depths[side]) * bm->totvert, __func__);
189     copy_vn_i(depths[side], bm->totvert, -1);
190 
191     /* needed for second side */
192     STACK_CLEAR(stack);
193     STACK_CLEAR(stack_other);
194 
195     for (int i = 0; i < ele_verts_len[side]; i++) {
196       BMVert *v = ele_verts[side][i];
197       depths[side][BM_elem_index_get(v)] = 0;
198       if (v->e && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
199         STACK_PUSH(stack, v);
200       }
201     }
202 
203 #ifdef USE_EDGE_CHAIN
204     /* Expand initial state to end-point vertices when they only have 2x edges,
205      * this prevents odd behavior when source or destination are in the middle
206      * of a long chain of edges. */
207     if (ELEM(path_htype, BM_VERT, BM_EDGE)) {
208       for (int i = 0; i < ele_verts_len[side]; i++) {
209         BMVert *v = ele_verts[side][i];
210         BMVert *v_end_pair[2];
211         if (BM_vert_is_edge_pair_manifold(v) && bm_vert_pair_ends(v, v_end_pair)) {
212           for (int j = 0; j < 2; j++) {
213             const int v_end_index = BM_elem_index_get(v_end_pair[j]);
214             if (depths[side][v_end_index] == -1) {
215               depths[side][v_end_index] = 0;
216               if (!BM_elem_flag_test(v_end_pair[j], BM_ELEM_TAG)) {
217                 STACK_PUSH(stack, v_end_pair[j]);
218               }
219             }
220           }
221         }
222       }
223     }
224 #endif /* USE_EDGE_CHAIN */
225 
226     /* Keep walking over connected geometry until we find all the vertices in
227      * `ele_verts[side_other]`, or exit the loop when there's no connection. */
228     found_all = false;
229     for (pass = 1; (STACK_SIZE(stack) != 0); pass++) {
230       while (STACK_SIZE(stack) != 0) {
231         BMVert *v_a = STACK_POP(stack);
232         // const int v_a_index = BM_elem_index_get(v_a);  /* only for assert */
233         BMEdge *e = v_a->e;
234 
235         do {
236           BMVert *v_b = BM_edge_other_vert(e, v_a);
237           int v_b_index = BM_elem_index_get(v_b);
238           if (depths[side][v_b_index] == -1) {
239 
240 #ifdef USE_EDGE_CHAIN
241             /* Walk along the chain, fill in values until we reach a vertex with 3+ edges. */
242             {
243               BMEdge *e_chain = e;
244               while (BM_vert_is_edge_pair_manifold(v_b) && ((depths[side][v_b_index] == -1))) {
245                 depths[side][v_b_index] = pass;
246 
247                 BMEdge *e_chain_next = BM_DISK_EDGE_NEXT(e_chain, v_b);
248                 BLI_assert(BM_DISK_EDGE_NEXT(e_chain_next, v_b) == e_chain);
249                 v_b = BM_edge_other_vert(e_chain_next, v_b);
250                 v_b_index = BM_elem_index_get(v_b);
251                 e_chain = e_chain_next;
252               }
253             }
254 #endif /* USE_EDGE_CHAIN */
255 
256             /* Add the other vertex to the stack, to be traversed in the next pass. */
257             if (depths[side][v_b_index] == -1) {
258 #ifdef USE_EDGE_CHAIN
259               BLI_assert(!BM_vert_is_edge_pair_manifold(v_b));
260 #endif
261               BLI_assert(pass == depths[side][BM_elem_index_get(v_a)] + 1);
262               depths[side][v_b_index] = pass;
263               if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
264                 STACK_PUSH(stack_other, v_b);
265               }
266             }
267           }
268         } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != v_a->e);
269       }
270 
271       /* Stop searching once there's none left.
272        * Note that this looks in-efficient, however until the target elements reached,
273        * it will exit immediately.
274        * After that, it takes as many passes as the element has edges to finish off. */
275       found_all = true;
276       for (int i = 0; i < ele_verts_len[side_other]; i++) {
277         if (depths[side][BM_elem_index_get(ele_verts[side_other][i])] == -1) {
278           found_all = false;
279           break;
280         }
281       }
282       if (found_all == true) {
283         pass++;
284         break;
285       }
286 
287       STACK_SWAP(stack, stack_other);
288     }
289 
290     /* if we have nothing left, and didn't find all elements on the other side,
291      * exit early and don't continue */
292     if (found_all == false) {
293       break;
294     }
295   }
296 
297   MEM_freeN(stack);
298   MEM_freeN(stack_other);
299 
300   /* Now we have depths recorded from both sides,
301    * select elements that use tagged verts. */
302   LinkNode *path = NULL;
303 
304   if (found_all == false) {
305     /* fail! (do nothing) */
306   }
307   else if (path_htype == BM_FACE) {
308     BMIter fiter;
309     BMFace *f;
310 
311     BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
312       if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
313         /* check all verts in face are tagged */
314         BMLoop *l_first, *l_iter;
315         l_iter = l_first = BM_FACE_FIRST_LOOP(f);
316         bool ok = true;
317 #if 0
318         do {
319           if (!bm_vert_region_test_chain(l_iter->v, depths, pass)) {
320             ok = false;
321             break;
322           }
323         } while ((l_iter = l_iter->next) != l_first);
324 #else
325         /* Allowing a single failure on a face gives fewer 'gaps'.
326          * While correct, in practice they're often part of what
327          * a user would consider the 'region'. */
328         int ok_tests = f->len > 3 ? 1 : 0; /* how many times we may fail */
329         do {
330           if (!bm_vert_region_test_chain(l_iter->v, depths, pass)) {
331             if (ok_tests == 0) {
332               ok = false;
333               break;
334             }
335             ok_tests--;
336           }
337         } while ((l_iter = l_iter->next) != l_first);
338 #endif
339 
340         if (ok) {
341           BLI_linklist_prepend(&path, f);
342         }
343       }
344     }
345   }
346   else if (path_htype == BM_EDGE) {
347     BMIter eiter;
348     BMEdge *e;
349 
350     BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
351       if (!BM_elem_flag_test(e, BM_ELEM_TAG)) {
352         /* check all verts in edge are tagged */
353         bool ok = true;
354         for (int j = 0; j < 2; j++) {
355           if (!bm_vert_region_test_chain(*((&e->v1) + j), depths, pass)) {
356             ok = false;
357             break;
358           }
359         }
360 
361         if (ok) {
362           BLI_linklist_prepend(&path, e);
363         }
364       }
365     }
366   }
367   else if (path_htype == BM_VERT) {
368     BMIter viter;
369     BMVert *v;
370 
371     BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
372       if (bm_vert_region_test_chain(v, depths, pass)) {
373         BLI_linklist_prepend(&path, v);
374       }
375     }
376   }
377 
378   for (int side = 0; side < 2; side++) {
379     if (depths[side]) {
380       MEM_freeN(depths[side]);
381     }
382   }
383 
384   return path;
385 }
386 
387 #undef USE_EDGE_CHAIN
388 
389 /** \name Main Functions (exposed externally).
390  * \{ */
391 
BM_mesh_calc_path_region_vert(BMesh * bm,BMElem * ele_src,BMElem * ele_dst,bool (* filter_fn)(BMVert *,void * user_data),void * user_data)392 LinkNode *BM_mesh_calc_path_region_vert(BMesh *bm,
393                                         BMElem *ele_src,
394                                         BMElem *ele_dst,
395                                         bool (*filter_fn)(BMVert *, void *user_data),
396                                         void *user_data)
397 {
398   LinkNode *path = NULL;
399   /* BM_ELEM_TAG flag is used to store visited verts */
400   BMVert *v;
401   BMIter viter;
402   int i;
403 
404   BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
405     BM_elem_flag_set(v, BM_ELEM_TAG, !filter_fn(v, user_data));
406     BM_elem_index_set(v, i); /* set_inline */
407   }
408   bm->elem_index_dirty &= ~BM_VERT;
409 
410   path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_VERT);
411 
412   return path;
413 }
414 
BM_mesh_calc_path_region_edge(BMesh * bm,BMElem * ele_src,BMElem * ele_dst,bool (* filter_fn)(BMEdge *,void * user_data),void * user_data)415 LinkNode *BM_mesh_calc_path_region_edge(BMesh *bm,
416                                         BMElem *ele_src,
417                                         BMElem *ele_dst,
418                                         bool (*filter_fn)(BMEdge *, void *user_data),
419                                         void *user_data)
420 {
421   LinkNode *path = NULL;
422   /* BM_ELEM_TAG flag is used to store visited verts */
423   BMEdge *e;
424   BMIter eiter;
425   int i;
426 
427   /* flush flag to verts */
428   BM_mesh_elem_hflag_enable_all(bm, BM_VERT, BM_ELEM_TAG, false);
429 
430   BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
431     bool test;
432     BM_elem_flag_set(e, BM_ELEM_TAG, test = !filter_fn(e, user_data));
433 
434     /* flush tag to verts */
435     if (test == false) {
436       for (int j = 0; j < 2; j++) {
437         BM_elem_flag_disable(*((&e->v1) + j), BM_ELEM_TAG);
438       }
439     }
440   }
441 
442   path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_EDGE);
443 
444   return path;
445 }
446 
BM_mesh_calc_path_region_face(BMesh * bm,BMElem * ele_src,BMElem * ele_dst,bool (* filter_fn)(BMFace *,void * user_data),void * user_data)447 LinkNode *BM_mesh_calc_path_region_face(BMesh *bm,
448                                         BMElem *ele_src,
449                                         BMElem *ele_dst,
450                                         bool (*filter_fn)(BMFace *, void *user_data),
451                                         void *user_data)
452 {
453   LinkNode *path = NULL;
454   /* BM_ELEM_TAG flag is used to store visited verts */
455   BMFace *f;
456   BMIter fiter;
457   int i;
458 
459   /* flush flag to verts */
460   BM_mesh_elem_hflag_enable_all(bm, BM_VERT, BM_ELEM_TAG, false);
461 
462   BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
463     bool test;
464     BM_elem_flag_set(f, BM_ELEM_TAG, test = !filter_fn(f, user_data));
465 
466     /* flush tag to verts */
467     if (test == false) {
468       BMLoop *l_first, *l_iter;
469       l_iter = l_first = BM_FACE_FIRST_LOOP(f);
470       do {
471         BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG);
472       } while ((l_iter = l_iter->next) != l_first);
473     }
474   }
475 
476   path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_FACE);
477 
478   return path;
479 }
480 
481 /** \} */
482