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