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