Blender  V2.93
bmo_rotate_edges.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 
23 #include "MEM_guardedalloc.h"
24 
25 #include "BLI_heap.h"
26 #include "BLI_math.h"
27 
28 #include "bmesh.h"
29 
30 #include "intern/bmesh_operators_private.h" /* own include */
31 
32 #define EDGE_OUT 1
33 #define FACE_MARK 1
34 
39  BMOperator *op,
40  const short check_flag,
41  const bool use_ccw)
42 {
43  BMOIter siter;
44  BMEdge *e;
45 
46  BMO_ITER (e, &siter, op->slots_in, "edges", BM_EDGE) {
50  if (BM_edge_rotate_check(e)) {
51  BMEdge *e_rotate = BM_edge_rotate(bm, e, use_ccw, check_flag);
52  if (e_rotate != NULL) {
53  BMO_edge_flag_enable(bm, e_rotate, EDGE_OUT);
54  }
55  }
56  }
57 }
58 
64 static float bm_edge_calc_rotate_cost(const BMEdge *e)
65 {
67 }
68 
72 static float bm_edge_rotate_is_boundary(const BMEdge *e)
73 {
74  /* Number of adjacent shared faces. */
75  int count = 0;
76  BMLoop *l_radial_iter = e->l;
77  do {
78  /* Skip this edge. */
79  BMLoop *l_iter = l_radial_iter->next;
80  do {
81  BMEdge *e_iter = l_iter->e;
82  const int e_iter_index = BM_elem_index_get(e_iter);
83  if ((e_iter_index != -1)) {
84  if (count == 1) {
85  return false;
86  }
87  count += 1;
88  break;
89  }
90  } while ((l_iter = l_iter->next) != l_radial_iter);
91  } while ((l_radial_iter = l_radial_iter->radial_next) != e->l);
92  return true;
93 }
94 
100  BMesh *bm, BMOperator *op, short check_flag, const bool use_ccw, const int edges_len)
101 {
102  Heap *heap = BLI_heap_new_ex(edges_len);
103  HeapNode **eheap_table = MEM_mallocN(sizeof(*eheap_table) * edges_len, __func__);
104  int edges_len_rotate = 0;
105 
106  {
107  BMIter iter;
108  BMEdge *e;
109  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
110  BM_elem_index_set(e, -1); /* set_dirty! */
111  }
113  }
114 
115  {
116  BMOIter siter;
117  BMEdge *e;
118  uint i;
119  BMO_ITER_INDEX (e, &siter, op->slots_in, "edges", BM_EDGE, i) {
120  BM_elem_index_set(e, BM_edge_is_manifold(e) ? i : -1); /* set_dirty! */
121  eheap_table[i] = NULL;
122  }
123  }
124 
125  /* First operate on boundary edges, this is often all that's needed,
126  * regions that have no boundaries are handles after. */
127  enum {
128  PASS_TYPE_BOUNDARY = 0,
129  PASS_TYPE_ALL = 1,
130  PASS_TYPE_DONE = 2,
131  };
132  uint pass_type = PASS_TYPE_BOUNDARY;
133 
134  while ((pass_type != PASS_TYPE_DONE) && (edges_len_rotate != edges_len)) {
136  {
137  BMOIter siter;
138  BMEdge *e;
139  uint i;
140  BMO_ITER_INDEX (e, &siter, op->slots_in, "edges", BM_EDGE, i) {
141  BLI_assert(eheap_table[i] == NULL);
142 
143  bool ok = (BM_elem_index_get(e) != -1) && BM_edge_rotate_check(e);
144 
145  if (ok) {
146  if (pass_type == PASS_TYPE_BOUNDARY) {
148  }
149  }
150 
151  if (ok) {
152  float cost = bm_edge_calc_rotate_cost(e);
153  if (pass_type == PASS_TYPE_BOUNDARY) {
154  /* Trick to ensure once started,
155  * non boundaries are handled before other boundary edges.
156  * This means the first longest boundary defines the starting point which is rotated
157  * until all its connected edges are exhausted
158  * and the next boundary is popped off the heap.
159  *
160  * Without this we may rotate from different starting points and meet in the middle
161  * with obviously uneven topology.
162  *
163  * Move from negative to positive value,
164  * inverting so large values are still handled first.
165  */
166  cost = cost != 0.0f ? -1.0f / cost : FLT_MAX;
167  }
168  eheap_table[i] = BLI_heap_insert(heap, cost, e);
169  }
170  }
171  }
172 
173  if (BLI_heap_is_empty(heap)) {
174  pass_type += 1;
175  continue;
176  }
177 
178  const int edges_len_rotate_prev = edges_len_rotate;
179  while (!BLI_heap_is_empty(heap)) {
180  BMEdge *e_best = BLI_heap_pop_min(heap);
181  eheap_table[BM_elem_index_get(e_best)] = NULL;
182 
183  /* No problem if this fails, re-evaluate if faces connected to this edge are touched. */
184  if (BM_edge_rotate_check(e_best)) {
185  BMEdge *e_rotate = BM_edge_rotate(bm, e_best, use_ccw, check_flag);
186  if (e_rotate != NULL) {
187  BMO_edge_flag_enable(bm, e_rotate, EDGE_OUT);
188 
189  /* invalidate so we don't try touch this again. */
190  BM_elem_index_set(e_rotate, -1); /* set_dirty! */
191 
192  edges_len_rotate += 1;
193 
194  /* Note: we could validate all edges which have not been rotated
195  * (not just previously degenerate edges).
196  * However there is no real need -
197  * they can be left until they're popped off the queue. */
198 
199  /* We don't know the exact topology after rotating the edge,
200  * so loop over all faces attached to the new edge,
201  * typically this will only be two faces. */
202  BMLoop *l_radial_iter = e_rotate->l;
203  do {
204  /* Skip this edge. */
205  BMLoop *l_iter = l_radial_iter->next;
206  do {
207  BMEdge *e_iter = l_iter->e;
208  const int e_iter_index = BM_elem_index_get(e_iter);
209  if ((e_iter_index != -1) && (eheap_table[e_iter_index] == NULL)) {
210  if (BM_edge_rotate_check(e_iter)) {
211  /* Previously degenerate, now valid. */
212  float cost = bm_edge_calc_rotate_cost(e_iter);
213  eheap_table[e_iter_index] = BLI_heap_insert(heap, cost, e_iter);
214  }
215  }
216  } while ((l_iter = l_iter->next) != l_radial_iter);
217  } while ((l_radial_iter = l_radial_iter->radial_next) != e_rotate->l);
218  }
219  }
220  }
221 
222  /* If no actions were taken, move onto the next pass. */
223  if (edges_len_rotate == edges_len_rotate_prev) {
224  pass_type += 1;
225  continue;
226  }
227  }
228 
229  BLI_heap_free(heap, NULL);
230  MEM_freeN(eheap_table);
231 }
232 
234 {
235  BMOIter siter;
236  BMEdge *e;
237  const int edges_len = BMO_slot_buffer_count(op->slots_in, "edges");
238  const bool use_ccw = BMO_slot_bool_get(op->slots_in, "use_ccw");
239  const bool is_single = (edges_len == 1);
240  short check_flag = is_single ? BM_EDGEROT_CHECK_EXISTS :
242 
243  bool is_simple = true;
244  if (is_single == false) {
245  BMO_ITER (e, &siter, op->slots_in, "edges", BM_EDGE) {
246  BMFace *f_pair[2];
247  if (BM_edge_face_pair(e, &f_pair[0], &f_pair[1])) {
248  for (uint i = 0; i < ARRAY_SIZE(f_pair); i += 1) {
249  if (BMO_face_flag_test(bm, f_pair[i], FACE_MARK)) {
250  is_simple = false;
251  break;
252  }
253  BMO_face_flag_enable(bm, f_pair[i], FACE_MARK);
254  }
255  if (is_simple == false) {
256  break;
257  }
258  }
259  }
260  }
261 
262  if (is_simple) {
263  bm_rotate_edges_simple(bm, op, check_flag, use_ccw);
264  }
265  else {
266  bm_rotate_edges_shared(bm, op, check_flag, use_ccw, edges_len);
267  }
268 
270 }
#define BLI_assert(a)
Definition: BLI_assert.h:58
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition: BLI_heap.c:221
Heap * BLI_heap_new_ex(unsigned int tot_reserve) ATTR_WARN_UNUSED_RESULT
Definition: BLI_heap.c:201
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition: BLI_heap.c:305
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition: BLI_heap.c:338
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition: BLI_heap.c:268
unsigned int uint
Definition: BLI_sys_types.h:83
#define ARRAY_SIZE(arr)
Read Guarded memory(de)allocation.
@ BM_EDGE
Definition: bmesh_class.h:384
#define BM_elem_index_get(ele)
Definition: bmesh_inline.h:124
#define BM_elem_index_set(ele, index)
Definition: bmesh_inline.h:125
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
bool BM_edge_rotate_check(BMEdge *e)
Check if Rotate Edge is OK.
Definition: bmesh_mods.c:834
BMEdge * BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag)
Rotate Edge.
Definition: bmesh_mods.c:983
@ BM_EDGEROT_CHECK_EXISTS
Definition: bmesh_mods.h:86
@ BM_EDGEROT_CHECK_DEGENERATE
Definition: bmesh_mods.h:90
#define BMO_ITER_INDEX(ele, iter, slot_args, slot_name, restrict_flag, i_)
#define BMO_edge_flag_enable(bm, e, oflag)
#define BMO_face_flag_enable(bm, e, oflag)
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
int BMO_slot_buffer_count(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
#define BMO_face_flag_test(bm, e, oflag)
void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char htype, const short oflag)
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
float BM_edge_calc_length_squared(const BMEdge *e)
Definition: bmesh_query.c:721
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
Definition: bmesh_query.c:732
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
static float bm_edge_calc_rotate_cost(const BMEdge *e)
static void bm_rotate_edges_shared(BMesh *bm, BMOperator *op, short check_flag, const bool use_ccw, const int edges_len)
static void bm_rotate_edges_simple(BMesh *bm, BMOperator *op, const short check_flag, const bool use_ccw)
#define FACE_MARK
#define EDGE_OUT
void bmo_rotate_edges_exec(BMesh *bm, BMOperator *op)
static float bm_edge_rotate_is_boundary(const BMEdge *e)
int count
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
struct BMLoop * l
Definition: bmesh_class.h:140
struct BMEdge * e
Definition: bmesh_class.h:176
struct BMLoop * radial_next
Definition: bmesh_class.h:216
struct BMLoop * next
Definition: bmesh_class.h:245
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
char elem_index_dirty
Definition: bmesh_class.h:305
Definition: BLI_heap.c:57