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