Blender V4.3
editmesh_rip.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "MEM_guardedalloc.h"
10
11#include "DNA_object_types.h"
12
13#include "BLI_math_geom.h"
14#include "BLI_math_vector.h"
15#include "BLI_math_vector.hh"
16#include "BLI_span.hh"
17#include "BLI_vector.hh"
18
19#include "BKE_context.hh"
20#include "BKE_editmesh.hh"
21#include "BKE_layer.hh"
22#include "BKE_report.hh"
23
24#include "BLT_translation.hh"
25
26#include "RNA_access.hh"
27#include "RNA_define.hh"
28
29#include "WM_types.hh"
30
31#include "ED_mesh.hh"
32#include "ED_transform.hh"
33#include "ED_view3d.hh"
34
35#include "bmesh.hh"
36#include "bmesh_tools.hh"
37
38#include "mesh_intern.hh" /* own include */
39
40using blender::float2;
41using blender::float3;
42using blender::Span;
43using blender::Vector;
44
45/* -------------------------------------------------------------------- */
56#define INSET_DEFAULT 0.00001f
58 const blender::float4x4 &mat,
59 const float co1[3],
60 const float co2[3],
61 const float mvalf[2],
62 const float inset)
63{
64
65 float2 vec1 = ED_view3d_project_float_v2_m4(region, co1, mat);
66 float2 vec2 = ED_view3d_project_float_v2_m4(region, co2, mat);
67
68 if (inset != 0.0f) {
69 const float dist_2d = len_v2v2(vec1, vec2);
70 if (dist_2d > FLT_EPSILON) {
71 const float dist = inset / dist_2d;
72 BLI_assert(isfinite(dist));
73 interp_v2_v2v2(vec1, vec1, vec2, dist);
74 interp_v2_v2v2(vec2, vec2, vec1, dist);
75 }
76 }
77
78 const float dist_sq = dist_squared_to_line_segment_v2(mvalf, vec1, vec2);
79 BLI_assert(isfinite(dist_sq));
80
81 return dist_sq;
82}
83
84#if 0
85static float edbm_rip_linedist(
86 ARegion *region, float mat[4][4], const float co1[3], const float co2[3], const float mvalf[2])
87{
88 float vec1[2], vec2[2];
89
90 ED_view3d_project_float_v2_m4(region, co1, vec1, mat);
91 ED_view3d_project_float_v2_m4(region, co2, vec2, mat);
92
93 return dist_to_line_v2(mvalf, vec1, vec2);
94}
95#endif
96
100static void edbm_calc_loop_co(BMLoop *l, float l_mid_co[3])
101{
102 BM_loop_calc_face_tangent(l, l_mid_co);
103
104 /* scale to average of surrounding edge size, only needs to be approx, but should
105 * be roughly equivalent to the check below which uses the middle of the edge. */
106 mul_v3_fl(l_mid_co, (BM_edge_calc_length(l->e) + BM_edge_calc_length(l->prev->e)) / 2.0f);
107
108 add_v3_v3(l_mid_co, l->v->co);
109}
110
112 BMLoop *e_l,
113 ARegion *region,
114 const blender::float4x4 &projectMat,
115 const float fmval[2])
116{
117 float vec[2];
118 float fmval_tweak[2];
119 float score;
120
121 BMVert *v1_other;
122 BMVert *v2_other;
123
125
126 /* method for calculating distance:
127 *
128 * for each edge: calculate face center, then made a vector
129 * from edge midpoint to face center. offset edge midpoint
130 * by a small amount along this vector. */
131
132 /* rather than the face center, get the middle of
133 * both edge verts connected to this one */
134 v1_other = BM_face_other_vert_loop(e_l->f, e->v2, e->v1)->v;
135 v2_other = BM_face_other_vert_loop(e_l->f, e->v1, e->v2)->v;
136
138 region, blender::math::midpoint(float3(v1_other->co), float3(v2_other->co)), projectMat);
140 region, blender::math::midpoint(float3(e->v1->co), float3(e->v2->co)), projectMat);
141
142 float2 e_v1_co = ED_view3d_project_float_v2_m4(region, e->v1->co, projectMat);
143 float2 e_v2_co = ED_view3d_project_float_v2_m4(region, e->v2->co, projectMat);
144
145 sub_v2_v2v2(vec, cent, mid);
146 normalize_v2_length(vec, 0.01f);
147
148 /* rather than adding to both verts, subtract from the mouse */
149 sub_v2_v2v2(fmval_tweak, fmval, vec);
150
151 score = len_v2v2(e_v1_co, e_v2_co);
152
153 if (dist_squared_to_line_segment_v2(fmval_tweak, e_v1_co, e_v2_co) >
154 dist_squared_to_line_segment_v2(fmval, e_v1_co, e_v2_co))
155 {
156 return score;
157 }
158 return -score;
159}
160
163/* -------------------------------------------------------------------- */
199#define IS_VISIT_POSSIBLE(e) (BM_edge_is_manifold(e) && BM_elem_flag_test(e, BM_ELEM_TAG))
200#define IS_VISIT_DONE(e) ((e)->l && (BM_elem_index_get((e)->l) != INVALID_UID))
201#define INVALID_UID INT_MIN
202
203/* mark, assign uid and step */
205{
206 BMIter iter;
207 BMEdge *e;
208 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
210 BMLoop *l_a, *l_b;
211
212 BM_edge_loop_pair(e, &l_a, &l_b); /* no need to check, we know this will be true */
213
214 /* so (IS_VISIT_DONE == true) */
215 BM_elem_index_set(l_a, uid); /* set_dirty */
216 BM_elem_index_set(l_b, uid); /* set_dirty */
217
218 return e;
219 }
220 }
221 return nullptr;
222}
223
228
230{
231 BMIter fiter;
232 BMIter liter;
233
234 BMFace *f;
235 BMLoop *l;
236
237 int uid_start;
238 int uid_end;
239 int uid = bm->totedge; /* can start anywhere */
240
241 /* initialize loops with dummy invalid index values */
242 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
243 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
244 BM_elem_index_set(l, INVALID_UID); /* set_dirty */
245 }
246 }
248
249 Vector<EdgeLoopPair> eloop_pairs;
250
251 /* set contiguous loops ordered 'uid' values for walking after split */
252 while (true) {
253 int tot = 0;
254 BMIter eiter;
255 BMEdge *e_step;
256 BMVert *v_step;
257 BMEdge *e;
258 BMEdge *e_first;
259 BMEdge *e_last;
260
261 e_first = nullptr;
262 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
264 e_first = e;
265 break;
266 }
267 }
268
269 if (e_first == nullptr) {
270 break;
271 }
272
273 /* Initialize. */
274 e_first = e;
275 v_step = e_first->v1;
276 e_step = nullptr; /* quiet warning, will never remain this value */
277
278 uid_start = uid;
279 while ((e = edbm_ripsel_edge_mark_step(v_step, uid))) {
280 v_step = BM_edge_other_vert((e_step = e), v_step);
281 uid++; /* only different line */
282 tot++;
283 }
284
285 /* this edges loops have the highest uid's, store this to walk down later */
286 e_last = e_step;
287
288 /* always store the highest 'uid' edge for the stride */
289 uid_end = uid - 1;
290 uid = uid_start - 1;
291
292 /* initialize */
293 v_step = e_first->v1;
294
295 while ((e = edbm_ripsel_edge_mark_step(v_step, uid))) {
296 v_step = BM_edge_other_vert((e_step = e), v_step);
297 uid--; /* only different line */
298 tot++;
299 }
300
301 /* stride far enough not to _ever_ overlap range */
302 uid_start = uid;
303 uid = uid_end + bm->totedge;
304
305 /* no need to check, we know this will be true */
306 EdgeLoopPair lp;
307 BM_edge_loop_pair(e_last, &lp.l_a, &lp.l_b);
308 eloop_pairs.append(lp);
309
310 BLI_assert(tot == uid_end - uid_start);
312
313#if 0
314 printf("%s: found contiguous edge loop of (%d)\n", __func__, uid_end - uid_start);
315#endif
316 }
317
318 return eloop_pairs;
319}
320
323/* -------------------------------------------------------------------- */
328{
329 BMIter eiter;
330 BMEdge *e;
331 BMVert *v = BM_edge_other_vert(e_orig, *v_prev);
332 const int uid_cmp = BM_elem_index_get(e_orig->l) - 1;
333
334 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
335 if (BM_elem_index_get(e->l) == uid_cmp) {
336 *v_prev = v;
337 return e;
338 }
339 }
340 return nullptr;
341}
342
344{
345 /* try step in a direction, if it fails we know do go the other way */
346 BMVert *v_test = e->v1;
347 return edbm_ripsel_edge_uid_step(e, &v_test) ? e->v1 : e->v2;
348}
349
351 const Span<EdgeLoopPair> eloop_pairs,
352 ARegion *region,
353 const blender::float4x4 &projectMat,
354 const float fmval[2])
355{
356 for (const EdgeLoopPair &lp : eloop_pairs) {
357 BMEdge *e;
358 BMVert *v_prev;
359
360 float score_a = 0.0f;
361 float score_b = 0.0f;
362
363 e = lp.l_a->e;
365 for (; e; e = edbm_ripsel_edge_uid_step(e, &v_prev)) {
366 score_a += edbm_rip_edge_side_measure(e, e->l, region, projectMat, fmval);
367 }
368 e = lp.l_b->e;
370 for (; e; e = edbm_ripsel_edge_uid_step(e, &v_prev)) {
371 score_b += edbm_rip_edge_side_measure(e, e->l, region, projectMat, fmval);
372 }
373
374 e = (score_a > score_b) ? lp.l_a->e : lp.l_b->e;
376 for (; e; e = edbm_ripsel_edge_uid_step(e, &v_prev)) {
377 BM_edge_select_set(bm, e, false);
378 }
379 }
380}
381
384/* -------------------------------------------------------------------- */
400 char flag;
401};
402enum {
403 ULP_FLIP_0 = (1 << 0),
404 ULP_FLIP_1 = (1 << 1),
405};
406
408{
409 BMIter iter;
410 BMEdge *e;
411
412 uint total_tag = 0;
413 /* count tags, could be pre-calculated */
414 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
416 total_tag++;
417 }
418 }
419
420 if (total_tag) {
421 UnorderedLoopPair *uloop_pairs = static_cast<UnorderedLoopPair *>(
422 MEM_mallocN(total_tag * sizeof(UnorderedLoopPair), __func__));
423 UnorderedLoopPair *ulp = uloop_pairs;
424
425 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
427 BMLoop *l1, *l2;
428 if (BM_edge_loop_pair(e, &l1, &l2)) {
429 BMVert *v_cmp = l1->e->v1;
430 ulp->flag = (((l1->v != v_cmp) ? ULP_FLIP_0 : 0) | ((l2->v == v_cmp) ? ULP_FLIP_1 : 0));
431 }
432 else {
433 ulp->flag = 0;
434 }
435 ulp->l_pair[0] = l1;
436 ulp->l_pair[1] = l2;
437
438 ulp++;
439 }
440 }
441
442 return uloop_pairs;
443 }
444 return nullptr;
445}
446
448{
450 uint total_tag = MEM_allocN_len(uloop_pairs) / sizeof(UnorderedLoopPair);
451 uint i;
452
453 for (i = 0, ulp = uloop_pairs; i < total_tag; i++, ulp++) {
454 if ((ulp->l_pair[0] && ulp->l_pair[1]) && (ulp->l_pair[0]->e != ulp->l_pair[1]->e)) {
455 /* time has come to make a face! */
456 BMVert *v_shared = BM_edge_share_vert(ulp->l_pair[0]->e, ulp->l_pair[1]->e);
457 BMFace *f, *f_example = ulp->l_pair[0]->f;
458 BMLoop *l_iter;
459 BMVert *f_verts[4];
460
461 if (v_shared == nullptr) {
462 /* quad */
463 f_verts[0] = ulp->l_pair[0]->e->v1;
464 f_verts[1] = ulp->l_pair[1]->e->v1;
465 f_verts[2] = ulp->l_pair[1]->e->v2;
466 f_verts[3] = ulp->l_pair[0]->e->v2;
467
468 if (ulp->flag & ULP_FLIP_0) {
469 std::swap(f_verts[0], f_verts[3]);
470 }
471 if (ulp->flag & ULP_FLIP_1) {
472 std::swap(f_verts[1], f_verts[2]);
473 }
474 }
475 else {
476 /* tri */
477 f_verts[0] = v_shared;
478 f_verts[1] = BM_edge_other_vert(ulp->l_pair[0]->e, v_shared);
479 f_verts[2] = BM_edge_other_vert(ulp->l_pair[1]->e, v_shared);
480 f_verts[3] = nullptr;
481
482 /* don't use the flip flags */
483 if (v_shared == ulp->l_pair[0]->v) {
484 std::swap(f_verts[0], f_verts[1]);
485 }
486 }
487
488 /* face should never exist */
489 BLI_assert(!BM_face_exists(f_verts, f_verts[3] ? 4 : 3));
490
491 f = BM_face_create_verts(bm, f_verts, f_verts[3] ? 4 : 3, f_example, BM_CREATE_NOP, true);
492
493 l_iter = BM_FACE_FIRST_LOOP(f);
494
495 if (f_verts[3]) {
496 BM_elem_attrs_copy(bm, BM_edge_other_loop(ulp->l_pair[0]->e, l_iter), l_iter);
497 l_iter = l_iter->next;
498 BM_elem_attrs_copy(bm, BM_edge_other_loop(ulp->l_pair[1]->e, l_iter), l_iter);
499 l_iter = l_iter->next;
500 BM_elem_attrs_copy(bm, BM_edge_other_loop(ulp->l_pair[1]->e, l_iter), l_iter);
501 l_iter = l_iter->next;
502 BM_elem_attrs_copy(bm, BM_edge_other_loop(ulp->l_pair[0]->e, l_iter), l_iter);
503 }
504 else {
505 BM_elem_attrs_copy(bm, BM_edge_other_loop(ulp->l_pair[0]->e, l_iter), l_iter);
506 l_iter = l_iter->next;
507 BM_elem_attrs_copy(bm, BM_edge_other_loop(ulp->l_pair[0]->e, l_iter), l_iter);
508 l_iter = l_iter->next;
509 BM_elem_attrs_copy(bm, BM_edge_other_loop(ulp->l_pair[1]->e, l_iter), l_iter);
510 }
511 }
512 }
513}
514
517/* -------------------------------------------------------------------- */
524static int edbm_rip_invoke__vert(bContext *C, const wmEvent *event, Object *obedit, bool do_fill)
525{
526 UnorderedLoopPair *fill_uloop_pairs = nullptr;
527 ARegion *region = CTX_wm_region(C);
530 BMesh *bm = em->bm;
531 BMIter iter, liter;
532 BMLoop *l;
533 BMEdge *e_best;
534 BMVert *v;
535 const int totvert_orig = bm->totvert;
536 int i;
537 float fmval[3] = {float(event->mval[0]), float(event->mval[1])};
538 float dist_sq = FLT_MAX;
539 float d;
540 bool is_wire, is_manifold_region;
541
542 BMEditSelection ese;
543 int totboundary_edge = 0;
544
545 const blender::float4x4 projectMat = ED_view3d_ob_project_mat_get(rv3d, obedit);
546
547 /* find selected vert - same some time and check history first */
548 if (BM_select_history_active_get(bm, &ese) && ese.htype == BM_VERT) {
549 v = (BMVert *)ese.ele;
550 }
551 else {
552 ese.ele = nullptr;
553
554 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
556 break;
557 }
558 }
559 }
560
561 /* (v == nullptr) should be impossible */
562 if ((v == nullptr) || (v->e == nullptr)) {
563 return OPERATOR_CANCELLED;
564 }
565
566 is_wire = BM_vert_is_wire(v);
567 is_manifold_region = BM_vert_is_manifold_region(v);
568
569 e_best = nullptr;
570
571 {
572 BMEdge *e;
573 /* find closest edge to mouse cursor */
574 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
575 /* consider wire as boundary for this purpose,
576 * otherwise we can't a face away from a wire edge */
577 totboundary_edge += (BM_edge_is_boundary(e) || BM_edge_is_wire(e));
579 if ((is_manifold_region == false) || BM_edge_is_manifold(e)) {
581 region, projectMat, e->v1->co, e->v2->co, fmval, INSET_DEFAULT);
582 if ((e_best == nullptr) || (d < dist_sq)) {
583 dist_sq = d;
584 e_best = e;
585 }
586 }
587 }
588 }
589 }
590
591 if (e_best && e_best->l && (is_manifold_region == false)) {
592 /* Try to split off a non-manifold fan (when we have multiple disconnected fans) */
593 BMLoop *l_sep = e_best->l->v == v ? e_best->l : e_best->l->next;
594 BMVert *v_new;
595
596 BLI_assert(l_sep->v == v);
599
600 BM_vert_select_set(bm, v, false);
602
603 BM_vert_select_set(bm, v_new, true);
604 if (ese.ele) {
606 }
607
608 if (do_fill) {
609 BM_edge_create(bm, v, v_new, nullptr, BM_CREATE_NOP);
610 }
611
612 return OPERATOR_FINISHED;
613 }
614
615 /* if we are ripping a single vertex from 3 faces,
616 * then measure the distance to the face corner as well as the edge */
618 BMEdge *e_all[3];
619 BMLoop *l_all[3];
620 int i1, i2;
621
622 BM_iter_as_array(bm, BM_EDGES_OF_VERT, v, (void **)e_all, 3);
623 BM_iter_as_array(bm, BM_LOOPS_OF_VERT, v, (void **)l_all, 3);
624
625 /* not do a loop similar to the one above, but test against loops */
626 for (i1 = 0; i1 < 3; i1++) {
627 /* consider wire as boundary for this purpose,
628 * otherwise we can't a face away from a wire edge */
629 float l_mid_co[3];
630 l = l_all[i1];
631 edbm_calc_loop_co(l, l_mid_co);
632 d = edbm_rip_edgedist_squared(region, projectMat, l->v->co, l_mid_co, fmval, INSET_DEFAULT);
633 if ((e_best == nullptr) || (d < dist_sq)) {
634 dist_sq = d;
635
636 /* find the edge that is not in this loop */
637 e_best = nullptr;
638 for (i2 = 0; i2 < 3; i2++) {
639 if (!BM_edge_in_loop(e_all[i2], l)) {
640 e_best = e_all[i2];
641 break;
642 }
643 }
644 BLI_assert(e_best != nullptr);
645 }
646 }
647 }
648
649 /* should we go ahead with edge rip or do we need to do special case, split off vertex?:
650 * split off vertex if...
651 * - we can't find an edge - this means we are ripping a faces vert that is connected to other
652 * geometry only at the vertex.
653 * - the boundary edge total is greater than 2,
654 * in this case edge split _can_ work but we get far nicer results if we use this special case.
655 * - there are only 2 edges but we are a wire vert. */
656 if ((is_wire == false && totboundary_edge > 2) || (is_wire == true && totboundary_edge > 1)) {
657 BMVert **vout;
658 int vout_len;
659
660 BM_vert_select_set(bm, v, false);
661
662 bmesh_kernel_vert_separate(bm, v, &vout, &vout_len, true);
663
664 if (vout_len < 2) {
665 MEM_freeN(vout);
666 /* set selection back to avoid active-unselected vertex */
667 BM_vert_select_set(bm, v, true);
668 /* should never happen */
669 return OPERATOR_CANCELLED;
670 }
671
672 int vi_best = 0;
673
674 if (ese.ele) {
676 }
677
678 dist_sq = FLT_MAX;
679
680 /* in the loop below we find the best vertex to drag based on its connected geometry,
681 * either by its face corner, or connected edge (when no faces are attached) */
682 for (i = 0; i < vout_len; i++) {
683
684 if (BM_vert_is_wire(vout[i]) == false) {
685 /* find the best face corner */
686 BM_ITER_ELEM (l, &iter, vout[i], BM_LOOPS_OF_VERT) {
688 float l_mid_co[3];
689
690 edbm_calc_loop_co(l, l_mid_co);
692 region, projectMat, v->co, l_mid_co, fmval, INSET_DEFAULT);
693
694 if (d < dist_sq) {
695 dist_sq = d;
696 vi_best = i;
697 }
698 }
699 }
700 }
701 else {
702 BMEdge *e;
703 /* a wire vert, find the best edge */
704 BM_ITER_ELEM (e, &iter, vout[i], BM_EDGES_OF_VERT) {
706 float e_mid_co[3];
707
708 mid_v3_v3v3(e_mid_co, e->v1->co, e->v2->co);
710 region, projectMat, v->co, e_mid_co, fmval, INSET_DEFAULT);
711
712 if (d < dist_sq) {
713 dist_sq = d;
714 vi_best = i;
715 }
716 }
717 }
718 }
719 }
720
721 /* vout[0] == best
722 * vout[1] == glue
723 * vout[2+] == splice with glue (when vout_len > 2)
724 */
725 if (vi_best != 0) {
726 std::swap(vout[0], vout[vi_best]);
727 vi_best = 0;
728 }
729
730 /* select the vert from the best region */
731 v = vout[vi_best];
732 BM_vert_select_set(bm, v, true);
733
734 if (ese.ele) {
736 }
737
738 /* splice all others back together */
739 if (vout_len > 2) {
740 for (i = 2; i < vout_len; i++) {
741 BM_vert_splice(bm, vout[1], vout[i]);
742 }
743 }
744
745 if (do_fill) {
746 /* match extrude vert-order */
747 BM_edge_create(bm, vout[1], vout[0], nullptr, BM_CREATE_NOP);
748 }
749
750 MEM_freeN(vout);
751
752 return OPERATOR_FINISHED;
753 }
754
755 if (!e_best) {
756 return OPERATOR_CANCELLED;
757 }
758
759 /* *** Execute the split! *** */
760 /* unlike edge split, for single vertex split we only use the operator in one of the cases
761 * but both allocate fill */
762
763 {
764 BMVert *v_rip;
765 BMLoop *larr[2];
766 int larr_len = 0;
767
768 /* rip two adjacent edges */
770 /* Don't run the edge split operator in this case */
771
772 l = BM_edge_vert_share_loop(e_best->l, v);
773 larr[larr_len] = l;
774 larr_len++;
775
776 /* only tag for face-fill (we don't call the operator) */
777 if (BM_edge_is_boundary(e_best)) {
779 }
780 else {
783 }
784 }
785 else {
786 if (BM_edge_is_manifold(e_best)) {
787 BMLoop *l_iter, *l_first;
788 l_iter = l_first = e_best->l;
789 do {
790 larr[larr_len] = BM_edge_vert_share_loop(l_iter, v);
791
792 if (do_fill) {
793 /* Only needed when filling...
794 * Also, we never want to tag best edge,
795 * that one won't change during split. See #44618. */
796 if (larr[larr_len]->e == e_best) {
797 BM_elem_flag_enable(larr[larr_len]->prev->e, BM_ELEM_TAG);
798 }
799 else {
800 BM_elem_flag_enable(larr[larr_len]->e, BM_ELEM_TAG);
801 }
802 }
803 larr_len++;
804 } while ((l_iter = l_iter->radial_next) != l_first);
805 }
806 else {
807 /* NOTE(@ideasman42): It looks like there are no split edges,
808 * we could just return/report-error? */
809 }
810 }
811
812 /* keep directly before edgesplit */
813 if (do_fill) {
814 fill_uloop_pairs = edbm_tagged_loop_pairs_to_fill(bm);
815 }
816
817 if (larr_len) {
818 v_rip = BM_face_loop_separate_multi(bm, larr, larr_len);
819 }
820 else {
821 v_rip = nullptr;
822 }
823
824 if (v_rip) {
825 BM_vert_select_set(bm, v_rip, true);
826 }
827 else {
828 if (fill_uloop_pairs) {
829 MEM_freeN(fill_uloop_pairs);
830 }
831 return OPERATOR_CANCELLED;
832 }
833 }
834
835 {
836 /* --- select which vert --- */
837 BMVert *v_best = nullptr;
838 float l_corner_co[3];
839
840 dist_sq = FLT_MAX;
841 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
843 /* disable by default, re-enable winner at end */
844 BM_vert_select_set(bm, v, false);
846
847 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
848
849 /* check if v_best is null in the _rare_ case there are numeric issues */
850 edbm_calc_loop_co(l, l_corner_co);
852 region, projectMat, l->v->co, l_corner_co, fmval, INSET_DEFAULT);
853 if ((v_best == nullptr) || (d < dist_sq)) {
854 v_best = v;
855 dist_sq = d;
856 }
857 }
858 }
859 }
860
861 if (v_best) {
862 BM_vert_select_set(bm, v_best, true);
863 if (ese.ele) {
865 }
866 }
867 }
868
869 if (do_fill && fill_uloop_pairs) {
870 edbm_tagged_loop_pairs_do_fill_faces(bm, fill_uloop_pairs);
871 MEM_freeN(fill_uloop_pairs);
872 }
873
874 if (totvert_orig == bm->totvert) {
875 return OPERATOR_CANCELLED;
876 }
877
878 return OPERATOR_FINISHED;
879}
880
883/* -------------------------------------------------------------------- */
890static int edbm_rip_invoke__edge(bContext *C, const wmEvent *event, Object *obedit, bool do_fill)
891{
892 UnorderedLoopPair *fill_uloop_pairs = nullptr;
893 ARegion *region = CTX_wm_region(C);
896 BMesh *bm = em->bm;
897 BMIter iter, eiter;
898 BMLoop *l;
899 BMEdge *e_best;
900 BMVert *v;
901 const int totvert_orig = bm->totvert;
902 const int totedge_orig = bm->totedge;
903 float fmval[3] = {float(event->mval[0]), float(event->mval[1])};
904
905 const blender::float4x4 projectMat = ED_view3d_ob_project_mat_get(rv3d, obedit);
906
907 /* important this runs on the original selection, before tampering with tagging */
909
910 /* expand edge selection */
911 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
912 BMEdge *e;
913 bool all_manifold;
914 int totedge_manifold; /* manifold, visible edges */
915 int i;
916
917 e_best = nullptr;
918 i = 0;
919 totedge_manifold = 0;
920 all_manifold = true;
921 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
922
924 /* important to check selection rather than tag here
925 * else we get feedback loop */
927 e_best = e;
928 i++;
929 /* Tag the edge verts so we know which verts to rip */
932 }
933 totedge_manifold++;
934 }
935
937 if ((all_manifold == true) && (BM_edge_is_manifold(e) == false)) {
938 all_manifold = false;
939 }
940 }
941
942 /* single edge, extend */
943 if (i == 1 && e_best->l) {
944 /* NOTE: if the case of 3 edges has one change in loop stepping,
945 * if this becomes more involved we may be better off splitting
946 * the 3 edge case into its own else-if branch */
947 if (ELEM(totedge_manifold, 4, 3) || (all_manifold == false)) {
948 BMLoop *l_a = e_best->l;
949 BMLoop *l_b = l_a->radial_next;
950
951 /* find the best face to follow, this way the edge won't point away from
952 * the mouse when there are more than 4 (takes the shortest face fan around) */
953 l = (edbm_rip_edge_side_measure(e_best, l_a, region, projectMat, fmval) <
954 edbm_rip_edge_side_measure(e_best, l_b, region, projectMat, fmval)) ?
955 l_a :
956 l_b;
957
959 /* Important edge is manifold else we can be attempting to split off
960 * a fan that don't budge, not crashing but adds duplicate edge. */
961 if (BM_edge_is_manifold(l->e)) {
962 l = l->radial_next;
963
964 if (totedge_manifold != 3) {
966 }
967
968 if (l) {
971 }
972 }
973 }
974 else {
975 e = BM_vert_other_disk_edge(v, e_best);
976
977 if (e) {
980 }
981 }
982 }
983 }
984
985 /* keep directly before edgesplit */
986 if (do_fill) {
987 fill_uloop_pairs = edbm_tagged_loop_pairs_to_fill(bm);
988 }
989
990 BM_mesh_edgesplit(em->bm, true, true, true);
991
992 /* NOTE: the output of the bmesh operator is ignored, since we built
993 * the contiguous loop pairs to split already, its possible that some
994 * edge did not split even though it was tagged which would not work
995 * as expected (but not crash), however there are checks to ensure
996 * tagged edges will split. So far its not been an issue. */
997 edbm_ripsel_deselect_helper(bm, eloop_pairs, region, projectMat, fmval);
998
999 /* deselect loose verts */
1001
1002 if (do_fill && fill_uloop_pairs) {
1003 edbm_tagged_loop_pairs_do_fill_faces(bm, fill_uloop_pairs);
1004 MEM_freeN(fill_uloop_pairs);
1005 }
1006
1007 if ((totvert_orig == bm->totvert) && (totedge_orig == bm->totedge)) {
1008 return OPERATOR_CANCELLED;
1009 }
1010
1012
1013 return OPERATOR_FINISHED;
1014}
1015
1018/* -------------------------------------------------------------------- */
1022/* based on mouse cursor position, it defines how is being ripped */
1023static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1024{
1025 const Scene *scene = CTX_data_scene(C);
1026 ViewLayer *view_layer = CTX_data_view_layer(C);
1028 scene, view_layer, CTX_wm_view3d(C));
1029 const bool do_fill = RNA_boolean_get(op->ptr, "use_fill");
1030
1031 bool no_vertex_selected = true;
1032 bool error_face_selected = true;
1033 bool error_disconnected_vertices = true;
1034 bool error_rip_failed = true;
1035
1036 for (Object *obedit : objects) {
1038
1039 BMesh *bm = em->bm;
1040 BMIter iter;
1041 BMEdge *e;
1042 const bool singlesel = (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0);
1043 int ret;
1044
1045 if (em->bm->totvertsel == 0) {
1046 continue;
1047 }
1048 no_vertex_selected = false;
1049
1050 /* running in face mode hardly makes sense, so convert to region loop and rip */
1051 if (bm->totfacesel) {
1052 /* highly nifty but hard to support since the operator can fail and we're left
1053 * with modified selection */
1054 // WM_operator_name_call(C, "MESH_OT_region_to_loop", WM_OP_INVOKE_DEFAULT, nullptr, event);
1055 continue;
1056 }
1057 error_face_selected = false;
1058
1059 /* we could support this, but not for now */
1060 if ((bm->totvertsel > 1) && (bm->totedgesel == 0)) {
1061 continue;
1062 }
1063 error_disconnected_vertices = false;
1064
1065 /* note on selection:
1066 * When calling edge split we operate on tagged edges rather than selected
1067 * this is important because the edges to operate on are extended by one,
1068 * but the selection is left alone.
1069 *
1070 * After calling edge split - the duplicated edges have the same selection state as the
1071 * original, so all we do is de-select the far side from the mouse and we have a
1072 * useful selection for grabbing.
1073 */
1074
1076
1077 /* BM_ELEM_SELECT --> BM_ELEM_TAG */
1078 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
1080 }
1081
1082 /* split 2 main parts of this operator out into vertex and edge ripping */
1083 if (singlesel) {
1084 ret = edbm_rip_invoke__vert(C, event, obedit, do_fill);
1085 }
1086 else {
1087 ret = edbm_rip_invoke__edge(C, event, obedit, do_fill);
1088 }
1089
1090 if (ret != OPERATOR_FINISHED) {
1091 continue;
1092 }
1093
1095
1096 BLI_assert(singlesel ? (bm->totvertsel > 0) : (bm->totedgesel > 0));
1097
1098 if (bm->totvertsel == 0) {
1099 continue;
1100 }
1101 error_rip_failed = false;
1102
1104 params.calc_looptris = true;
1105 params.calc_normals = true;
1106 params.is_destructive = true;
1107 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1108 }
1109
1110 if (no_vertex_selected) {
1111 /* Ignore it. */
1112 return OPERATOR_CANCELLED;
1113 }
1114 if (error_face_selected) {
1115 BKE_report(op->reports, RPT_ERROR, "Cannot rip selected faces");
1116 return OPERATOR_CANCELLED;
1117 }
1118 if (error_disconnected_vertices) {
1119 BKE_report(op->reports, RPT_ERROR, "Cannot rip multiple disconnected vertices");
1120 return OPERATOR_CANCELLED;
1121 }
1122 if (error_rip_failed) {
1123 BKE_report(op->reports, RPT_ERROR, "Rip failed");
1124 return OPERATOR_CANCELLED;
1125 }
1126 /* No errors, everything went fine. */
1127 return OPERATOR_FINISHED;
1128}
1129
1131{
1132 PropertyRNA *prop;
1133
1134 /* identifiers */
1135 ot->name = "Rip";
1136 ot->idname = "MESH_OT_rip";
1137 ot->description = "Disconnect vertex or edges from connected geometry";
1138
1139 /* api callbacks */
1142
1143 /* flags */
1145
1146 /* to give to transform */
1148 prop = RNA_def_boolean(ot->srna, "use_fill", false, "Fill", "Fill the ripped region");
1150}
1151
Scene * CTX_data_scene(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
float dist_to_line_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:284
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:289
MINLINE float normalize_v2_length(float n[2], float unit_length)
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
Definition math_vector.c:21
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
unsigned int uint
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define BLT_I18NCONTEXT_ID_MESH
Object is a sort of wrapper for general info.
@ SCE_SELECT_EDGE
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
#define P_PROPORTIONAL
void Transform_Properties(wmOperatorType *ot, int flags)
#define P_MIRROR_DUMMY
blender::float2 ED_view3d_project_float_v2_m4(const ARegion *region, const float co[3], const blender::float4x4 &mat)
blender::float4x4 ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, const Object *ob)
Read Guarded memory(de)allocation.
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
@ BM_LOOP
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
BMFace * BM_face_create_verts(BMesh *bm, BMVert **vert_arr, const int len, const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges)
void bmesh_kernel_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, const bool copy_select)
Separate Vert.
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
@ BM_CREATE_NOP
Definition bmesh_core.hh:24
void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, const bool copy_select)
#define BM_elem_index_get(ele)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
int BM_iter_as_array(BMesh *bm, const char itype, void *data, void **array, const int len)
Iterator as Array.
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_EDGES_OF_VERT
@ BM_LOOPS_OF_FACE
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_select_mode_clean_ex(BMesh *bm, const short selectmode)
Select Mode Clean.
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_select_history_validate(BMesh *bm)
bool BM_select_history_active_get(BMesh *bm, BMEditSelection *ese)
#define BM_select_history_store(bm, ele)
#define BM_select_history_remove(bm, ele)
bool BM_custom_loop_normals_to_vector_layer(BMesh *bm)
void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges)
BMVert * BM_face_loop_separate_multi(BMesh *bm, BMLoop **larr, int larr_len)
BMVert * BM_face_loop_separate_multi_isolated(BMesh *bm, BMLoop *l_sep)
#define BM_VERT
BMLoop * BM_loop_other_edge_loop(BMLoop *l, BMVert *v)
BMVert * BM_edge_share_vert(BMEdge *e1, BMEdge *e2)
bool BM_vert_is_wire(const BMVert *v)
bool BM_vert_is_manifold_region(const BMVert *v)
void BM_loop_calc_face_tangent(const BMLoop *l, float r_tangent[3])
BM_loop_calc_face_tangent.
bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb)
BMFace * BM_face_exists(BMVert *const *varr, int len)
BMEdge * BM_vert_other_disk_edge(BMVert *v, BMEdge *e_first)
BMLoop * BM_edge_vert_share_loop(BMLoop *l, BMVert *v)
Return the Loop Shared by Edge and Vert.
BMLoop * BM_vert_find_first_loop(BMVert *v)
BMLoop * BM_edge_other_loop(BMEdge *e, BMLoop *l)
BMLoop * BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v)
Other Loop in Face Sharing a Vertex.
float BM_edge_calc_length(const BMEdge *e)
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define BM_vert_edge_count_is_equal(v, n)
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_in_loop(const BMEdge *e, const BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define BM_vert_face_count_is_equal(v, n)
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
ATTR_WARN_UNUSED_RESULT const BMVert * v
void append(const T &value)
#define printf
static BMVert * edbm_ripsel_edloop_pair_start_vert(BMEdge *e)
#define INSET_DEFAULT
static int edbm_rip_invoke__edge(bContext *C, const wmEvent *event, Object *obedit, bool do_fill)
static int edbm_rip_invoke__vert(bContext *C, const wmEvent *event, Object *obedit, bool do_fill)
static void edbm_tagged_loop_pairs_do_fill_faces(BMesh *bm, UnorderedLoopPair *uloop_pairs)
static float edbm_rip_edge_side_measure(BMEdge *e, BMLoop *e_l, ARegion *region, const blender::float4x4 &projectMat, const float fmval[2])
static Vector< EdgeLoopPair > edbm_ripsel_looptag_helper(BMesh *bm)
#define IS_VISIT_POSSIBLE(e)
#define INVALID_UID
static void edbm_calc_loop_co(BMLoop *l, float l_mid_co[3])
static BMEdge * edbm_ripsel_edge_mark_step(BMVert *v, const int uid)
static UnorderedLoopPair * edbm_tagged_loop_pairs_to_fill(BMesh *bm)
static void edbm_ripsel_deselect_helper(BMesh *bm, const Span< EdgeLoopPair > eloop_pairs, ARegion *region, const blender::float4x4 &projectMat, const float fmval[2])
static BMEdge * edbm_ripsel_edge_uid_step(BMEdge *e_orig, BMVert **v_prev)
void MESH_OT_rip(wmOperatorType *ot)
#define IS_VISIT_DONE(e)
static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static float edbm_rip_edgedist_squared(ARegion *region, const blender::float4x4 &mat, const float co1[3], const float co2[3], const float mvalf[2], const float inset)
@ ULP_FLIP_0
@ ULP_FLIP_1
bool EDBM_view3d_poll(bContext *C)
draw_view in_light_buf[] float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
size_t(* MEM_allocN_len)(const void *vmemh)
Definition mallocn.cc:36
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
T midpoint(const T &a, const T &b)
return ret
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
#define FLT_MAX
Definition stdcycles.h:14
BMVert * v1
BMVert * v2
struct BMLoop * l
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
int totvert
int totfacesel
char elem_index_dirty
int totedge
int totvertsel
int totedgesel
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
wmOperatorType * ot
Definition wm_files.cc:4125