Blender V4.3
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
20#include "BLI_heap.h"
21#include "BLI_math_geom.h"
22#include "BLI_math_matrix.h"
23#include "BLI_math_vector.h"
25
26#include "MEM_guardedalloc.h"
27
28#include "bmesh.hh"
29#include "bmesh_beautify.hh" /* own include */
30
31// #define DEBUG_TIME
32
33#ifdef DEBUG_TIME
34# include "BLI_time.h"
35# include "BLI_time_utildefines.h"
36#endif
37
38/* -------------------------------------------------------------------- */
39/* GSet for edge rotation */
40
41struct EdRotState {
45 int v_pair[2];
52 int f_pair[2];
53};
54
55#if 0
56/* use BLI_ghashutil_inthash_v4 direct */
57static uint erot_gsetutil_hash(const void *ptr)
58{
59 const EdRotState *e_state = (const EdRotState *)ptr;
60 return BLI_ghashutil_inthash_v4(&e_state->v_pair[0]);
61}
62#endif
63#if 0
64static int erot_gsetutil_cmp(const void *a, const void *b)
65{
66 const EdRotState *e_state_a = (const EdRotState *)a;
67 const EdRotState *e_state_b = (const EdRotState *)b;
68 if (e_state_a->v_pair[0] < e_state_b->v_pair[0]) {
69 return -1;
70 }
71 if (e_state_a->v_pair[0] > e_state_b->v_pair[0]) {
72 return 1;
73 }
74 if (e_state_a->v_pair[1] < e_state_b->v_pair[1]) {
75 return -1;
76 }
77 if (e_state_a->v_pair[1] > e_state_b->v_pair[1]) {
78 return 1;
79 }
80 if (e_state_a->f_pair[0] < e_state_b->f_pair[0]) {
81 return -1;
82 }
83 if (e_state_a->f_pair[0] > e_state_b->f_pair[0]) {
84 return 1;
85 }
86 if (e_state_a->f_pair[1] < e_state_b->f_pair[1]) {
87 return -1;
88 }
89 if (e_state_a->f_pair[1] > e_state_b->f_pair[1]) {
90 return 1;
91 }
92 return 0;
93}
94#endif
99
100/* ensure v0 is smaller */
101#define EDGE_ORD(v0, v1) \
102 if (v0 > v1) { \
103 std::swap(v0, v1); \
104 } \
105 (void)0
106
107static void erot_state_ex(const BMEdge *e, int v_index[2], int f_index[2])
108{
110 BLI_assert(BM_vert_in_edge(e, e->l->prev->v) == false);
111 BLI_assert(BM_vert_in_edge(e, e->l->radial_next->prev->v) == false);
112
113 /* verts of the edge */
114 v_index[0] = BM_elem_index_get(e->v1);
115 v_index[1] = BM_elem_index_get(e->v2);
116 EDGE_ORD(v_index[0], v_index[1]);
117
118 /* verts of each of the 2 faces attached to this edge
119 * (that are not a part of this edge) */
120 f_index[0] = BM_elem_index_get(e->l->prev->v);
121 f_index[1] = BM_elem_index_get(e->l->radial_next->prev->v);
122 EDGE_ORD(f_index[0], f_index[1]);
123}
124
125static void erot_state_current(const BMEdge *e, EdRotState *e_state)
126{
127 erot_state_ex(e, e_state->v_pair, e_state->f_pair);
128}
129
130static void erot_state_alternate(const BMEdge *e, EdRotState *e_state)
131{
132 erot_state_ex(e, e_state->f_pair, e_state->v_pair);
133}
134
135/* -------------------------------------------------------------------- */
136/* Calculate the improvement of rotating the edge */
137
138static float bm_edge_calc_rotate_beauty__area(const float v1[3],
139 const float v2[3],
140 const float v3[3],
141 const float v4[3],
142 const bool lock_degenerate)
143{
144 /* not a loop (only to be able to break out) */
145 do {
146 float v1_xy[2], v2_xy[2], v3_xy[2], v4_xy[2];
147
148 /* first get the 2d values */
149 {
150 const float eps = 1e-5;
151 float no_a[3], no_b[3];
152 float no[3];
153 float axis_mat[3][3];
154 float no_scale;
155 cross_tri_v3(no_a, v2, v3, v4);
156 cross_tri_v3(no_b, v2, v4, v1);
157
158 // printf("%p %p %p %p - %p %p\n", v1, v2, v3, v4, e->l->f, e->l->radial_next->f);
159 BLI_assert((ELEM(v1, v2, v3, v4) == false) && (ELEM(v2, v1, v3, v4) == false) &&
160 (ELEM(v3, v1, v2, v4) == false) && (ELEM(v4, v1, v2, v3) == false));
161
162 add_v3_v3v3(no, no_a, no_b);
163 if (UNLIKELY((no_scale = normalize_v3(no)) == 0.0f)) {
164 break;
165 }
166
167 axis_dominant_v3_to_m3(axis_mat, no);
168 mul_v2_m3v3(v1_xy, axis_mat, v1);
169 mul_v2_m3v3(v2_xy, axis_mat, v2);
170 mul_v2_m3v3(v3_xy, axis_mat, v3);
171 mul_v2_m3v3(v4_xy, axis_mat, v4);
172
188 if (!(signum_i_ex(cross_tri_v2(v2_xy, v3_xy, v4_xy) / no_scale, eps) +
189 signum_i_ex(cross_tri_v2(v2_xy, v4_xy, v1_xy) / no_scale, eps)))
190 {
191 break;
192 }
193 }
194
202 v1_xy, v2_xy, v3_xy, v4_xy, lock_degenerate, nullptr);
203 } while (false);
204
205 return FLT_MAX;
206}
207
208static float bm_edge_calc_rotate_beauty__angle(const float v1[3],
209 const float v2[3],
210 const float v3[3],
211 const float v4[3])
212{
213 /* not a loop (only to be able to break out) */
214 do {
215 float no_a[3], no_b[3];
216 float angle_24, angle_13;
217
218 /* edge (2-4), current state */
219 normal_tri_v3(no_a, v2, v3, v4);
220 normal_tri_v3(no_b, v2, v4, v1);
221 angle_24 = angle_normalized_v3v3(no_a, no_b);
222
223 /* edge (1-3), new state */
224 /* only check new state for degenerate outcome */
225 if ((normal_tri_v3(no_a, v1, v2, v3) == 0.0f) || (normal_tri_v3(no_b, v1, v3, v4) == 0.0f)) {
226 break;
227 }
228 angle_13 = angle_normalized_v3v3(no_a, no_b);
229
230 return angle_13 - angle_24;
231 } while (false);
232
233 return FLT_MAX;
234}
235
237 const BMVert *v2,
238 const BMVert *v3,
239 const BMVert *v4,
240 const short flag,
241 const short method)
242{
243 /* not a loop (only to be able to break out) */
244 do {
245 if (flag & VERT_RESTRICT_TAG) {
246 const BMVert *v_a = v1, *v_b = v3;
248 break;
249 }
250 }
251
252 if (UNLIKELY(v1 == v3)) {
253 // printf("This should never happen, but does sometimes!\n");
254 break;
255 }
256
257 switch (method) {
258 case 0:
260 v1->co, v2->co, v3->co, v4->co, flag & EDGE_RESTRICT_DEGENERATE);
261 default:
262 return bm_edge_calc_rotate_beauty__angle(v1->co, v2->co, v3->co, v4->co);
263 }
264 } while (false);
265
266 return FLT_MAX;
267}
268
269static float bm_edge_calc_rotate_beauty(const BMEdge *e, const short flag, const short method)
270{
271 const BMVert *v1, *v2, *v3, *v4;
272 v1 = e->l->prev->v; /* First vert co */
273 v2 = e->l->v; /* `e->v1` or `e->v2`. */
274 v3 = e->l->radial_next->prev->v; /* Second vert co */
275 v4 = e->l->next->v; /* `e->v1` or `e->v2`. */
276
277 return BM_verts_calc_rotate_beauty(v1, v2, v3, v4, flag, method);
278}
279
280/* -------------------------------------------------------------------- */
281/* Update the edge cost of rotation in the heap */
282
283BLI_INLINE bool edge_in_array(const BMEdge *e, const BMEdge **edge_array, const int edge_array_len)
284{
285 const int index = BM_elem_index_get(e);
286 return ((index >= 0) && (index < edge_array_len) && (e == edge_array[index]));
287}
288
289/* recalc an edge in the heap (surrounding geometry has changed) */
291 Heap *eheap,
292 HeapNode **eheap_table,
293 GSet **edge_state_arr,
294 /* only for testing the edge is in the array */
295 const BMEdge **edge_array,
296 const int edge_array_len,
297
298 const short flag,
299 const short method)
300{
301 if (edge_in_array(e, edge_array, edge_array_len)) {
302 const int i = BM_elem_index_get(e);
303 GSet *e_state_set = edge_state_arr[i];
304
305 if (eheap_table[i]) {
306 BLI_heap_remove(eheap, eheap_table[i]);
307 eheap_table[i] = nullptr;
308 }
309
310 /* check if we can add it back */
312
313 /* check we're not moving back into a state we have been in before */
314 if (e_state_set != nullptr) {
315 EdRotState e_state_alt;
316 erot_state_alternate(e, &e_state_alt);
317 if (BLI_gset_haskey(e_state_set, (void *)&e_state_alt)) {
318 // printf(" skipping, we already have this state\n");
319 return;
320 }
321 }
322
323 {
324 /* recalculate edge */
325 const float cost = bm_edge_calc_rotate_beauty(e, flag, method);
326 if (cost < 0.0f) {
327 eheap_table[i] = BLI_heap_insert(eheap, cost, e);
328 }
329 else {
330 eheap_table[i] = nullptr;
331 }
332 }
333 }
334}
335
336/* we have rotated an edge, tag other edges and clear this one */
338 Heap *eheap,
339 HeapNode **eheap_table,
340 GSet **edge_state_arr,
341 const BMEdge **edge_array,
342 const int edge_array_len,
343 /* only for testing the edge is in the array */
344 const short flag,
345 const short method)
346{
347 int i;
348
349 BMEdge *e_arr[4] = {
350 e->l->next->e,
351 e->l->prev->e,
352 e->l->radial_next->next->e,
353 e->l->radial_next->prev->e,
354 };
355
356 BLI_assert(e->l->f->len == 3 && e->l->radial_next->f->len == 3);
357
359
360 for (i = 0; i < 4; i++) {
362 e_arr[i], eheap, eheap_table, edge_state_arr, edge_array, edge_array_len, flag, method);
363 }
364}
365
366/* -------------------------------------------------------------------- */
367/* Beautify Fill */
368
370 BMEdge **edge_array,
371 const int edge_array_len,
372 const short flag,
373 const short method,
374 const short oflag_edge,
375 const short oflag_face)
376{
377 Heap *eheap; /* edge heap */
378 HeapNode **eheap_table; /* edge index aligned table pointing to the eheap */
379
380 GSet **edge_state_arr = static_cast<GSet **>(
381 MEM_callocN(size_t(edge_array_len) * sizeof(GSet *), __func__));
382 BLI_mempool *edge_state_pool = BLI_mempool_create(sizeof(EdRotState), 0, 512, BLI_MEMPOOL_NOP);
383 int i;
384
385#ifdef DEBUG_TIME
386 TIMEIT_START(beautify_fill);
387#endif
388
389 eheap = BLI_heap_new_ex(uint(edge_array_len));
390 eheap_table = static_cast<HeapNode **>(
391 MEM_mallocN(sizeof(HeapNode *) * size_t(edge_array_len), __func__));
392
393 /* build heap */
394 for (i = 0; i < edge_array_len; i++) {
395 BMEdge *e = edge_array[i];
396 const float cost = bm_edge_calc_rotate_beauty(e, flag, method);
397 if (cost < 0.0f) {
398 eheap_table[i] = BLI_heap_insert(eheap, cost, e);
399 }
400 else {
401 eheap_table[i] = nullptr;
402 }
403
404 BM_elem_index_set(e, i); /* set_dirty */
405 }
407
408 while (BLI_heap_is_empty(eheap) == false) {
409 BMEdge *e = static_cast<BMEdge *>(BLI_heap_pop_min(eheap));
410 i = BM_elem_index_get(e);
411 eheap_table[i] = nullptr;
412
414
416
417 BLI_assert(e == nullptr || BM_edge_face_count_is_equal(e, 2));
418
419 if (LIKELY(e)) {
420 GSet *e_state_set = edge_state_arr[i];
421
422 /* add the new state into the set so we don't move into this state again
423 * NOTE: we could add the previous state too but this isn't essential)
424 * for avoiding eternal loops */
425 EdRotState *e_state = static_cast<EdRotState *>(BLI_mempool_alloc(edge_state_pool));
426 erot_state_current(e, e_state);
427 if (UNLIKELY(e_state_set == nullptr)) {
428 edge_state_arr[i] = e_state_set = erot_gset_new(); /* store previous state */
429 }
430 BLI_assert(BLI_gset_haskey(e_state_set, (void *)e_state) == false);
431 BLI_gset_insert(e_state_set, e_state);
432
433 // printf(" %d -> %d, %d\n", i, BM_elem_index_get(e->v1), BM_elem_index_get(e->v2));
434
435 /* maintain the index array */
436 edge_array[i] = e;
438
439 /* recalculate faces connected on the heap */
441 eheap,
442 eheap_table,
443 edge_state_arr,
444 (const BMEdge **)edge_array,
445 edge_array_len,
446 flag,
447 method);
448
449 /* update flags */
450 if (oflag_edge) {
451 BMO_edge_flag_enable(bm, e, oflag_edge);
452 }
453
454 if (oflag_face) {
455 BMO_face_flag_enable(bm, e->l->f, oflag_face);
456 BMO_face_flag_enable(bm, e->l->radial_next->f, oflag_face);
457 }
458 }
459 }
460
461 BLI_heap_free(eheap, nullptr);
462 MEM_freeN(eheap_table);
463
464 for (i = 0; i < edge_array_len; i++) {
465 if (edge_state_arr[i]) {
466 BLI_gset_free(edge_state_arr[i], nullptr);
467 }
468 }
469
470 MEM_freeN(edge_state_arr);
471 BLI_mempool_destroy(edge_state_pool);
472
473#ifdef DEBUG_TIME
474 TIMEIT_END(beautify_fill);
475#endif
476}
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
struct GSet GSet
Definition BLI_ghash.h:341
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:1004
#define BLI_ghashutil_inthash_v4_cmp
Definition BLI_ghash.h:602
#define BLI_ghashutil_inthash_v4(key)
Definition BLI_ghash.h:591
void BLI_gset_insert(GSet *gs, void *key)
Definition BLI_ghash.c:959
GSet * BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:944
#define BLI_ghashutil_inthash_v4_p
Definition BLI_ghash.h:593
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
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
void void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1
MINLINE int signum_i_ex(float a, float eps)
MINLINE float cross_tri_v2(const float v1[2], const float v2[2], const float v3[2])
void cross_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:24
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:39
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float n[3])
void * BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1)
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
@ BLI_MEMPOOL_NOP
Definition BLI_mempool.h:86
void BLI_mempool_destroy(BLI_mempool *pool) ATTR_NONNULL(1)
float BLI_polyfill_beautify_quad_rotate_calc_ex(const float v1[2], const float v2[2], const float v3[2], const float v4[2], bool lock_degenerate, float *r_area)
unsigned int uint
Platform independent time functions.
Utility defines for timing/benchmarks.
#define TIMEIT_START(var)
#define TIMEIT_END(var)
#define UNLIKELY(x)
#define ELEM(...)
#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__area(const float v1[3], const float v2[3], const float v3[3], const float v4[3], const bool lock_degenerate)
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)
ATTR_WARN_UNUSED_RESULT 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
local_group_size(16, 16) .push_constant(Type b
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
const btScalar eps
Definition poly34.cpp:11
#define FLT_MAX
Definition stdcycles.h:14
float co[3]
struct BMEdge * e
char elem_index_dirty
PointerRNA * ptr
Definition wm_files.cc:4126
uint8_t flag
Definition wm_window.cc:138