Blender V5.0
scanfill_utils.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
8
9#include <cstdlib>
10
11#include "DNA_listBase.h"
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_ghash.h"
16#include "BLI_listbase.h"
17#include "BLI_math_geom.h"
18#include "BLI_math_vector.h"
19#include "BLI_utildefines.h"
20
21#include "BLI_scanfill.h" /* own include */
22
23#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
24
29
32 float co[3];
33
34 /* newly created vertex */
36};
37
38#define V_ISISECT 1
39#define E_ISISECT 1
40#define E_ISDELETE 2
41
42#define EFLAG_SET(eed, val) \
43 { \
44 CHECK_TYPE(eed, ScanFillEdge *); \
45 (eed)->user_flag = (eed)->user_flag | uint(val); \
46 } \
47 (void)0
48#if 0
49# define EFLAG_CLEAR(eed, val) \
50 { \
51 CHECK_TYPE(eed, ScanFillEdge *); \
52 (eed)->user_flag = (eed)->user_flag & ~uint(val); \
53 } \
54 (void)0
55#endif
56
57#define VFLAG_SET(eve, val) \
58 { \
59 CHECK_TYPE(eve, ScanFillVert *); \
60 (eve)->user_flag = (eve)->user_flag | uint(val); \
61 } \
62 (void)0
63#if 0
64# define VFLAG_CLEAR(eve, val) \
65 { \
66 CHECK_TYPE(eve, ScanFillVert *); \
67 (eve)->user_flags = (eve)->user_flag & ~uint(val); \
68 } \
69 (void)0
70#endif
71
72#if 0
73void BLI_scanfill_obj_dump(ScanFillContext *sf_ctx)
74{
75 FILE *f = fopen("test.obj", "w");
76 uint i = 1;
77
78 ScanFillVert *eve;
79 ScanFillEdge *eed;
80
81 for (eve = sf_ctx->fillvertbase.first; eve; eve = eve->next, i++) {
82 fprintf(f, "v %f %f %f\n", UNPACK3(eve->co));
83 eve->keyindex = i;
84 }
85 for (eed = sf_ctx->filledgebase.first; eed; eed = eed->next) {
86 fprintf(f, "f %d %d\n", eed->v1->keyindex, eed->v2->keyindex);
87 }
88 fclose(f);
89}
90#endif
91
93{
94 void **val_p;
95
96 if (!BLI_ghash_ensure_p(isect_hash, eed, &val_p)) {
97 *val_p = MEM_callocN<ListBase>(__func__);
98 }
99
100 return static_cast<ListBase *>(*val_p);
101}
102
104{
105 ListBase *e_ls;
106 LinkData *isect_link;
107 e_ls = edge_isect_ls_ensure(isect_hash, eed);
108 isect_link = MEM_callocN<LinkData>(__func__);
109 isect_link->data = isect;
110 EFLAG_SET(eed, E_ISISECT);
111 BLI_addtail(e_ls, isect_link);
112 return e_ls;
113}
114
115static int edge_isect_ls_sort_cb(void *thunk, const void *def_a_ptr, const void *def_b_ptr)
116{
117 const float *co = static_cast<const float *>(thunk);
118
119 const ScanFillIsect *i_a = static_cast<const ScanFillIsect *>(
120 ((const LinkData *)def_a_ptr)->data);
121 const ScanFillIsect *i_b = static_cast<const ScanFillIsect *>(
122 ((const LinkData *)def_b_ptr)->data);
123 const float a = len_squared_v2v2(co, i_a->co);
124 const float b = len_squared_v2v2(co, i_b->co);
125
126 if (a > b) {
127 return -1;
128 }
129
130 return (a < b);
131}
132
133static ScanFillEdge *edge_step(PolyInfo *poly_info,
134 const ushort poly_nr,
135 ScanFillVert *v_prev,
136 ScanFillVert *v_curr,
137 ScanFillEdge *e_curr)
138{
139 ScanFillEdge *eed;
140
141 BLI_assert(ELEM(v_prev, e_curr->v1, e_curr->v2));
142 BLI_assert(ELEM(v_curr, e_curr->v1, e_curr->v2));
143
144 eed = (e_curr->next && e_curr != poly_info[poly_nr].edge_last) ? e_curr->next :
145 poly_info[poly_nr].edge_first;
146 if (ELEM(v_curr, eed->v1, eed->v2) == true && ELEM(v_prev, eed->v1, eed->v2) == false) {
147 return eed;
148 }
149
150 eed = (e_curr->prev && e_curr != poly_info[poly_nr].edge_first) ? e_curr->prev :
151 poly_info[poly_nr].edge_last;
152 if (ELEM(v_curr, eed->v1, eed->v2) == true && ELEM(v_prev, eed->v1, eed->v2) == false) {
153 return eed;
154 }
155
156 BLI_assert(0);
157 return nullptr;
158}
159
161 PolyInfo *poly_info,
162 const ushort poly_nr,
163 ListBase *filledgebase)
164{
165 PolyInfo *pi = &poly_info[poly_nr];
166 GHash *isect_hash = nullptr;
167 ListBase isect_lb = {nullptr};
168
169 /* warning, O(n2) check here, should use spatial lookup */
170 {
171 ScanFillEdge *eed;
172
173 for (eed = pi->edge_first; eed; eed = (eed == pi->edge_last) ? nullptr : eed->next) {
174 ScanFillEdge *eed_other;
175
176 for (eed_other = eed->next; eed_other;
177 eed_other = (eed_other == pi->edge_last) ? nullptr : eed_other->next)
178 {
179 if (!ELEM(eed->v1, eed_other->v1, eed_other->v2) &&
180 !ELEM(eed->v2, eed_other->v1, eed_other->v2) && (eed != eed_other))
181 {
182 /* check isect */
183 float pt[2];
184 BLI_assert(eed != eed_other);
185
187 eed->v1->co, eed->v2->co, eed_other->v1->co, eed_other->v2->co, pt) == 1)
188 {
189 ScanFillIsect *isect;
190
191 if (UNLIKELY(isect_hash == nullptr)) {
192 isect_hash = BLI_ghash_ptr_new(__func__);
193 }
194
195 isect = MEM_mallocN<ScanFillIsect>(__func__);
196
197 BLI_addtail(&isect_lb, isect);
198
199 copy_v2_v2(isect->co, pt);
200 isect->co[2] = eed->v1->co[2];
201 isect->v = BLI_scanfill_vert_add(sf_ctx, isect->co);
202
203 /* NOTE: vert may belong to 2 polys now */
204 isect->v->poly_nr = eed->v1->poly_nr;
205
206 VFLAG_SET(isect->v, V_ISISECT);
207 edge_isect_ls_add(isect_hash, eed, isect);
208 edge_isect_ls_add(isect_hash, eed_other, isect);
209 }
210 }
211 }
212 }
213 }
214
215 if (isect_hash == nullptr) {
216 return false;
217 }
218
219 /* now subdiv the edges */
220 {
221 ScanFillEdge *eed;
222
223 for (eed = pi->edge_first; eed; eed = (eed == pi->edge_last) ? nullptr : eed->next) {
224 if (eed->user_flag & E_ISISECT) {
225 ListBase *e_ls = static_cast<ListBase *>(BLI_ghash_lookup(isect_hash, eed));
226
227 if (UNLIKELY(e_ls == nullptr)) {
228 /* only happens in very rare cases (entirely overlapping splines).
229 * in this case we can't do much useful. but at least don't crash */
230 continue;
231 }
232
233 /* Maintain correct terminating edge. */
234 if (pi->edge_last == eed) {
235 pi->edge_last = nullptr;
236 }
237
238 if (BLI_listbase_is_single(e_ls) == false) {
240 }
241
242 /* move original edge to filledgebase and add replacement
243 * (which gets subdivided next) */
244 {
245 ScanFillEdge *eed_tmp;
246 eed_tmp = BLI_scanfill_edge_add(sf_ctx, eed->v1, eed->v2);
247 BLI_remlink(&sf_ctx->filledgebase, eed_tmp);
248 BLI_insertlinkafter(&sf_ctx->filledgebase, eed, eed_tmp);
249 BLI_remlink(&sf_ctx->filledgebase, eed);
250 BLI_addtail(filledgebase, eed);
251 if (pi->edge_first == eed) {
252 pi->edge_first = eed_tmp;
253 }
254 eed = eed_tmp;
255 }
256
257 LISTBASE_FOREACH (LinkData *, isect_link, e_ls) {
258 ScanFillIsect *isect = static_cast<ScanFillIsect *>(isect_link->data);
259 ScanFillEdge *eed_subd;
260
261 eed_subd = BLI_scanfill_edge_add(sf_ctx, isect->v, eed->v2);
262 eed_subd->poly_nr = poly_nr;
263 eed->v2 = isect->v;
264
265 BLI_remlink(&sf_ctx->filledgebase, eed_subd);
266 BLI_insertlinkafter(&sf_ctx->filledgebase, eed, eed_subd);
267
268 /* step to the next edge and continue dividing */
269 eed = eed_subd;
270 }
271
272 BLI_freelistN(e_ls);
273 MEM_freeN(e_ls);
274
275 if (pi->edge_last == nullptr) {
276 pi->edge_last = eed;
277 }
278 }
279 }
280 }
281
282 BLI_freelistN(&isect_lb);
283 BLI_ghash_free(isect_hash, nullptr, nullptr);
284
285 {
286 ScanFillEdge *e_init;
287 ScanFillEdge *e_curr;
288 ScanFillEdge *e_next;
289
290 ScanFillVert *v_prev;
291 ScanFillVert *v_curr;
292
293 bool inside = false;
294
295 /* first vert */
296#if 0
297 e_init = pi->edge_last;
298 e_curr = e_init;
299 e_next = pi->edge_first;
300
301 v_prev = e_curr->v1;
302 v_curr = e_curr->v2;
303#else
304
305 /* find outside vertex */
306 {
307 ScanFillEdge *eed;
308 ScanFillEdge *eed_prev;
309 float min_x = FLT_MAX;
310
311 e_curr = pi->edge_last;
312 e_next = pi->edge_first;
313
314 eed_prev = pi->edge_last;
315 for (eed = pi->edge_first; eed; eed = (eed == pi->edge_last) ? nullptr : eed->next) {
316 if (eed->v2->co[0] < min_x) {
317 min_x = eed->v2->co[0];
318 e_curr = eed_prev;
319 e_next = eed;
320 }
321 eed_prev = eed;
322 }
323
324 e_init = e_curr;
325 v_prev = e_curr->v1;
326 v_curr = e_curr->v2;
327 }
328#endif
329
330 BLI_assert(e_curr->poly_nr == poly_nr);
331 BLI_assert(pi->edge_last->poly_nr == poly_nr);
332
333 do {
334 ScanFillVert *v_next;
335
336 v_next = (e_next->v1 == v_curr) ? e_next->v2 : e_next->v1;
337 BLI_assert(ELEM(v_curr, e_next->v1, e_next->v2));
338
339 /* track intersections */
340 if (inside) {
341 EFLAG_SET(e_next, E_ISDELETE);
342 }
343 if (v_next->user_flag & V_ISISECT) {
344 inside = !inside;
345 }
346 /* now step... */
347
348 v_prev = v_curr;
349 v_curr = v_next;
350 e_curr = e_next;
351
352 e_next = edge_step(poly_info, poly_nr, v_prev, v_curr, e_curr);
353
354 } while (e_curr != e_init);
355 }
356
357 return true;
358}
359
361 ListBase *remvertbase,
362 ListBase *remedgebase)
363{
364 const uint poly_num = uint(sf_ctx->poly_nr) + 1;
365 bool changed = false;
366
367 if (UNLIKELY(sf_ctx->poly_nr == SF_POLY_UNSET)) {
368 return false;
369 }
370
371 PolyInfo *poly_info = MEM_calloc_arrayN<PolyInfo>(poly_num, __func__);
372
373 /* get the polygon span */
374 if (sf_ctx->poly_nr == 0) {
375 poly_info->edge_first = static_cast<ScanFillEdge *>(sf_ctx->filledgebase.first);
376 poly_info->edge_last = static_cast<ScanFillEdge *>(sf_ctx->filledgebase.last);
377 }
378 else {
379 ushort poly_nr = 0;
380 uint eed_index = 0;
381
382 LISTBASE_FOREACH_INDEX (ScanFillEdge *, eed, &sf_ctx->filledgebase, eed_index) {
383 BLI_assert(eed->poly_nr == eed->v1->poly_nr);
384 BLI_assert(eed->poly_nr == eed->v2->poly_nr);
385
386 if ((poly_info[poly_nr].edge_last != nullptr) &&
387 (poly_info[poly_nr].edge_last->poly_nr != eed->poly_nr))
388 {
389 poly_nr++;
390 }
391
392 if (poly_info[poly_nr].edge_first == nullptr) {
393 poly_info[poly_nr].edge_first = eed;
394 poly_info[poly_nr].edge_last = eed;
395 }
396 else if (poly_info[poly_nr].edge_last->poly_nr == eed->poly_nr) {
397 poly_info[poly_nr].edge_last = eed;
398 }
399
400 BLI_assert(poly_info[poly_nr].edge_first->poly_nr == poly_info[poly_nr].edge_last->poly_nr);
401 }
402 }
403
404 /* self-intersect each polygon */
405 {
406 ushort poly_nr;
407 for (poly_nr = 0; poly_nr < poly_num; poly_nr++) {
408 changed |= scanfill_preprocess_self_isect(sf_ctx, poly_info, poly_nr, remedgebase);
409 }
410 }
411
412 MEM_freeN(poly_info);
413
414 if (changed == false) {
415 return false;
416 }
417
418 /* move free edges into their own list */
419 {
421 if (eed->user_flag & E_ISDELETE) {
422 BLI_remlink(&sf_ctx->filledgebase, eed);
423 BLI_addtail(remedgebase, eed);
424 }
425 }
426 }
427
428 /* move free vertices into their own list */
429 {
430 LISTBASE_FOREACH (ScanFillVert *, eve, &sf_ctx->fillvertbase) {
431 eve->user_flag = 0;
432 eve->poly_nr = SF_POLY_UNSET;
433 }
434 LISTBASE_FOREACH (ScanFillEdge *, eed, &sf_ctx->filledgebase) {
435 eed->v1->user_flag = 1;
436 eed->v2->user_flag = 1;
437 eed->poly_nr = SF_POLY_UNSET;
438 }
439
441 if (eve->user_flag != 1) {
442 BLI_remlink(&sf_ctx->fillvertbase, eve);
443 BLI_addtail(remvertbase, eve);
444 }
445 else {
446 eve->user_flag = 0;
447 }
448 }
449 }
450
451 /* polygon id's are no longer meaningful,
452 * when removing self intersections we may have created new isolated polys */
453 sf_ctx->poly_nr = SF_POLY_UNSET;
454
455#if 0
456 BLI_scanfill_view3d_dump(sf_ctx);
457 BLI_scanfill_obj_dump(sf_ctx);
458#endif
459
460 return changed;
461}
#define BLI_assert(a)
Definition BLI_assert.h:46
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:731
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:752
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void void void BLI_listbase_sort_r(ListBase *listbase, int(*cmp)(void *, const void *, const void *), void *thunk) ATTR_NONNULL(1
void void BLI_INLINE bool BLI_listbase_is_single(const ListBase *lb)
int isect_seg_seg_v2_point(const float v0[2], const float v1[2], const float v2[2], const float v3[2], float r_vi[2])
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2(float r[2], const float a[2])
struct ScanFillVert * BLI_scanfill_vert_add(ScanFillContext *sf_ctx, const float vec[3])
Definition scanfill.cc:100
struct ScanFillEdge * BLI_scanfill_edge_add(ScanFillContext *sf_ctx, struct ScanFillVert *v1, struct ScanFillVert *v2)
Definition scanfill.cc:122
#define SF_POLY_UNSET
unsigned int uint
unsigned short ushort
#define UNPACK3(a)
#define UNLIKELY(x)
#define ELEM(...)
These structs are the foundation for all linked lists in the library system.
Read Guarded memory(de)allocation.
BMesh const char void * data
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define E_ISISECT
static ListBase * edge_isect_ls_add(GHash *isect_hash, ScanFillEdge *eed, ScanFillIsect *isect)
#define EFLAG_SET(eed, val)
#define VFLAG_SET(eve, val)
static int edge_isect_ls_sort_cb(void *thunk, const void *def_a_ptr, const void *def_b_ptr)
static ScanFillEdge * edge_step(PolyInfo *poly_info, const ushort poly_nr, ScanFillVert *v_prev, ScanFillVert *v_curr, ScanFillEdge *e_curr)
#define E_ISDELETE
bool BLI_scanfill_calc_self_isect(ScanFillContext *sf_ctx, ListBase *remvertbase, ListBase *remedgebase)
#define V_ISISECT
static ListBase * edge_isect_ls_ensure(GHash *isect_hash, ScanFillEdge *eed)
static bool scanfill_preprocess_self_isect(ScanFillContext *sf_ctx, PolyInfo *poly_info, const ushort poly_nr, ListBase *filledgebase)
#define FLT_MAX
Definition stdcycles.h:14
void * data
void * last
void * first
ScanFillEdge * edge_first
ScanFillEdge * edge_last
ScanFillVert * vert_outer
ListBase fillvertbase
ListBase filledgebase
unsigned short poly_nr
struct ScanFillEdge * prev
unsigned short poly_nr
struct ScanFillVert * v1
struct ScanFillVert * v2
struct ScanFillEdge * next
unsigned int user_flag
ScanFillIsect * prev
ScanFillIsect * next
ScanFillVert * v
unsigned short poly_nr
struct ScanFillVert * next
float co[3]
unsigned int user_flag
unsigned int keyindex
i
Definition text_draw.cc:230