Blender V4.3
bmesh_bisect_plane.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
18#include <climits>
19
20#include "MEM_guardedalloc.h"
21
22#include "BLI_alloca.h"
23#include "BLI_linklist.h"
24#include "BLI_linklist_stack.h"
25#include "BLI_math_geom.h"
26#include "BLI_math_vector.h"
27#include "BLI_utildefines.h"
29
30#include "bmesh.hh"
31#include "bmesh_bisect_plane.hh" /* Own include. */
32
33#include "BLI_strict_flags.h" /* Keep last. */
34
35/* -------------------------------------------------------------------- */
39static short plane_point_test_v3(const float plane[4],
40 const float co[3],
41 const float eps,
42 float *r_depth)
43{
44 const float f = plane_point_side_v3(plane, co);
45 *r_depth = f;
46
47 if (f <= -eps) {
48 return -1;
49 }
50 if (f >= eps) {
51 return 1;
52 }
53 return 0;
54}
55
58/* -------------------------------------------------------------------- */
66#define BM_VERT_DIR(v) ((short *)(&(v)->head.index))[0] /* Direction -1/0/1 */
67#define BM_VERT_SKIP(v) ((short *)(&(v)->head.index))[1] /* Skip Vert 0/1 */
68#define BM_VERT_DIST(v) ((v)->no[0]) /* Distance from the plane. */
69#define BM_VERT_SORTVAL(v) ((v)->no[1]) /* Temp value for sorting. */
70#define BM_VERT_LOOPINDEX(v) /* The verts index within a face (temp var) */ \
71 (*((uint *)(&(v)->no[2])))
72
75/* -------------------------------------------------------------------- */
95
97{
98 const uint delta = uint(abs(int(BM_VERT_LOOPINDEX(v_a)) - int(BM_VERT_LOOPINDEX(v_b))));
99 return ELEM(delta, 1, uint(f_len_orig - 1));
100}
101
112{
113 return (BM_elem_flag_test(e, BM_ELEM_TAG) != 0);
114}
115
126{
127 return (BM_elem_flag_test(f, BM_ELEM_TAG) == 0);
128}
129
132/* -------------------------------------------------------------------- */
136static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
137{
138 const float val_a = BM_VERT_SORTVAL(*((BMVert **)v_a_v));
139 const float val_b = BM_VERT_SORTVAL(*((BMVert **)v_b_v));
140
141 if (val_a > val_b) {
142 return 1;
143 }
144 if (val_a < val_b) {
145 return -1;
146 }
147 return 0;
148}
149
151 BMesh *bm, BMFace *f, const float plane[4], const short oflag_center, const short oflag_new)
152{
153 /* Unlikely more than 2 verts are needed. */
154 const uint f_len_orig = uint(f->len);
155 BMVert **vert_split_arr = BLI_array_alloca(vert_split_arr, f_len_orig);
156 STACK_DECLARE(vert_split_arr);
157 BMLoop *l_iter, *l_first;
158 bool use_dirs[3] = {false, false, false};
159 bool is_inside = false;
160 /* True when the face contains one or more edges with both it's vertices on the plane.
161 * When set, centered loops are walked over to check if they need to be skipped. */
162 bool face_has_center_edge = false;
163
164 STACK_INIT(vert_split_arr, f_len_orig);
165
166 l_first = BM_FACE_FIRST_LOOP(f);
167
168 /* Add plane-aligned verts to the stack and check we have verts from both sides in this face
169 * (that the face doesn't only have boundary verts on the plane for eg). */
170 l_iter = l_first;
171 do {
172 if (vert_is_center_test(l_iter->v)) {
173 BLI_assert(BM_VERT_DIR(l_iter->v) == 0);
174
175 /* If both are -1 or 1, or both are zero: don't flip 'inside' var while walking. */
176 BM_VERT_SKIP(l_iter->v) = ((BM_VERT_DIR(l_iter->prev->v) ^ BM_VERT_DIR(l_iter->next->v)) ==
177 0);
178
179 STACK_PUSH(vert_split_arr, l_iter->v);
180
181 if (face_has_center_edge == false) {
182 if (vert_is_center_test(l_iter->prev->v)) {
183 face_has_center_edge = true;
184 }
185 }
186 }
187 use_dirs[BM_VERT_DIR(l_iter->v) + 1] = true;
188 } while ((l_iter = l_iter->next) != l_first);
189
190 if ((STACK_SIZE(vert_split_arr) > 1) && (use_dirs[0] && use_dirs[2])) {
191 if (LIKELY(STACK_SIZE(vert_split_arr) == 2)) {
192 BMLoop *l_new;
193 BMLoop *l_a, *l_b;
194
195 l_a = BM_face_vert_share_loop(f, vert_split_arr[0]);
196 l_b = BM_face_vert_share_loop(f, vert_split_arr[1]);
197
198 /* Common case, just cut the face once. */
199 BM_face_split(bm, f, l_a, l_b, &l_new, nullptr, true);
200 if (l_new) {
201 if (oflag_center | oflag_new) {
202 BMO_edge_flag_enable(bm, l_new->e, oflag_center | oflag_new);
203 }
204 if (oflag_new) {
205 BMO_face_flag_enable(bm, l_new->f, oflag_new);
206 }
207 }
208 }
209 else {
210 /* Less common case, _complicated_ we need to calculate how to do multiple cuts. */
211
212 uint i = 0;
213
214 /* ---- */
215 /* Check contiguous spans of centered vertices (skipping when necessary). */
216 if (face_has_center_edge) {
217
218 /* Loop indices need to be set for adjacency checks. */
219 l_iter = l_first;
220 do {
221 BM_VERT_LOOPINDEX(l_iter->v) = i++;
222 } while ((l_iter = l_iter->next) != l_first);
223
224 /* Start out on a non-centered vertex so a span of centered vertices can be looped over
225 * without having to scan backwards as well as forwards. */
226 BMLoop *l_first_non_center = l_first;
227 while (vert_is_center_test(l_first_non_center->v)) {
228 l_first_non_center = l_first_non_center->next;
229 }
230
231 l_iter = l_first_non_center;
232 do {
233 if (BM_VERT_SKIP(l_iter->v)) {
234 continue;
235 }
236 /* No need to check the previous as the iteration starts on a non-centered vertex. */
237 if (!(vert_is_center_test(l_iter->v) && vert_is_center_test(l_iter->next->v))) {
238 continue;
239 }
240
241 /* Walk over the next loops as long as they are centered. */
242 BMLoop *l_prev = l_iter->prev;
243 BMLoop *l_next = l_iter->next->next;
244 /* No need to scan the previous vertices,
245 * these will have been dealt with in previous steps. */
247 while (vert_is_center_test(l_next->v)) {
248 l_next = l_next->next;
249 }
250
251 /* Skip all vertices when the edges connected to the beginning/end
252 * of the range are on a different side of the bisecting plane. */
253 if (!(BM_VERT_DIR(l_prev->v) ^ BM_VERT_DIR(l_next->v))) {
255 l_iter = l_prev->next;
256 while (l_iter != l_next) {
258 BM_VERT_SKIP(l_iter->v) = true;
259 l_iter = l_iter->next;
260 }
261 }
262 /* Step over the span already handled, even if skip wasn't set. */
263 l_iter = l_next->prev;
264 } while ((l_iter = l_iter->next) != l_first_non_center);
265 }
266
267 BMFace **face_split_arr = BLI_array_alloca(face_split_arr, STACK_SIZE(vert_split_arr));
268 STACK_DECLARE(face_split_arr);
269
270 float sort_dir[3];
271
272 /* ---- */
273 /* Calculate the direction to sort verts in the face intersecting the plane */
274
275 /* The exact direction isn't important, vertices just need to be sorted across the face.
276 * (`sort_dir` could be flipped either way). */
278 cross_v3_v3v3(sort_dir, f->no, plane);
279 if (UNLIKELY(normalize_v3(sort_dir) == 0.0f)) {
280 /* find any 2 verts and get their direction */
281 for (i = 0; i < STACK_SIZE(vert_split_arr); i++) {
282 if (!equals_v3v3(vert_split_arr[0]->co, vert_split_arr[i]->co)) {
283 sub_v3_v3v3(sort_dir, vert_split_arr[0]->co, vert_split_arr[i]->co);
284 normalize_v3(sort_dir);
285 }
286 }
287 if (UNLIKELY(i == STACK_SIZE(vert_split_arr))) {
288 /* Ok, we can't do anything useful here,
289 * face has no area or so, bail out, this is highly unlikely but not impossible. */
290 goto finally;
291 }
292 }
293
294 /* ---- */
295 /* Sort the verts across the face from one side to another. */
296 for (i = 0; i < STACK_SIZE(vert_split_arr); i++) {
297 BMVert *v = vert_split_arr[i];
298 BM_VERT_SORTVAL(v) = dot_v3v3(sort_dir, v->co);
299 }
300
301 qsort(
302 vert_split_arr, STACK_SIZE(vert_split_arr), sizeof(*vert_split_arr), bm_vert_sortval_cb);
303
304 /* ---- */
305 /* Split the face across sorted splits. */
306
307 /* NOTE: we don't know which face gets which splits,
308 * so at the moment we have to search all faces for the vert pair,
309 * while not all that nice, typically there are < 5 resulting faces,
310 * so its not _that_ bad. */
311
312 STACK_INIT(face_split_arr, STACK_SIZE(vert_split_arr));
313 STACK_PUSH(face_split_arr, f);
314
315 for (i = 0; i < STACK_SIZE(vert_split_arr) - 1; i++) {
316 BMVert *v_a = vert_split_arr[i];
317 BMVert *v_b = vert_split_arr[i + 1];
318
319 if (face_has_center_edge) {
320 if (vert_pair_adjacent_in_orig_face(v_a, v_b, f_len_orig)) {
321 continue;
322 }
323 }
324
325 if (!BM_VERT_SKIP(v_a)) {
327 }
328
329 if (is_inside) {
330 BMLoop *l_a, *l_b;
331 bool found = false;
332 uint j;
333
334 for (j = 0; j < STACK_SIZE(face_split_arr); j++) {
335 /* It would be nice to avoid loop lookup here,
336 * but we need to know which face the verts are in. */
337 if ((l_a = BM_face_vert_share_loop(face_split_arr[j], v_a)) &&
338 (l_b = BM_face_vert_share_loop(face_split_arr[j], v_b)))
339 {
340 found = true;
341 break;
342 }
343 }
344
345 /* Ideally won't happen, but it can for self-intersecting faces. */
346 // BLI_assert(found == true);
347
348 /* In fact this simple test is good enough, test if the loops are adjacent. */
349 if (found && !BM_loop_is_adjacent(l_a, l_b)) {
350 BMLoop *l_new;
351 BMFace *f_tmp;
352 f_tmp = BM_face_split(bm, face_split_arr[j], l_a, l_b, &l_new, nullptr, true);
353
354 if (l_new) {
355 if (oflag_center | oflag_new) {
356 BMO_edge_flag_enable(bm, l_new->e, oflag_center | oflag_new);
357 }
358 if (oflag_new) {
359 BMO_face_flag_enable(bm, l_new->f, oflag_new);
360 }
361 }
362
363 if (f_tmp) {
364 if (f_tmp != face_split_arr[j]) {
365 STACK_PUSH(face_split_arr, f_tmp);
366 BLI_assert(STACK_SIZE(face_split_arr) <= STACK_SIZE(vert_split_arr));
367 }
368 }
369 }
370 }
371 else {
372 // printf("no intersect\n");
373 }
374 }
375 }
376 }
377
378finally:
379 (void)vert_split_arr;
380}
381
384/* -------------------------------------------------------------------- */
389 const float plane[4],
390 const bool use_snap_center,
391 const bool use_tag,
392 const short oflag_center,
393 const short oflag_new,
394 const float eps)
395{
396 uint einput_len;
397 uint i;
398 BMEdge **edges_arr = static_cast<BMEdge **>(
399 MEM_mallocN(sizeof(*edges_arr) * size_t(bm->totedge), __func__));
400
401 BLI_LINKSTACK_DECLARE(face_stack, BMFace *);
402
403 BMVert *v;
404 BMFace *f;
405
406 BMIter iter;
407
408 if (use_tag) {
409 /* Build tagged edge array. */
410 BMEdge *e;
411 einput_len = 0;
412
413 /* Flush edge tags to verts. */
415
416 /* Keep face tags as is. */
418 if (edge_is_cut_test(e)) {
419 edges_arr[einput_len++] = e;
420
421 /* Flush edge tags to verts. */
424 }
425 }
426
427 /* Face tags are set by caller. */
428 }
429 else {
430 BMEdge *e;
431 einput_len = uint(bm->totedge);
434 edges_arr[i] = e;
435 }
436
437 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
439 }
440 }
441
442 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
443
444 if (use_tag && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
446
447 /* These should never be accessed. */
448 BM_VERT_DIR(v) = 0;
449 BM_VERT_DIST(v) = 0.0f;
450
451 continue;
452 }
453
456
457 if (BM_VERT_DIR(v) == 0) {
458 if (oflag_center) {
459 BMO_vert_flag_enable(bm, v, oflag_center);
460 }
461 if (use_snap_center) {
462 closest_to_plane_v3(v->co, plane, v->co);
463 }
464 }
465 }
466
467 /* Store a stack of faces to be evaluated for splitting. */
468 BLI_LINKSTACK_INIT(face_stack);
469
470 for (i = 0; i < einput_len; i++) {
471 /* We could check `edge_is_cut_test(e)` but there is no point. */
472 BMEdge *e = edges_arr[i];
473 const int side[2] = {BM_VERT_DIR(e->v1), BM_VERT_DIR(e->v2)};
474 const float dist[2] = {BM_VERT_DIST(e->v1), BM_VERT_DIST(e->v2)};
475
476 if (side[0] && side[1] && (side[0] != side[1])) {
477 const float e_fac = dist[0] / (dist[0] - dist[1]);
478 BMVert *v_new;
479
480 if (e->l) {
481 BMLoop *l_iter, *l_first;
482 l_iter = l_first = e->l;
483 do {
484 if (!face_in_stack_test(l_iter->f)) {
485 face_in_stack_enable(l_iter->f);
486 BLI_LINKSTACK_PUSH(face_stack, l_iter->f);
487 }
488 } while ((l_iter = l_iter->radial_next) != l_first);
489 }
490
491 {
492 BMEdge *e_new;
493 v_new = BM_edge_split(bm, e, e->v1, &e_new, e_fac);
494 if (oflag_new) {
495 BMO_edge_flag_enable(bm, e_new, oflag_new);
496 }
497 }
498
500 if (oflag_new | oflag_center) {
501 BMO_vert_flag_enable(bm, v_new, oflag_new | oflag_center);
502 }
503
504 BM_VERT_DIR(v_new) = 0;
505 BM_VERT_DIST(v_new) = 0.0f;
506 }
507 else if (side[0] == 0 || side[1] == 0) {
508 /* Check if either edge verts are aligned,
509 * if so - tag and push all faces that use it into the stack. */
510 uint j;
511 BM_ITER_ELEM_INDEX (v, &iter, e, BM_VERTS_OF_EDGE, j) {
512 if (side[j] == 0) {
513 if (vert_is_center_test(v) == 0) {
514 BMIter itersub;
515 BMLoop *l_iter;
516
518
519 BM_ITER_ELEM (l_iter, &itersub, v, BM_LOOPS_OF_VERT) {
520 if (!face_in_stack_test(l_iter->f)) {
521 face_in_stack_enable(l_iter->f);
522 BLI_LINKSTACK_PUSH(face_stack, l_iter->f);
523 }
524 }
525 }
526 }
527 }
528
529 /* If both verts are on the center - tag it. */
530 if (oflag_center) {
531 if (side[0] == 0 && side[1] == 0) {
532 BMO_edge_flag_enable(bm, e, oflag_center);
533 }
534 }
535 }
536 }
537
538 MEM_freeN(edges_arr);
539
540 while ((f = BLI_LINKSTACK_POP(face_stack))) {
541 bm_face_bisect_verts(bm, f, plane, oflag_center, oflag_new);
542 }
543
544 /* Caused by access macros: #BM_VERT_DIR, #BM_VERT_SKIP. */
546
547 /* Now we have all faces to split in the stack. */
548 BLI_LINKSTACK_FREE(face_stack);
549}
550
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
MINLINE float plane_point_side_v3(const float plane[4], const float co[3])
void closest_to_plane_v3(float r_close[3], const float plane[4], const float pt[3])
Definition math_geom.cc:433
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3(float n[3])
unsigned int uint
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
Read Guarded memory(de)allocation.
BLI_INLINE void vert_is_center_enable(BMVert *v)
static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
void BM_mesh_bisect_plane(BMesh *bm, const float plane[4], const bool use_snap_center, const bool use_tag, const short oflag_center, const short oflag_new, const float eps)
BLI_INLINE void face_in_stack_enable(BMFace *f)
static void bm_face_bisect_verts(BMesh *bm, BMFace *f, const float plane[4], const short oflag_center, const short oflag_new)
#define BM_VERT_SKIP(v)
BLI_INLINE void vert_is_center_disable(BMVert *v)
BLI_INLINE bool edge_is_cut_test(BMEdge *e)
BLI_INLINE bool vert_is_center_test(BMVert *v)
BLI_INLINE void edge_is_cut_disable(BMEdge *e)
#define BM_VERT_DIR(v)
#define BM_VERT_LOOPINDEX(v)
#define BM_VERT_DIST(v)
static short plane_point_test_v3(const float plane[4], const float co[3], const float eps, float *r_depth)
BLI_INLINE bool face_in_stack_test(BMFace *f)
#define BM_VERT_SORTVAL(v)
BLI_INLINE void edge_is_cut_enable(BMEdge *e)
BLI_INLINE bool vert_pair_adjacent_in_orig_face(BMVert *v_a, BMVert *v_b, const uint f_len_orig)
BLI_INLINE void face_in_stack_disable(BMFace *f)
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_VERTS_OF_EDGE
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
BMVert * BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
Edge Split.
BMFace * BM_face_split(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, const bool no_double)
Face Split.
#define BM_VERT
#define BMO_edge_flag_enable(bm, e, oflag)
#define BMO_vert_flag_enable(bm, e, oflag)
#define BMO_face_flag_enable(bm, e, oflag)
bool BM_face_is_normal_valid(const BMFace *f)
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
ATTR_WARN_UNUSED_RESULT const BMVert * v
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:768
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
const btScalar eps
Definition poly34.cpp:11
float no[3]
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
char elem_index_dirty
int totedge
ccl_device_inline int abs(int x)
Definition util/math.h:120