Blender V4.3
bmo_rotate_edges.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
11#include "MEM_guardedalloc.h"
12
13#include "BLI_heap.h"
14
15#include "bmesh.hh"
16
17#include "intern/bmesh_operators_private.hh" /* own include */
18
19#define EDGE_OUT 1
20#define FACE_MARK 1
21
26 BMOperator *op,
27 const short check_flag,
28 const bool use_ccw)
29{
30 BMOIter siter;
31 BMEdge *e;
32
33 BMO_ITER (e, &siter, op->slots_in, "edges", BM_EDGE) {
34 /* This ends up being called twice, could add option to not to call check in
35 * #BM_edge_rotate to get some extra speed. */
37 BMEdge *e_rotate = BM_edge_rotate(bm, e, use_ccw, check_flag);
38 if (e_rotate != nullptr) {
40 }
41 }
42 }
43}
44
50static float bm_edge_calc_rotate_cost(const BMEdge *e)
51{
53}
54
59{
60 /* Number of adjacent shared faces. */
61 int count = 0;
62 BMLoop *l_radial_iter = e->l;
63 do {
64 /* Skip this edge. */
65 BMLoop *l_iter = l_radial_iter->next;
66 do {
67 BMEdge *e_iter = l_iter->e;
68 const int e_iter_index = BM_elem_index_get(e_iter);
69 if (e_iter_index != -1) {
70 if (count == 1) {
71 return false;
72 }
73 count += 1;
74 break;
75 }
76 } while ((l_iter = l_iter->next) != l_radial_iter);
77 } while ((l_radial_iter = l_radial_iter->radial_next) != e->l);
78 return true;
79}
80
86 BMesh *bm, BMOperator *op, short check_flag, const bool use_ccw, const int edges_len)
87{
88 Heap *heap = BLI_heap_new_ex(edges_len);
89 HeapNode **eheap_table = static_cast<HeapNode **>(
90 MEM_mallocN(sizeof(*eheap_table) * edges_len, __func__));
91
92 BMEdge **edges = reinterpret_cast<BMEdge **>(
94 int edges_len_rotate = 0;
95
96 /* Never read edges with this value in the `eheap_table` since they have been freed. */
97 HeapNode *edge_free_id = reinterpret_cast<HeapNode *>(uintptr_t(-1));
98
99 {
100 BMIter iter;
101 BMEdge *e;
102 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
103 BM_elem_index_set(e, -1); /* set_dirty! */
104 }
106 }
107
108 for (int i = 0; i < edges_len; i++) {
109 BMEdge *e = edges[i];
110 BM_elem_index_set(e, BM_edge_is_manifold(e) ? i : -1); /* set_dirty! */
111 eheap_table[i] = nullptr;
112 }
113
114 /* First operate on boundary edges, this is often all that's needed,
115 * regions that have no boundaries are handles after. */
116 enum {
117 PASS_TYPE_BOUNDARY = 0,
118 PASS_TYPE_ALL = 1,
119 PASS_TYPE_DONE = 2,
120 };
121 uint pass_type = PASS_TYPE_BOUNDARY;
122
123 while ((pass_type != PASS_TYPE_DONE) && (edges_len_rotate != edges_len)) {
125 {
126 for (int i = 0; i < edges_len; i++) {
127 if (eheap_table[i] == edge_free_id) {
128 /* `e` is freed. */
129 continue;
130 }
131 BMEdge *e = edges[i];
132 BLI_assert(eheap_table[i] == nullptr);
133
134 bool ok = (BM_elem_index_get(e) != -1) && BM_edge_rotate_check(e);
135
136 if (ok) {
137 if (pass_type == PASS_TYPE_BOUNDARY) {
139 }
140 }
141
142 if (ok) {
143 float cost = bm_edge_calc_rotate_cost(e);
144 if (pass_type == PASS_TYPE_BOUNDARY) {
145 /* Trick to ensure once started,
146 * non boundaries are handled before other boundary edges.
147 * This means the first longest boundary defines the starting point which is rotated
148 * until all its connected edges are exhausted
149 * and the next boundary is popped off the heap.
150 *
151 * Without this we may rotate from different starting points and meet in the middle
152 * with obviously uneven topology.
153 *
154 * Move from negative to positive value,
155 * inverting so large values are still handled first.
156 */
157 cost = cost != 0.0f ? -1.0f / cost : FLT_MAX;
158 }
159 eheap_table[i] = BLI_heap_insert(heap, cost, e);
160 }
161 }
162 }
163
164 if (BLI_heap_is_empty(heap)) {
165 pass_type += 1;
166 continue;
167 }
168
169 const int edges_len_rotate_prev = edges_len_rotate;
170 while (!BLI_heap_is_empty(heap)) {
171 BMEdge *e_best = static_cast<BMEdge *>(BLI_heap_pop_min(heap));
172 const int e_best_index = BM_elem_index_get(e_best);
173 eheap_table[e_best_index] = nullptr;
174
175 /* No problem if this fails, re-evaluate if faces connected to this edge are touched. */
176 if (BM_edge_rotate_check(e_best)) {
177 BMEdge *e_rotate = BM_edge_rotate(bm, e_best, use_ccw, check_flag);
178 if (e_rotate != nullptr) {
179 BMO_edge_flag_enable(bm, e_rotate, EDGE_OUT);
180
181 /* invalidate so we don't try touch this again. */
182 BM_elem_index_set(e_rotate, -1); /* set_dirty! */
183 /* If rotate succeeds, the edge has been freed. */
184 eheap_table[e_best_index] = edge_free_id;
185
186 edges_len_rotate += 1;
187
188 /* NOTE: we could validate all edges which have not been rotated
189 * (not just previously degenerate edges).
190 * However there is no real need -
191 * they can be left until they're popped off the queue. */
192
193 /* We don't know the exact topology after rotating the edge,
194 * so loop over all faces attached to the new edge,
195 * typically this will only be two faces. */
196 BMLoop *l_radial_iter = e_rotate->l;
197 do {
198 /* Skip this edge. */
199 BMLoop *l_iter = l_radial_iter->next;
200 do {
201 BMEdge *e_iter = l_iter->e;
202 const int e_iter_index = BM_elem_index_get(e_iter);
203 if ((e_iter_index != -1) && (eheap_table[e_iter_index] == nullptr)) {
204 /* Once freed, they cannot be accessed via connected geometry. */
205 BLI_assert((eheap_table[e_iter_index] != edge_free_id));
206 if (BM_edge_rotate_check(e_iter)) {
207 /* Previously degenerate, now valid. */
208 float cost = bm_edge_calc_rotate_cost(e_iter);
209 eheap_table[e_iter_index] = BLI_heap_insert(heap, cost, e_iter);
210 }
211 }
212 } while ((l_iter = l_iter->next) != l_radial_iter);
213 } while ((l_radial_iter = l_radial_iter->radial_next) != e_rotate->l);
214 }
215 }
216 }
217
218 /* If no actions were taken, move onto the next pass. */
219 if (edges_len_rotate == edges_len_rotate_prev) {
220 pass_type += 1;
221 continue;
222 }
223 }
224
225 BLI_heap_free(heap, nullptr);
226 MEM_freeN(eheap_table);
227}
228
230{
231 BMOIter siter;
232 BMEdge *e;
233 const int edges_len = BMO_slot_buffer_len(op->slots_in, "edges");
234 const bool use_ccw = BMO_slot_bool_get(op->slots_in, "use_ccw");
235 const bool is_single = (edges_len == 1);
236 short check_flag = is_single ? BM_EDGEROT_CHECK_EXISTS :
238
239 bool is_simple = true;
240 if (is_single == false) {
241 BMO_ITER (e, &siter, op->slots_in, "edges", BM_EDGE) {
242 BMFace *f_pair[2];
243 if (BM_edge_face_pair(e, &f_pair[0], &f_pair[1])) {
244 for (uint i = 0; i < ARRAY_SIZE(f_pair); i += 1) {
245 if (BMO_face_flag_test(bm, f_pair[i], FACE_MARK)) {
246 is_simple = false;
247 break;
248 }
249 BMO_face_flag_enable(bm, f_pair[i], FACE_MARK);
250 }
251 if (is_simple == false) {
252 break;
253 }
254 }
255 }
256 }
257
258 if (is_simple) {
259 bm_rotate_edges_simple(bm, op, check_flag, use_ccw);
260 }
261 else {
262 bm_rotate_edges_shared(bm, op, check_flag, use_ccw, edges_len);
263 }
264
266}
#define BLI_assert(a)
Definition BLI_assert.h:50
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.c:192
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.c:172
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.c:269
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.c:291
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition BLI_heap.c:235
unsigned int uint
#define ARRAY_SIZE(arr)
Read Guarded memory(de)allocation.
#define BM_elem_index_get(ele)
#define BM_elem_index_set(ele, index)
#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.
BMEdge * BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag)
Rotate Edge.
@ BM_EDGEROT_CHECK_EXISTS
@ BM_EDGEROT_CHECK_DEGENERATE
#define BM_EDGE
#define BMO_edge_flag_enable(bm, e, oflag)
BMOpSlot * BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
BMESH OPSTACK GET SLOT.
#define BMO_SLOT_AS_BUFFER(slot)
void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
#define BMO_face_flag_enable(bm, e, oflag)
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
int BMO_slot_buffer_len(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
#define BMO_face_flag_test(bm, e, 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)
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
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_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
#define FLT_MAX
Definition stdcycles.h:14
_W64 unsigned int uintptr_t
Definition stdint.h:119
struct BMLoop * l
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * next
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
char elem_index_dirty