Blender V5.0
bmesh_beautify.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
19
20#include "BLI_heap.h"
21#include "BLI_math_geom.h"
22#include "BLI_math_vector.h"
24
25#include "MEM_guardedalloc.h"
26
27#include "bmesh.hh"
28#include "bmesh_beautify.hh" /* own include */
29
30// #define DEBUG_TIME
31
32#ifdef DEBUG_TIME
33# include "BLI_time.h"
34# include "BLI_time_utildefines.h"
35#endif
36
37/* -------------------------------------------------------------------- */
38/* GSet for edge rotation */
39
40struct EdRotState {
44 int v_pair[2];
51 int f_pair[2];
52};
53
54#if 0
55/* use BLI_ghashutil_inthash_v4 direct */
56static uint erot_gsetutil_hash(const void *ptr)
57{
58 const EdRotState *e_state = (const EdRotState *)ptr;
59 return BLI_ghashutil_inthash_v4(&e_state->v_pair[0]);
60}
61#endif
62#if 0
63static int erot_gsetutil_cmp(const void *a, const void *b)
64{
65 const EdRotState *e_state_a = (const EdRotState *)a;
66 const EdRotState *e_state_b = (const EdRotState *)b;
67 if (e_state_a->v_pair[0] < e_state_b->v_pair[0]) {
68 return -1;
69 }
70 if (e_state_a->v_pair[0] > e_state_b->v_pair[0]) {
71 return 1;
72 }
73 if (e_state_a->v_pair[1] < e_state_b->v_pair[1]) {
74 return -1;
75 }
76 if (e_state_a->v_pair[1] > e_state_b->v_pair[1]) {
77 return 1;
78 }
79 if (e_state_a->f_pair[0] < e_state_b->f_pair[0]) {
80 return -1;
81 }
82 if (e_state_a->f_pair[0] > e_state_b->f_pair[0]) {
83 return 1;
84 }
85 if (e_state_a->f_pair[1] < e_state_b->f_pair[1]) {
86 return -1;
87 }
88 if (e_state_a->f_pair[1] > e_state_b->f_pair[1]) {
89 return 1;
90 }
91 return 0;
92}
93#endif
98
99/* ensure v0 is smaller */
100#define EDGE_ORD(v0, v1) \
101 if (v0 > v1) { \
102 std::swap(v0, v1); \
103 } \
104 (void)0
105
106static void erot_state_ex(const BMEdge *e, int v_index[2], int f_index[2])
107{
109 BLI_assert(BM_vert_in_edge(e, e->l->prev->v) == false);
110 BLI_assert(BM_vert_in_edge(e, e->l->radial_next->prev->v) == false);
111
112 /* verts of the edge */
113 v_index[0] = BM_elem_index_get(e->v1);
114 v_index[1] = BM_elem_index_get(e->v2);
115 EDGE_ORD(v_index[0], v_index[1]);
116
117 /* verts of each of the 2 faces attached to this edge
118 * (that are not a part of this edge) */
119 f_index[0] = BM_elem_index_get(e->l->prev->v);
120 f_index[1] = BM_elem_index_get(e->l->radial_next->prev->v);
121 EDGE_ORD(f_index[0], f_index[1]);
122}
123
124static void erot_state_current(const BMEdge *e, EdRotState *e_state)
125{
126 erot_state_ex(e, e_state->v_pair, e_state->f_pair);
127}
128
129static void erot_state_alternate(const BMEdge *e, EdRotState *e_state)
130{
131 erot_state_ex(e, e_state->f_pair, e_state->v_pair);
132}
133
134/* -------------------------------------------------------------------- */
135/* Calculate the improvement of rotating the edge */
136
137static float bm_edge_calc_rotate_beauty__angle(const float v1[3],
138 const float v2[3],
139 const float v3[3],
140 const float v4[3])
141{
142 /* not a loop (only to be able to break out) */
143 do {
144 float no_a[3], no_b[3];
145 float angle_24, angle_13;
146
147 /* edge (2-4), current state */
148 normal_tri_v3(no_a, v2, v3, v4);
149 normal_tri_v3(no_b, v2, v4, v1);
150 angle_24 = angle_normalized_v3v3(no_a, no_b);
151
152 /* edge (1-3), new state */
153 /* only check new state for degenerate outcome */
154 if ((normal_tri_v3(no_a, v1, v2, v3) == 0.0f) || (normal_tri_v3(no_b, v1, v3, v4) == 0.0f)) {
155 break;
156 }
157 angle_13 = angle_normalized_v3v3(no_a, no_b);
158
159 return angle_13 - angle_24;
160 } while (false);
161
162 return FLT_MAX;
163}
164
166 const BMVert *v2,
167 const BMVert *v3,
168 const BMVert *v4,
169 const short flag,
170 const short method)
171{
172 /* not a loop (only to be able to break out) */
173 do {
174 if (flag & VERT_RESTRICT_TAG) {
175 const BMVert *v_a = v1, *v_b = v3;
177 break;
178 }
179 }
180
181 if (UNLIKELY(v1 == v3)) {
182 // printf("This should never happen, but does sometimes!\n");
183 break;
184 }
185
186 switch (method) {
187 case 0:
189 v1->co, v2->co, v3->co, v4->co, flag & EDGE_RESTRICT_DEGENERATE);
190 default:
191 return bm_edge_calc_rotate_beauty__angle(v1->co, v2->co, v3->co, v4->co);
192 }
193 } while (false);
194
195 return FLT_MAX;
196}
197
198static float bm_edge_calc_rotate_beauty(const BMEdge *e, const short flag, const short method)
199{
200 const BMVert *v1, *v2, *v3, *v4;
201 v1 = e->l->prev->v; /* First vert co */
202 v2 = e->l->v; /* `e->v1` or `e->v2`. */
203 v3 = e->l->radial_next->prev->v; /* Second vert co */
204 v4 = e->l->next->v; /* `e->v1` or `e->v2`. */
205
206 return BM_verts_calc_rotate_beauty(v1, v2, v3, v4, flag, method);
207}
208
209/* -------------------------------------------------------------------- */
210/* Update the edge cost of rotation in the heap */
211
212BLI_INLINE bool edge_in_array(const BMEdge *e, const BMEdge **edge_array, const int edge_array_len)
213{
214 const int index = BM_elem_index_get(e);
215 return ((index >= 0) && (index < edge_array_len) && (e == edge_array[index]));
216}
217
218/* recalc an edge in the heap (surrounding geometry has changed) */
220 Heap *eheap,
221 HeapNode **eheap_table,
222 GSet **edge_state_arr,
223 /* only for testing the edge is in the array */
224 const BMEdge **edge_array,
225 const int edge_array_len,
226
227 const short flag,
228 const short method)
229{
230 if (edge_in_array(e, edge_array, edge_array_len)) {
231 const int i = BM_elem_index_get(e);
232 GSet *e_state_set = edge_state_arr[i];
233
234 if (eheap_table[i]) {
235 BLI_heap_remove(eheap, eheap_table[i]);
236 eheap_table[i] = nullptr;
237 }
238
239 /* check if we can add it back */
241
242 /* check we're not moving back into a state we have been in before */
243 if (e_state_set != nullptr) {
244 EdRotState e_state_alt;
245 erot_state_alternate(e, &e_state_alt);
246 if (BLI_gset_haskey(e_state_set, (void *)&e_state_alt)) {
247 // printf(" skipping, we already have this state\n");
248 return;
249 }
250 }
251
252 {
253 /* recalculate edge */
254 const float cost = bm_edge_calc_rotate_beauty(e, flag, method);
255 if (cost < 0.0f) {
256 eheap_table[i] = BLI_heap_insert(eheap, cost, e);
257 }
258 else {
259 eheap_table[i] = nullptr;
260 }
261 }
262 }
263}
264
265/* we have rotated an edge, tag other edges and clear this one */
267 Heap *eheap,
268 HeapNode **eheap_table,
269 GSet **edge_state_arr,
270 const BMEdge **edge_array,
271 const int edge_array_len,
272 /* only for testing the edge is in the array */
273 const short flag,
274 const short method)
275{
276 int i;
277
278 BMEdge *e_arr[4] = {
279 e->l->next->e,
280 e->l->prev->e,
281 e->l->radial_next->next->e,
282 e->l->radial_next->prev->e,
283 };
284
285 BLI_assert(e->l->f->len == 3 && e->l->radial_next->f->len == 3);
286
288
289 for (i = 0; i < 4; i++) {
291 e_arr[i], eheap, eheap_table, edge_state_arr, edge_array, edge_array_len, flag, method);
292 }
293}
294
295/* -------------------------------------------------------------------- */
296/* Beautify Fill */
297
299 BMEdge **edge_array,
300 const int edge_array_len,
301 const short flag,
302 const short method,
303 const short oflag_edge,
304 const short oflag_face)
305{
306 Heap *eheap; /* edge heap */
307 HeapNode **eheap_table; /* edge index aligned table pointing to the eheap */
308
309 GSet **edge_state_arr = MEM_calloc_arrayN<GSet *>(edge_array_len, __func__);
310 BLI_mempool *edge_state_pool = BLI_mempool_create(sizeof(EdRotState), 0, 512, BLI_MEMPOOL_NOP);
311 int i;
312
313#ifdef DEBUG_TIME
314 TIMEIT_START(beautify_fill);
315#endif
316
317 eheap = BLI_heap_new_ex(uint(edge_array_len));
318 eheap_table = MEM_malloc_arrayN<HeapNode *>(size_t(edge_array_len), __func__);
319
320 /* build heap */
321 for (i = 0; i < edge_array_len; i++) {
322 BMEdge *e = edge_array[i];
323 const float cost = bm_edge_calc_rotate_beauty(e, flag, method);
324 if (cost < 0.0f) {
325 eheap_table[i] = BLI_heap_insert(eheap, cost, e);
326 }
327 else {
328 eheap_table[i] = nullptr;
329 }
330
331 BM_elem_index_set(e, i); /* set_dirty */
332 }
333 bm->elem_index_dirty |= BM_EDGE;
334
335 while (BLI_heap_is_empty(eheap) == false) {
336 BMEdge *e = static_cast<BMEdge *>(BLI_heap_pop_min(eheap));
338 eheap_table[i] = nullptr;
339
341
343
344 BLI_assert(e == nullptr || BM_edge_face_count_is_equal(e, 2));
345
346 if (LIKELY(e)) {
347 GSet *e_state_set = edge_state_arr[i];
348
349 /* add the new state into the set so we don't move into this state again
350 * NOTE: we could add the previous state too but this isn't essential)
351 * for avoiding eternal loops */
352 EdRotState *e_state = static_cast<EdRotState *>(BLI_mempool_alloc(edge_state_pool));
353 erot_state_current(e, e_state);
354 if (UNLIKELY(e_state_set == nullptr)) {
355 edge_state_arr[i] = e_state_set = erot_gset_new(); /* store previous state */
356 }
357 BLI_assert(BLI_gset_haskey(e_state_set, (void *)e_state) == false);
358 BLI_gset_insert(e_state_set, e_state);
359
360 // printf(" %d -> %d, %d\n", i, BM_elem_index_get(e->v1), BM_elem_index_get(e->v2));
361
362 /* maintain the index array */
363 edge_array[i] = e;
365
366 /* recalculate faces connected on the heap */
368 eheap,
369 eheap_table,
370 edge_state_arr,
371 (const BMEdge **)edge_array,
372 edge_array_len,
373 flag,
374 method);
375
376 /* update flags */
377 if (oflag_edge) {
378 BMO_edge_flag_enable(bm, e, oflag_edge);
379 }
380
381 if (oflag_face) {
382 BMO_face_flag_enable(bm, e->l->f, oflag_face);
383 BMO_face_flag_enable(bm, e->l->radial_next->f, oflag_face);
384 }
385 }
386 }
387
388 BLI_heap_free(eheap, nullptr);
389 MEM_freeN(eheap_table);
390
391 for (i = 0; i < edge_array_len; i++) {
392 if (edge_state_arr[i]) {
393 BLI_gset_free(edge_state_arr[i], nullptr);
394 }
395 }
396
397 MEM_freeN(edge_state_arr);
398 BLI_mempool_destroy(edge_state_pool);
399
400#ifdef DEBUG_TIME
401 TIMEIT_END(beautify_fill);
402#endif
403}
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
struct GSet GSet
Definition BLI_ghash.h:337
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
#define BLI_ghashutil_inthash_v4_cmp
Definition BLI_ghash.h:598
#define BLI_ghashutil_inthash_v4(key)
Definition BLI_ghash.h:587
void BLI_gset_insert(GSet *gs, void *key)
Definition BLI_ghash.cc:959
GSet * BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:944
#define BLI_ghashutil_inthash_v4_p
Definition BLI_ghash.h:589
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
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
void void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
void * BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1)
@ BLI_MEMPOOL_NOP
BLI_mempool * BLI_mempool_create(unsigned int esize, unsigned int elem_num, unsigned int pchunk, unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
void BLI_mempool_destroy(BLI_mempool *pool) ATTR_NONNULL(1)
float BLI_polyfill_edge_calc_rotate_beauty__area(const float v1[3], const float v2[3], const float v3[3], const float v4[3], bool lock_degenerate)
unsigned int uint
Platform independent time functions.
Utility defines for timing/benchmarks.
#define TIMEIT_START(var)
#define TIMEIT_END(var)
#define UNLIKELY(x)
#define LIKELY(x)
Read Guarded memory(de)allocation.
static void erot_state_alternate(const BMEdge *e, EdRotState *e_state)
BLI_INLINE bool edge_in_array(const BMEdge *e, const BMEdge **edge_array, const int edge_array_len)
void BM_mesh_beautify_fill(BMesh *bm, BMEdge **edge_array, const int edge_array_len, const short flag, const short method, const short oflag_edge, const short oflag_face)
float BM_verts_calc_rotate_beauty(const BMVert *v1, const BMVert *v2, const BMVert *v3, const BMVert *v4, const short flag, const short method)
static void erot_state_current(const BMEdge *e, EdRotState *e_state)
#define EDGE_ORD(v0, v1)
static void bm_edge_update_beauty_cost_single(BMEdge *e, Heap *eheap, HeapNode **eheap_table, GSet **edge_state_arr, const BMEdge **edge_array, const int edge_array_len, const short flag, const short method)
static float bm_edge_calc_rotate_beauty__angle(const float v1[3], const float v2[3], const float v3[3], const float v4[3])
static void erot_state_ex(const BMEdge *e, int v_index[2], int f_index[2])
static GSet * erot_gset_new()
static void bm_edge_update_beauty_cost(BMEdge *e, Heap *eheap, HeapNode **eheap_table, GSet **edge_state_arr, const BMEdge **edge_array, const int edge_array_len, const short flag, const short method)
static float bm_edge_calc_rotate_beauty(const BMEdge *e, const short flag, const short method)
@ EDGE_RESTRICT_DEGENERATE
@ VERT_RESTRICT_TAG
@ BM_ELEM_TAG
#define BM_elem_index_get(ele)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
BMesh * bm
BMEdge * BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag)
Rotate Edge.
@ BM_EDGEROT_CHECK_EXISTS
#define BM_EDGE
#define BMO_edge_flag_enable(bm, e, oflag)
#define BMO_face_flag_enable(bm, e, oflag)
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define BM_edge_face_count_is_equal(e, n)
BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define FLT_MAX
Definition stdcycles.h:14
float co[3]
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238
uint8_t flag
Definition wm_window.cc:145