Blender V5.0
polyfill_2d_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
25
26#include "BLI_utildefines.h"
27
28#include "BLI_heap.h"
29#include "BLI_math_geom.h"
30#include "BLI_math_matrix.h"
31#include "BLI_memarena.h"
32
33#include "BLI_polyfill_2d_beautify.h" /* own include */
34
35#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
36
37/* Used to find matching edges. */
42
43/* Half edge used for rotating in-place. */
50
51static int oedge_cmp(const void *a1, const void *a2)
52{
53 const OrderEdge *x1 = static_cast<const OrderEdge *>(a1);
54 const OrderEdge *x2 = static_cast<const OrderEdge *>(a2);
55 if (x1->verts[0] > x2->verts[0]) {
56 return 1;
57 }
58 if (x1->verts[0] < x2->verts[0]) {
59 return -1;
60 }
61
62 if (x1->verts[1] > x2->verts[1]) {
63 return 1;
64 }
65 if (x1->verts[1] < x2->verts[1]) {
66 return -1;
67 }
68
69 /* Only for predictability. */
70 if (x1->e_half > x2->e_half) {
71 return 1;
72 }
73 if (x1->e_half < x2->e_half) {
74 return -1;
75 }
76 /* Should never get here, no two edges should be the same. */
77 BLI_assert(false);
78 return 0;
79}
80
81BLI_INLINE bool is_boundary_edge(uint i_a, uint i_b, const uint coord_last)
82{
83 BLI_assert(i_a < i_b);
84 return ((i_a + 1 == i_b) || UNLIKELY((i_a == 0) && (i_b == coord_last)));
85}
87 const float v2[2],
88 const float v3[2],
89 const float v4[2],
90 const bool lock_degenerate,
91 float *r_area)
92{
93 /* not a loop (only to be able to break out) */
94 do {
95 /* Allow very small faces to be considered non-zero. */
96 const float eps_zero_area = 1e-12f;
97 const float area_2x_234 = cross_tri_v2(v2, v3, v4);
98 const float area_2x_241 = cross_tri_v2(v2, v4, v1);
99
100 const float area_2x_123 = cross_tri_v2(v1, v2, v3);
101 const float area_2x_134 = cross_tri_v2(v1, v3, v4);
102
103 BLI_assert((ELEM(v1, v2, v3, v4) == false) && (ELEM(v2, v1, v3, v4) == false) &&
104 (ELEM(v3, v1, v2, v4) == false) && (ELEM(v4, v1, v2, v3) == false));
105
106 if (r_area) {
107 *r_area = (fabsf(area_2x_234) + fabsf(area_2x_241) +
108 /* Include both pairs for predictable results. */
109 fabsf(area_2x_123) + fabsf(area_2x_134)) /
110 8.0f;
111 }
112
113 /*
114 * Test for unusable (1-3) state.
115 * - Area sign flipping to check faces aren't going to point in opposite directions.
116 * - Area epsilon check that the one of the faces won't be zero area.
117 */
118 if ((area_2x_123 >= 0.0f) != (area_2x_134 >= 0.0f)) {
119 break;
120 }
121 if ((fabsf(area_2x_123) <= eps_zero_area) || (fabsf(area_2x_134) <= eps_zero_area)) {
122 break;
123 }
124
125 /* Test for unusable (2-4) state (same as above). */
126 if ((area_2x_234 >= 0.0f) != (area_2x_241 >= 0.0f)) {
127 if (lock_degenerate) {
128 break;
129 }
130
131 return -FLT_MAX; /* always rotate */
132 }
133 if ((fabsf(area_2x_234) <= eps_zero_area) || (fabsf(area_2x_241) <= eps_zero_area)) {
134 return -FLT_MAX; /* always rotate */
135 }
136
137 {
138 /* testing rule: the area divided by the perimeter,
139 * check if (1-3) beats the existing (2-4) edge rotation */
140 float area_a, area_b;
141 float prim_a, prim_b;
142 float fac_24, fac_13;
143
144 float len_12, len_23, len_34, len_41, len_24, len_13;
145
146 /* edges around the quad */
147 len_12 = len_v2v2(v1, v2);
148 len_23 = len_v2v2(v2, v3);
149 len_34 = len_v2v2(v3, v4);
150 len_41 = len_v2v2(v4, v1);
151 /* edges crossing the quad interior */
152 len_13 = len_v2v2(v1, v3);
153 len_24 = len_v2v2(v2, v4);
154
155 /* NOTE: area is in fact (area * 2),
156 * but in this case its OK, since we're comparing ratios */
157
158 /* edge (2-4), current state */
159 area_a = fabsf(area_2x_234);
160 area_b = fabsf(area_2x_241);
161 prim_a = len_23 + len_34 + len_24;
162 prim_b = len_41 + len_12 + len_24;
163 fac_24 = (area_a / prim_a) + (area_b / prim_b);
164
165 /* edge (1-3), new state */
166 area_a = fabsf(area_2x_123);
167 area_b = fabsf(area_2x_134);
168 prim_a = len_12 + len_23 + len_13;
169 prim_b = len_34 + len_41 + len_13;
170 fac_13 = (area_a / prim_a) + (area_b / prim_b);
171
172 /* negative number if (1-3) is an improved state */
173 return fac_24 - fac_13;
174 }
175 } while (false);
176
177 return FLT_MAX;
178}
179
181 const float v2[3],
182 const float v3[3],
183 const float v4[3],
184 const bool lock_degenerate)
185{
186 /* not a loop (only to be able to break out) */
187 do {
188 float v1_xy[2], v2_xy[2], v3_xy[2], v4_xy[2];
189
190 /* first get the 2d values */
191 {
192 const float eps = 1e-5f;
193 float no_a[3], no_b[3];
194 float no[3];
195 float axis_mat[3][3];
196 float no_scale;
197 cross_tri_v3(no_a, v2, v3, v4);
198 cross_tri_v3(no_b, v2, v4, v1);
199
200 // printf("%p %p %p %p - %p %p\n", v1, v2, v3, v4, e->l->f, e->l->radial_next->f);
201 BLI_assert((ELEM(v1, v2, v3, v4) == false) && (ELEM(v2, v1, v3, v4) == false) &&
202 (ELEM(v3, v1, v2, v4) == false) && (ELEM(v4, v1, v2, v3) == false));
203
204 add_v3_v3v3(no, no_a, no_b);
205 if (UNLIKELY((no_scale = normalize_v3(no)) == 0.0f)) {
206 break;
207 }
208
209 axis_dominant_v3_to_m3(axis_mat, no);
210 mul_v2_m3v3(v1_xy, axis_mat, v1);
211 mul_v2_m3v3(v2_xy, axis_mat, v2);
212 mul_v2_m3v3(v3_xy, axis_mat, v3);
213 mul_v2_m3v3(v4_xy, axis_mat, v4);
214
230 if (!(signum_i_ex(cross_tri_v2(v2_xy, v3_xy, v4_xy) / no_scale, eps) +
231 signum_i_ex(cross_tri_v2(v2_xy, v4_xy, v1_xy) / no_scale, eps)))
232 {
233 break;
234 }
235 }
236
244 v1_xy, v2_xy, v3_xy, v4_xy, lock_degenerate, nullptr);
245 } while (false);
246
247 return FLT_MAX;
248}
249
250static float polyedge_rotate_beauty_calc(const float (*coords)[2],
251 const HalfEdge *edges,
252 const HalfEdge *e_a,
253 float *r_area)
254{
255 const HalfEdge *e_b = &edges[e_a->e_radial];
256
257 const HalfEdge *e_a_other = &edges[edges[e_a->e_next].e_next];
258 const HalfEdge *e_b_other = &edges[edges[e_b->e_next].e_next];
259
260 const float *v1, *v2, *v3, *v4;
261
262 v1 = coords[e_a_other->v];
263 v2 = coords[e_a->v];
264 v3 = coords[e_b_other->v];
265 v4 = coords[e_b->v];
266
267 return BLI_polyfill_beautify_quad_rotate_calc_ex(v1, v2, v3, v4, false, r_area);
268}
269
270static void polyedge_beauty_cost_update_single(const float (*coords)[2],
271 const HalfEdge *edges,
272 HalfEdge *e,
273 Heap *eheap,
274 HeapNode **eheap_table)
275{
276 const uint i = e->base_index;
277 /* recalculate edge */
278 float area;
279 const float cost = polyedge_rotate_beauty_calc(coords, edges, e, &area);
280 /* We can get cases where both choices generate very small negative costs,
281 * which leads to infinite loop. Anyway, costs above that are not worth recomputing,
282 * maybe we could even optimize it to a smaller limit?
283 * Actually, FLT_EPSILON is too small in some cases, 1e-6f seems to work OK hopefully?
284 * See #43578, #49478.
285 *
286 * In fact a larger epsilon can still fail when the area of the face is very large,
287 * now the epsilon is scaled by the face area.
288 * See #56532. */
289 if (cost < -1e-6f * max_ff(area, 1.0f)) {
290 BLI_heap_insert_or_update(eheap, &eheap_table[i], cost, e);
291 }
292 else {
293 if (eheap_table[i]) {
294 BLI_heap_remove(eheap, eheap_table[i]);
295 eheap_table[i] = nullptr;
296 }
297 }
298}
299
301 const float (*coords)[2], HalfEdge *edges, HalfEdge *e, Heap *eheap, HeapNode **eheap_table)
302{
303 HalfEdge *e_arr[4];
304 e_arr[0] = &edges[e->e_next];
305 e_arr[1] = &edges[e_arr[0]->e_next];
306
307 e = &edges[e->e_radial];
308 e_arr[2] = &edges[e->e_next];
309 e_arr[3] = &edges[e_arr[2]->e_next];
310
311 for (uint i = 0; i < 4; i++) {
312 if (e_arr[i] && e_arr[i]->base_index != UINT_MAX) {
313 polyedge_beauty_cost_update_single(coords, edges, e_arr[i], eheap, eheap_table);
314 }
315 }
316}
317
318static void polyedge_rotate(HalfEdge *edges, const HalfEdge *e)
319{
335 HalfEdge *ed[6];
336 uint ed_index[6];
337
338 ed_index[0] = uint(e - edges);
339 ed[0] = &edges[ed_index[0]];
340 ed_index[1] = ed[0]->e_next;
341 ed[1] = &edges[ed_index[1]];
342 ed_index[2] = ed[1]->e_next;
343 ed[2] = &edges[ed_index[2]];
344
345 ed_index[3] = e->e_radial;
346 ed[3] = &edges[ed_index[3]];
347 ed_index[4] = ed[3]->e_next;
348 ed[4] = &edges[ed_index[4]];
349 ed_index[5] = ed[4]->e_next;
350 ed[5] = &edges[ed_index[5]];
351
352 ed[0]->e_next = ed_index[2];
353 ed[1]->e_next = ed_index[3];
354 ed[2]->e_next = ed_index[4];
355 ed[3]->e_next = ed_index[5];
356 ed[4]->e_next = ed_index[0];
357 ed[5]->e_next = ed_index[1];
358
359 ed[0]->v = ed[5]->v;
360 ed[3]->v = ed[2]->v;
361}
362
363void BLI_polyfill_beautify(const float (*coords)[2],
364 const uint coords_num,
365 uint (*tris)[3],
366
367 /* structs for reuse */
368 MemArena *arena,
369 Heap *eheap)
370{
371 const uint coord_last = coords_num - 1;
372 const uint tris_len = coords_num - 2;
373 /* internal edges only (between 2 tris) */
374 const uint edges_len = tris_len - 1;
375
376 HeapNode **eheap_table;
377
378 const uint half_edges_len = 3 * tris_len;
379 HalfEdge *half_edges = static_cast<HalfEdge *>(
380 BLI_memarena_alloc(arena, sizeof(*half_edges) * half_edges_len));
381 OrderEdge *order_edges = static_cast<OrderEdge *>(
382 BLI_memarena_alloc(arena, sizeof(OrderEdge) * 2 * edges_len));
383 uint order_edges_len = 0;
384
385 /* first build edges */
386 for (uint i = 0; i < tris_len; i++) {
387 for (uint j_curr = 0, j_prev = 2; j_curr < 3; j_prev = j_curr++) {
388 const uint e_index_prev = (i * 3) + j_prev;
389 const uint e_index_curr = (i * 3) + j_curr;
390
391 half_edges[e_index_prev].v = tris[i][j_prev];
392 half_edges[e_index_prev].e_next = e_index_curr;
393 half_edges[e_index_prev].e_radial = UINT_MAX;
394 half_edges[e_index_prev].base_index = UINT_MAX;
395
396 uint e_pair[2] = {tris[i][j_prev], tris[i][j_curr]};
397 if (e_pair[0] > e_pair[1]) {
398 SWAP(uint, e_pair[0], e_pair[1]);
399 }
400
401 /* ensure internal edges. */
402 if (!is_boundary_edge(e_pair[0], e_pair[1], coord_last)) {
403 order_edges[order_edges_len].verts[0] = e_pair[0];
404 order_edges[order_edges_len].verts[1] = e_pair[1];
405 order_edges[order_edges_len].e_half = e_index_prev;
406 order_edges_len += 1;
407 }
408 }
409 }
410 BLI_assert(edges_len * 2 == order_edges_len);
411
412 qsort(order_edges, order_edges_len, sizeof(OrderEdge), oedge_cmp);
413
414 for (uint i = 0, base_index = 0; i < order_edges_len; base_index++) {
415 const OrderEdge *oe_a = &order_edges[i++];
416 const OrderEdge *oe_b = &order_edges[i++];
417 BLI_assert(oe_a->verts[0] == oe_b->verts[0] && oe_a->verts[1] == oe_b->verts[1]);
418 half_edges[oe_a->e_half].e_radial = oe_b->e_half;
419 half_edges[oe_b->e_half].e_radial = oe_a->e_half;
420 half_edges[oe_a->e_half].base_index = base_index;
421 half_edges[oe_b->e_half].base_index = base_index;
422 }
423 /* order_edges could be freed now. */
424
425 /* Now perform iterative rotations. */
426#if 0
427 eheap_table = BLI_memarena_alloc(arena, sizeof(HeapNode *) * size_t(edges_len));
428#else
429 /* We can re-use this since its big enough. */
430 eheap_table = (HeapNode **)order_edges;
431 order_edges = nullptr;
432#endif
433
434 /* Build heap. */
435 {
436 HalfEdge *e = half_edges;
437 for (uint i = 0; i < half_edges_len; i++, e++) {
438 /* Accounts for boundary edged too (UINT_MAX). */
439 if (e->e_radial < i) {
440 const float cost = polyedge_rotate_beauty_calc(coords, half_edges, e, nullptr);
441 if (cost < 0.0f) {
442 eheap_table[e->base_index] = BLI_heap_insert(eheap, cost, e);
443 }
444 else {
445 eheap_table[e->base_index] = nullptr;
446 }
447 }
448 }
449 }
450
451 while (BLI_heap_is_empty(eheap) == false) {
452 HalfEdge *e = static_cast<HalfEdge *>(BLI_heap_pop_min(eheap));
453 eheap_table[e->base_index] = nullptr;
454
455 polyedge_rotate(half_edges, e);
456
457 /* recalculate faces connected on the heap */
458 polyedge_beauty_cost_update(coords, half_edges, e, eheap, eheap_table);
459 }
460
461 BLI_heap_clear(eheap, nullptr);
462
463 // MEM_freeN(eheap_table); /* arena */
464
465 /* Get triangles from half edge. */
466 uint tri_index = 0;
467 for (uint i = 0; i < half_edges_len; i++) {
468 HalfEdge *e = &half_edges[i];
469 if (e->v != UINT_MAX) {
470 uint *tri = tris[tri_index++];
471
472 tri[0] = e->v;
473 e->v = UINT_MAX;
474
475 e = &half_edges[e->e_next];
476 tri[1] = e->v;
477 e->v = UINT_MAX;
478
479 e = &half_edges[e->e_next];
480 tri[2] = e->v;
481 e->v = UINT_MAX;
482 }
483 }
484}
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
A min-heap / priority queue ADT.
void BLI_heap_insert_or_update(Heap *heap, HeapNode **node_p, float value, void *ptr) ATTR_NONNULL(1
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:269
void BLI_heap_clear(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:213
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
MINLINE float max_ff(float a, float b)
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:26
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3(float n[3])
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
unsigned int uint
#define SWAP(type, a, b)
#define UNLIKELY(x)
#define ELEM(...)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
#define UINT_MAX
Definition hash_md5.cc:44
const btScalar eps
Definition poly34.cpp:11
float BLI_polyfill_beautify_quad_rotate_calc_ex(const float v1[2], const float v2[2], const float v3[2], const float v4[2], const bool lock_degenerate, float *r_area)
static float polyedge_rotate_beauty_calc(const float(*coords)[2], const HalfEdge *edges, const HalfEdge *e_a, float *r_area)
float BLI_polyfill_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 void polyedge_rotate(HalfEdge *edges, const HalfEdge *e)
static void polyedge_beauty_cost_update_single(const float(*coords)[2], const HalfEdge *edges, HalfEdge *e, Heap *eheap, HeapNode **eheap_table)
static int oedge_cmp(const void *a1, const void *a2)
void BLI_polyfill_beautify(const float(*coords)[2], const uint coords_num, uint(*tris)[3], MemArena *arena, Heap *eheap)
BLI_INLINE bool is_boundary_edge(uint i_a, uint i_b, const uint coord_last)
static void polyedge_beauty_cost_update(const float(*coords)[2], HalfEdge *edges, HalfEdge *e, Heap *eheap, HeapNode **eheap_table)
#define fabsf
#define FLT_MAX
Definition stdcycles.h:14
i
Definition text_draw.cc:230