Blender  V3.3
editmesh_rip.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2004 Blender Foundation. All rights reserved. */
3 
8 #include "MEM_guardedalloc.h"
9 
10 #include "DNA_object_types.h"
11 
12 #include "BLI_array.h"
13 #include "BLI_math.h"
14 
15 #include "BKE_context.h"
16 #include "BKE_editmesh.h"
17 #include "BKE_layer.h"
18 #include "BKE_report.h"
19 
20 #include "RNA_access.h"
21 #include "RNA_define.h"
22 
23 #include "WM_types.h"
24 
25 #include "ED_mesh.h"
26 #include "ED_screen.h"
27 #include "ED_transform.h"
28 #include "ED_view3d.h"
29 
30 #include "bmesh.h"
31 #include "bmesh_tools.h"
32 
33 #include "mesh_intern.h" /* own include */
34 
42 #define INSET_DEFAULT 0.00001f
43 static float edbm_rip_edgedist_squared(ARegion *region,
44  float mat[4][4],
45  const float co1[3],
46  const float co2[3],
47  const float mvalf[2],
48  const float inset)
49 {
50  float vec1[2], vec2[2], dist_sq;
51 
52  ED_view3d_project_float_v2_m4(region, co1, vec1, mat);
53  ED_view3d_project_float_v2_m4(region, co2, vec2, mat);
54 
55  if (inset != 0.0f) {
56  const float dist_2d = len_v2v2(vec1, vec2);
57  if (dist_2d > FLT_EPSILON) {
58  const float dist = inset / dist_2d;
59  BLI_assert(isfinite(dist));
60  interp_v2_v2v2(vec1, vec1, vec2, dist);
61  interp_v2_v2v2(vec2, vec2, vec1, dist);
62  }
63  }
64 
65  dist_sq = dist_squared_to_line_segment_v2(mvalf, vec1, vec2);
66  BLI_assert(isfinite(dist_sq));
67 
68  return dist_sq;
69 }
70 
71 #if 0
72 static float edbm_rip_linedist(
73  ARegion *region, float mat[4][4], const float co1[3], const float co2[3], const float mvalf[2])
74 {
75  float vec1[2], vec2[2];
76 
77  ED_view3d_project_float_v2_m4(region, co1, vec1, mat);
78  ED_view3d_project_float_v2_m4(region, co2, vec2, mat);
79 
80  return dist_to_line_v2(mvalf, vec1, vec2);
81 }
82 #endif
83 
87 static void edbm_calc_loop_co(BMLoop *l, float l_mid_co[3])
88 {
89  BM_loop_calc_face_tangent(l, l_mid_co);
90 
91  /* scale to average of surrounding edge size, only needs to be approx, but should
92  * be roughly equivalent to the check below which uses the middle of the edge. */
93  mul_v3_fl(l_mid_co, (BM_edge_calc_length(l->e) + BM_edge_calc_length(l->prev->e)) / 2.0f);
94 
95  add_v3_v3(l_mid_co, l->v->co);
96 }
97 
99  BMEdge *e, BMLoop *e_l, ARegion *region, float projectMat[4][4], const float fmval[2])
100 {
101  float cent[3] = {0, 0, 0}, mid[3];
102 
103  float vec[2];
104  float fmval_tweak[2];
105  float e_v1_co[2], e_v2_co[2];
106  float score;
107 
108  BMVert *v1_other;
109  BMVert *v2_other;
110 
111  BLI_assert(BM_vert_in_edge(e, e_l->v));
112 
113  /* method for calculating distance:
114  *
115  * for each edge: calculate face center, then made a vector
116  * from edge midpoint to face center. offset edge midpoint
117  * by a small amount along this vector. */
118 
119  /* rather than the face center, get the middle of
120  * both edge verts connected to this one */
121  v1_other = BM_face_other_vert_loop(e_l->f, e->v2, e->v1)->v;
122  v2_other = BM_face_other_vert_loop(e_l->f, e->v1, e->v2)->v;
123  mid_v3_v3v3(cent, v1_other->co, v2_other->co);
124  mid_v3_v3v3(mid, e->v1->co, e->v2->co);
125 
126  ED_view3d_project_float_v2_m4(region, cent, cent, projectMat);
127  ED_view3d_project_float_v2_m4(region, mid, mid, projectMat);
128 
129  ED_view3d_project_float_v2_m4(region, e->v1->co, e_v1_co, projectMat);
130  ED_view3d_project_float_v2_m4(region, e->v2->co, e_v2_co, projectMat);
131 
132  sub_v2_v2v2(vec, cent, mid);
133  normalize_v2_length(vec, 0.01f);
134 
135  /* rather than adding to both verts, subtract from the mouse */
136  sub_v2_v2v2(fmval_tweak, fmval, vec);
137 
138  score = len_v2v2(e_v1_co, e_v2_co);
139 
140  if (dist_squared_to_line_segment_v2(fmval_tweak, e_v1_co, e_v2_co) >
141  dist_squared_to_line_segment_v2(fmval, e_v1_co, e_v2_co)) {
142  return score;
143  }
144  return -score;
145 }
146 
147 /* - Advanced selection handling 'ripsel' functions ----- */
148 
181 #define IS_VISIT_POSSIBLE(e) (BM_edge_is_manifold(e) && BM_elem_flag_test(e, BM_ELEM_TAG))
182 #define IS_VISIT_DONE(e) ((e)->l && (BM_elem_index_get((e)->l) != INVALID_UID))
183 #define INVALID_UID INT_MIN
184 
185 /* mark, assign uid and step */
186 static BMEdge *edbm_ripsel_edge_mark_step(BMVert *v, const int uid)
187 {
188  BMIter iter;
189  BMEdge *e;
190  BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
191  if (IS_VISIT_POSSIBLE(e) && !IS_VISIT_DONE(e)) {
192  BMLoop *l_a, *l_b;
193 
194  BM_edge_loop_pair(e, &l_a, &l_b); /* no need to check, we know this will be true */
195 
196  /* so (IS_VISIT_DONE == true) */
197  BM_elem_index_set(l_a, uid); /* set_dirty */
198  BM_elem_index_set(l_b, uid); /* set_dirty */
199 
200  return e;
201  }
202  }
203  return NULL;
204 }
205 
206 typedef struct EdgeLoopPair {
210 
212 {
213  BMIter fiter;
214  BMIter liter;
215 
216  BMFace *f;
217  BMLoop *l;
218 
219  int uid_start;
220  int uid_end;
221  int uid = bm->totedge; /* can start anywhere */
222 
223  EdgeLoopPair *eloop_pairs = NULL;
224  BLI_array_declare(eloop_pairs);
225  EdgeLoopPair *lp;
226 
227  /* initialize loops with dummy invalid index values */
228  BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
229  BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
230  BM_elem_index_set(l, INVALID_UID); /* set_dirty */
231  }
232  }
234 
235  /* set contiguous loops ordered 'uid' values for walking after split */
236  while (true) {
237  int tot = 0;
238  BMIter eiter;
239  BMEdge *e_step;
240  BMVert *v_step;
241  BMEdge *e;
242  BMEdge *e_first;
243  BMEdge *e_last;
244 
245  e_first = NULL;
246  BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
247  if (IS_VISIT_POSSIBLE(e) && !IS_VISIT_DONE(e)) {
248  e_first = e;
249  break;
250  }
251  }
252 
253  if (e_first == NULL) {
254  break;
255  }
256 
257  /* Initialize. */
258  e_first = e;
259  v_step = e_first->v1;
260  e_step = NULL; /* quiet warning, will never remain this value */
261 
262  uid_start = uid;
263  while ((e = edbm_ripsel_edge_mark_step(v_step, uid))) {
264  v_step = BM_edge_other_vert((e_step = e), v_step);
265  uid++; /* only different line */
266  tot++;
267  }
268 
269  /* this edges loops have the highest uid's, store this to walk down later */
270  e_last = e_step;
271 
272  /* always store the highest 'uid' edge for the stride */
273  uid_end = uid - 1;
274  uid = uid_start - 1;
275 
276  /* initialize */
277  v_step = e_first->v1;
278 
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  /* stride far enough not to _ever_ overlap range */
286  uid_start = uid;
287  uid = uid_end + bm->totedge;
288 
289  lp = BLI_array_append_ret(eloop_pairs);
290  /* no need to check, we know this will be true */
291  BM_edge_loop_pair(e_last, &lp->l_a, &lp->l_b);
292 
293  BLI_assert(tot == uid_end - uid_start);
294 
295 #if 0
296  printf("%s: found contiguous edge loop of (%d)\n", __func__, uid_end - uid_start);
297 #endif
298  }
299 
300  /* null terminate */
301  lp = BLI_array_append_ret(eloop_pairs);
302  lp->l_a = lp->l_b = NULL;
303 
304  return eloop_pairs;
305 }
306 
307 /* - De-Select the worst rip-edge side -------------------------------- */
308 
309 static BMEdge *edbm_ripsel_edge_uid_step(BMEdge *e_orig, BMVert **v_prev)
310 {
311  BMIter eiter;
312  BMEdge *e;
313  BMVert *v = BM_edge_other_vert(e_orig, *v_prev);
314  const int uid_cmp = BM_elem_index_get(e_orig->l) - 1;
315 
316  BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
317  if (BM_elem_index_get(e->l) == uid_cmp) {
318  *v_prev = v;
319  return e;
320  }
321  }
322  return NULL;
323 }
324 
326 {
327  /* try step in a direction, if it fails we know do go the other way */
328  BMVert *v_test = e->v1;
329  return (edbm_ripsel_edge_uid_step(e, &v_test)) ? e->v1 : e->v2;
330 }
331 
333  EdgeLoopPair *eloop_pairs,
334  ARegion *region,
335  float projectMat[4][4],
336  const float fmval[2])
337 {
338  EdgeLoopPair *lp;
339 
340  for (lp = eloop_pairs; lp->l_a; lp++) {
341  BMEdge *e;
342  BMVert *v_prev;
343 
344  float score_a = 0.0f;
345  float score_b = 0.0f;
346 
347  e = lp->l_a->e;
349  for (; e; e = edbm_ripsel_edge_uid_step(e, &v_prev)) {
350  score_a += edbm_rip_edge_side_measure(e, e->l, region, projectMat, fmval);
351  }
352  e = lp->l_b->e;
354  for (; e; e = edbm_ripsel_edge_uid_step(e, &v_prev)) {
355  score_b += edbm_rip_edge_side_measure(e, e->l, region, projectMat, fmval);
356  }
357 
358  e = (score_a > score_b) ? lp->l_a->e : lp->l_b->e;
360  for (; e; e = edbm_ripsel_edge_uid_step(e, &v_prev)) {
361  BM_edge_select_set(bm, e, false);
362  }
363  }
364 }
365 /* --- end 'ripsel' selection handling code --- */
366 
367 /* --- face-fill code --- */
378 typedef struct UnorderedLoopPair {
380  char flag;
382 enum {
383  ULP_FLIP_0 = (1 << 0),
384  ULP_FLIP_1 = (1 << 1),
385 };
386 
388 {
389  BMIter iter;
390  BMEdge *e;
391 
392  uint total_tag = 0;
393  /* count tags, could be pre-calculated */
394  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
396  total_tag++;
397  }
398  }
399 
400  if (total_tag) {
401  UnorderedLoopPair *uloop_pairs = MEM_mallocN(total_tag * sizeof(UnorderedLoopPair), __func__);
402  UnorderedLoopPair *ulp = uloop_pairs;
403 
404  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
406  BMLoop *l1, *l2;
407  if (BM_edge_loop_pair(e, &l1, &l2)) {
408  BMVert *v_cmp = l1->e->v1;
409  ulp->flag = (((l1->v != v_cmp) ? ULP_FLIP_0 : 0) | ((l2->v == v_cmp) ? ULP_FLIP_1 : 0));
410  }
411  else {
412  ulp->flag = 0;
413  }
414  ulp->l_pair[0] = l1;
415  ulp->l_pair[1] = l2;
416 
417  ulp++;
418  }
419  }
420 
421  return uloop_pairs;
422  }
423  return NULL;
424 }
425 
427 {
428  UnorderedLoopPair *ulp;
429  uint total_tag = MEM_allocN_len(uloop_pairs) / sizeof(UnorderedLoopPair);
430  uint i;
431 
432  for (i = 0, ulp = uloop_pairs; i < total_tag; i++, ulp++) {
433  if ((ulp->l_pair[0] && ulp->l_pair[1]) && (ulp->l_pair[0]->e != ulp->l_pair[1]->e)) {
434  /* time has come to make a face! */
435  BMVert *v_shared = BM_edge_share_vert(ulp->l_pair[0]->e, ulp->l_pair[1]->e);
436  BMFace *f, *f_example = ulp->l_pair[0]->f;
437  BMLoop *l_iter;
438  BMVert *f_verts[4];
439 
440  if (v_shared == NULL) {
441  /* quad */
442  f_verts[0] = ulp->l_pair[0]->e->v1;
443  f_verts[1] = ulp->l_pair[1]->e->v1;
444  f_verts[2] = ulp->l_pair[1]->e->v2;
445  f_verts[3] = ulp->l_pair[0]->e->v2;
446 
447  if (ulp->flag & ULP_FLIP_0) {
448  SWAP(BMVert *, f_verts[0], f_verts[3]);
449  }
450  if (ulp->flag & ULP_FLIP_1) {
451  SWAP(BMVert *, f_verts[1], f_verts[2]);
452  }
453  }
454  else {
455  /* tri */
456  f_verts[0] = v_shared;
457  f_verts[1] = BM_edge_other_vert(ulp->l_pair[0]->e, v_shared);
458  f_verts[2] = BM_edge_other_vert(ulp->l_pair[1]->e, v_shared);
459  f_verts[3] = NULL;
460 
461  /* don't use the flip flags */
462  if (v_shared == ulp->l_pair[0]->v) {
463  SWAP(BMVert *, f_verts[0], f_verts[1]);
464  }
465  }
466 
467  /* face should never exist */
468  BLI_assert(!BM_face_exists(f_verts, f_verts[3] ? 4 : 3));
469 
470  f = BM_face_create_verts(bm, f_verts, f_verts[3] ? 4 : 3, f_example, BM_CREATE_NOP, true);
471 
472  l_iter = BM_FACE_FIRST_LOOP(f);
473 
474  if (f_verts[3]) {
475  BM_elem_attrs_copy(bm, bm, BM_edge_other_loop(ulp->l_pair[0]->e, l_iter), l_iter);
476  l_iter = l_iter->next;
477  BM_elem_attrs_copy(bm, bm, BM_edge_other_loop(ulp->l_pair[1]->e, l_iter), l_iter);
478  l_iter = l_iter->next;
479  BM_elem_attrs_copy(bm, bm, BM_edge_other_loop(ulp->l_pair[1]->e, l_iter), l_iter);
480  l_iter = l_iter->next;
481  BM_elem_attrs_copy(bm, bm, BM_edge_other_loop(ulp->l_pair[0]->e, l_iter), l_iter);
482  }
483  else {
484  BM_elem_attrs_copy(bm, bm, BM_edge_other_loop(ulp->l_pair[0]->e, l_iter), l_iter);
485  l_iter = l_iter->next;
486  BM_elem_attrs_copy(bm, bm, BM_edge_other_loop(ulp->l_pair[0]->e, l_iter), l_iter);
487  l_iter = l_iter->next;
488  BM_elem_attrs_copy(bm, bm, BM_edge_other_loop(ulp->l_pair[1]->e, l_iter), l_iter);
489  }
490  }
491  }
492 }
493 
494 /* --- end 'face-fill' code --- */
495 
499 static int edbm_rip_invoke__vert(bContext *C, const wmEvent *event, Object *obedit, bool do_fill)
500 {
501  UnorderedLoopPair *fill_uloop_pairs = NULL;
502  ARegion *region = CTX_wm_region(C);
504  BMEditMesh *em = BKE_editmesh_from_object(obedit);
505  BMesh *bm = em->bm;
506  BMIter iter, liter;
507  BMLoop *l;
508  BMEdge *e_best;
509  BMVert *v;
510  const int totvert_orig = bm->totvert;
511  int i;
512  float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]};
513  float dist_sq = FLT_MAX;
514  float d;
515  bool is_wire, is_manifold_region;
516 
517  BMEditSelection ese;
518  int totboundary_edge = 0;
519 
520  ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat);
521 
522  /* find selected vert - same some time and check history first */
523  if (BM_select_history_active_get(bm, &ese) && ese.htype == BM_VERT) {
524  v = (BMVert *)ese.ele;
525  }
526  else {
527  ese.ele = NULL;
528 
529  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
531  break;
532  }
533  }
534  }
535 
536  /* (v == NULL) should be impossible */
537  if ((v == NULL) || (v->e == NULL)) {
538  return OPERATOR_CANCELLED;
539  }
540 
541  is_wire = BM_vert_is_wire(v);
542  is_manifold_region = BM_vert_is_manifold_region(v);
543 
544  e_best = NULL;
545 
546  {
547  BMEdge *e;
548  /* find closest edge to mouse cursor */
549  BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
550  /* consider wire as boundary for this purpose,
551  * otherwise we can't a face away from a wire edge */
552  totboundary_edge += (BM_edge_is_boundary(e) || BM_edge_is_wire(e));
554  if ((is_manifold_region == false) || BM_edge_is_manifold(e)) {
556  region, projectMat, e->v1->co, e->v2->co, fmval, INSET_DEFAULT);
557  if ((e_best == NULL) || (d < dist_sq)) {
558  dist_sq = d;
559  e_best = e;
560  }
561  }
562  }
563  }
564  }
565 
566  if (e_best && e_best->l && (is_manifold_region == false)) {
567  /* Try to split off a non-manifold fan (when we have multiple disconnected fans) */
568  BMLoop *l_sep = e_best->l->v == v ? e_best->l : e_best->l->next;
569  BMVert *v_new;
570 
571  BLI_assert(l_sep->v == v);
574 
575  BM_vert_select_set(bm, v, false);
577 
578  BM_vert_select_set(bm, v_new, true);
579  if (ese.ele) {
580  BM_select_history_store(bm, v_new);
581  }
582 
583  if (do_fill) {
584  BM_edge_create(bm, v, v_new, NULL, BM_CREATE_NOP);
585  }
586 
587  return OPERATOR_FINISHED;
588  }
589 
590  /* if we are ripping a single vertex from 3 faces,
591  * then measure the distance to the face corner as well as the edge */
593  BMEdge *e_all[3];
594  BMLoop *l_all[3];
595  int i1, i2;
596 
597  BM_iter_as_array(bm, BM_EDGES_OF_VERT, v, (void **)e_all, 3);
598  BM_iter_as_array(bm, BM_LOOPS_OF_VERT, v, (void **)l_all, 3);
599 
600  /* not do a loop similar to the one above, but test against loops */
601  for (i1 = 0; i1 < 3; i1++) {
602  /* consider wire as boundary for this purpose,
603  * otherwise we can't a face away from a wire edge */
604  float l_mid_co[3];
605  l = l_all[i1];
606  edbm_calc_loop_co(l, l_mid_co);
607  d = edbm_rip_edgedist_squared(region, projectMat, l->v->co, l_mid_co, fmval, INSET_DEFAULT);
608  if ((e_best == NULL) || (d < dist_sq)) {
609  dist_sq = d;
610 
611  /* find the edge that is not in this loop */
612  e_best = NULL;
613  for (i2 = 0; i2 < 3; i2++) {
614  if (!BM_edge_in_loop(e_all[i2], l)) {
615  e_best = e_all[i2];
616  break;
617  }
618  }
619  BLI_assert(e_best != NULL);
620  }
621  }
622  }
623 
624  /* should we go ahead with edge rip or do we need to do special case, split off vertex?:
625  * split off vertex if...
626  * - we can't find an edge - this means we are ripping a faces vert that is connected to other
627  * geometry only at the vertex.
628  * - the boundary edge total is greater than 2,
629  * in this case edge split _can_ work but we get far nicer results if we use this special case.
630  * - there are only 2 edges but we are a wire vert. */
631  if ((is_wire == false && totboundary_edge > 2) || (is_wire == true && totboundary_edge > 1)) {
632  BMVert **vout;
633  int vout_len;
634 
635  BM_vert_select_set(bm, v, false);
636 
637  bmesh_kernel_vert_separate(bm, v, &vout, &vout_len, true);
638 
639  if (vout_len < 2) {
640  MEM_freeN(vout);
641  /* set selection back to avoid active-unselected vertex */
642  BM_vert_select_set(bm, v, true);
643  /* should never happen */
644  return OPERATOR_CANCELLED;
645  }
646 
647  int vi_best = 0;
648 
649  if (ese.ele) {
651  }
652 
653  dist_sq = FLT_MAX;
654 
655  /* in the loop below we find the best vertex to drag based on its connected geometry,
656  * either by its face corner, or connected edge (when no faces are attached) */
657  for (i = 0; i < vout_len; i++) {
658 
659  if (BM_vert_is_wire(vout[i]) == false) {
660  /* find the best face corner */
661  BM_ITER_ELEM (l, &iter, vout[i], BM_LOOPS_OF_VERT) {
663  float l_mid_co[3];
664 
665  edbm_calc_loop_co(l, l_mid_co);
667  region, projectMat, v->co, l_mid_co, fmval, INSET_DEFAULT);
668 
669  if (d < dist_sq) {
670  dist_sq = d;
671  vi_best = i;
672  }
673  }
674  }
675  }
676  else {
677  BMEdge *e;
678  /* a wire vert, find the best edge */
679  BM_ITER_ELEM (e, &iter, vout[i], BM_EDGES_OF_VERT) {
681  float e_mid_co[3];
682 
683  mid_v3_v3v3(e_mid_co, e->v1->co, e->v2->co);
685  region, projectMat, v->co, e_mid_co, fmval, INSET_DEFAULT);
686 
687  if (d < dist_sq) {
688  dist_sq = d;
689  vi_best = i;
690  }
691  }
692  }
693  }
694  }
695 
696  /* vout[0] == best
697  * vout[1] == glue
698  * vout[2+] == splice with glue (when vout_len > 2)
699  */
700  if (vi_best != 0) {
701  SWAP(BMVert *, vout[0], vout[vi_best]);
702  vi_best = 0;
703  }
704 
705  /* select the vert from the best region */
706  v = vout[vi_best];
707  BM_vert_select_set(bm, v, true);
708 
709  if (ese.ele) {
711  }
712 
713  /* splice all others back together */
714  if (vout_len > 2) {
715  for (i = 2; i < vout_len; i++) {
716  BM_vert_splice(bm, vout[1], vout[i]);
717  }
718  }
719 
720  if (do_fill) {
721  /* match extrude vert-order */
722  BM_edge_create(bm, vout[1], vout[0], NULL, BM_CREATE_NOP);
723  }
724 
725  MEM_freeN(vout);
726 
727  return OPERATOR_FINISHED;
728  }
729 
730  if (!e_best) {
731  return OPERATOR_CANCELLED;
732  }
733 
734  /* *** Execute the split! *** */
735  /* unlike edge split, for single vertex split we only use the operator in one of the cases
736  * but both allocate fill */
737 
738  {
739  BMVert *v_rip;
740  BMLoop *larr[2];
741  int larr_len = 0;
742 
743  /* rip two adjacent edges */
745  /* Don't run the edge split operator in this case */
746 
747  l = BM_edge_vert_share_loop(e_best->l, v);
748  larr[larr_len] = l;
749  larr_len++;
750 
751  /* only tag for face-fill (we don't call the operator) */
752  if (BM_edge_is_boundary(e_best)) {
754  }
755  else {
758  }
759  }
760  else {
761  if (BM_edge_is_manifold(e_best)) {
762  BMLoop *l_iter, *l_first;
763  l_iter = l_first = e_best->l;
764  do {
765  larr[larr_len] = BM_edge_vert_share_loop(l_iter, v);
766 
767  if (do_fill) {
768  /* Only needed when filling...
769  * Also, we never want to tag best edge,
770  * that one won't change during split. See T44618. */
771  if (larr[larr_len]->e == e_best) {
772  BM_elem_flag_enable(larr[larr_len]->prev->e, BM_ELEM_TAG);
773  }
774  else {
775  BM_elem_flag_enable(larr[larr_len]->e, BM_ELEM_TAG);
776  }
777  }
778  larr_len++;
779  } while ((l_iter = l_iter->radial_next) != l_first);
780  }
781  else {
782  /* looks like there are no split edges, we could just return/report-error? - Campbell */
783  }
784  }
785 
786  /* keep directly before edgesplit */
787  if (do_fill) {
788  fill_uloop_pairs = edbm_tagged_loop_pairs_to_fill(bm);
789  }
790 
791  if (larr_len) {
792  v_rip = BM_face_loop_separate_multi(bm, larr, larr_len);
793  }
794  else {
795  v_rip = NULL;
796  }
797 
798  if (v_rip) {
799  BM_vert_select_set(bm, v_rip, true);
800  }
801  else {
802  if (fill_uloop_pairs) {
803  MEM_freeN(fill_uloop_pairs);
804  }
805  return OPERATOR_CANCELLED;
806  }
807  }
808 
809  {
810  /* --- select which vert --- */
811  BMVert *v_best = NULL;
812  float l_corner_co[3];
813 
814  dist_sq = FLT_MAX;
815  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
817  /* disable by default, re-enable winner at end */
818  BM_vert_select_set(bm, v, false);
820 
821  BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
822 
823  /* check if v_best is null in the _rare_ case there are numeric issues */
824  edbm_calc_loop_co(l, l_corner_co);
826  region, projectMat, l->v->co, l_corner_co, fmval, INSET_DEFAULT);
827  if ((v_best == NULL) || (d < dist_sq)) {
828  v_best = v;
829  dist_sq = d;
830  }
831  }
832  }
833  }
834 
835  if (v_best) {
836  BM_vert_select_set(bm, v_best, true);
837  if (ese.ele) {
838  BM_select_history_store(bm, v_best);
839  }
840  }
841  }
842 
843  if (do_fill && fill_uloop_pairs) {
844  edbm_tagged_loop_pairs_do_fill_faces(bm, fill_uloop_pairs);
845  MEM_freeN(fill_uloop_pairs);
846  }
847 
848  if (totvert_orig == bm->totvert) {
849  return OPERATOR_CANCELLED;
850  }
851 
852  return OPERATOR_FINISHED;
853 }
854 
858 static int edbm_rip_invoke__edge(bContext *C, const wmEvent *event, Object *obedit, bool do_fill)
859 {
860  UnorderedLoopPair *fill_uloop_pairs = NULL;
861  ARegion *region = CTX_wm_region(C);
863  BMEditMesh *em = BKE_editmesh_from_object(obedit);
864  BMesh *bm = em->bm;
865  BMIter iter, eiter;
866  BMLoop *l;
867  BMEdge *e_best;
868  BMVert *v;
869  const int totvert_orig = bm->totvert;
870  const int totedge_orig = bm->totedge;
871  float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]};
872 
873  EdgeLoopPair *eloop_pairs;
874 
875  ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat);
876 
877  /* important this runs on the original selection, before tampering with tagging */
878  eloop_pairs = edbm_ripsel_looptag_helper(bm);
879 
880  /* expand edge selection */
881  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
882  BMEdge *e;
883  bool all_manifold;
884  int totedge_manifold; /* manifold, visible edges */
885  int i;
886 
887  e_best = NULL;
888  i = 0;
889  totedge_manifold = 0;
890  all_manifold = true;
891  BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
892 
894  /* important to check selection rather than tag here
895  * else we get feedback loop */
897  e_best = e;
898  i++;
899  /* Tag the edge verts so we know which verts to rip */
902  }
903  totedge_manifold++;
904  }
905 
907  if ((all_manifold == true) && (BM_edge_is_manifold(e) == false)) {
908  all_manifold = false;
909  }
910  }
911 
912  /* single edge, extend */
913  if (i == 1 && e_best->l) {
914  /* NOTE: if the case of 3 edges has one change in loop stepping,
915  * if this becomes more involved we may be better off splitting
916  * the 3 edge case into its own else-if branch */
917  if ((ELEM(totedge_manifold, 4, 3)) || (all_manifold == false)) {
918  BMLoop *l_a = e_best->l;
919  BMLoop *l_b = l_a->radial_next;
920 
921  /* find the best face to follow, this way the edge won't point away from
922  * the mouse when there are more than 4 (takes the shortest face fan around) */
923  l = (edbm_rip_edge_side_measure(e_best, l_a, region, projectMat, fmval) <
924  edbm_rip_edge_side_measure(e_best, l_b, region, projectMat, fmval)) ?
925  l_a :
926  l_b;
927 
929  /* Important edge is manifold else we can be attempting to split off
930  * a fan that don't budge, not crashing but adds duplicate edge. */
931  if (BM_edge_is_manifold(l->e)) {
932  l = l->radial_next;
933 
934  if (totedge_manifold != 3) {
936  }
937 
938  if (l) {
941  }
942  }
943  }
944  else {
945  e = BM_vert_other_disk_edge(v, e_best);
946 
947  if (e) {
950  }
951  }
952  }
953  }
954 
955  /* keep directly before edgesplit */
956  if (do_fill) {
957  fill_uloop_pairs = edbm_tagged_loop_pairs_to_fill(bm);
958  }
959 
960  BM_mesh_edgesplit(em->bm, true, true, true);
961 
962  /* NOTE: the output of the bmesh operator is ignored, since we built
963  * the contiguous loop pairs to split already, its possible that some
964  * edge did not split even though it was tagged which would not work
965  * as expected (but not crash), however there are checks to ensure
966  * tagged edges will split. So far its not been an issue. */
967  edbm_ripsel_deselect_helper(bm, eloop_pairs, region, projectMat, fmval);
968  MEM_freeN(eloop_pairs);
969 
970  /* deselect loose verts */
972 
973  if (do_fill && fill_uloop_pairs) {
974  edbm_tagged_loop_pairs_do_fill_faces(bm, fill_uloop_pairs);
975  MEM_freeN(fill_uloop_pairs);
976  }
977 
978  if ((totvert_orig == bm->totvert) && (totedge_orig == bm->totedge)) {
979  return OPERATOR_CANCELLED;
980  }
981 
983 
984  return OPERATOR_FINISHED;
985 }
986 
987 /* based on mouse cursor position, it defines how is being ripped */
988 static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
989 {
990  ViewLayer *view_layer = CTX_data_view_layer(C);
991  uint objects_len = 0;
993  view_layer, CTX_wm_view3d(C), &objects_len);
994  const bool do_fill = RNA_boolean_get(op->ptr, "use_fill");
995 
996  bool no_vertex_selected = true;
997  bool error_face_selected = true;
998  bool error_disconnected_vertices = true;
999  bool error_rip_failed = true;
1000 
1001  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1002  Object *obedit = objects[ob_index];
1003  BMEditMesh *em = BKE_editmesh_from_object(obedit);
1004 
1005  BMesh *bm = em->bm;
1006  BMIter iter;
1007  BMEdge *e;
1008  const bool singlesel = (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0);
1009  int ret;
1010 
1011  if (em->bm->totvertsel == 0) {
1012  continue;
1013  }
1014  no_vertex_selected = false;
1015 
1016  /* running in face mode hardly makes sense, so convert to region loop and rip */
1017  if (bm->totfacesel) {
1018  /* highly nifty but hard to support since the operator can fail and we're left
1019  * with modified selection */
1020  // WM_operator_name_call(C, "MESH_OT_region_to_loop", WM_OP_INVOKE_DEFAULT, NULL, event);
1021  continue;
1022  }
1023  error_face_selected = false;
1024 
1025  /* we could support this, but not for now */
1026  if ((bm->totvertsel > 1) && (bm->totedgesel == 0)) {
1027  continue;
1028  }
1029  error_disconnected_vertices = false;
1030 
1031  /* note on selection:
1032  * When calling edge split we operate on tagged edges rather than selected
1033  * this is important because the edges to operate on are extended by one,
1034  * but the selection is left alone.
1035  *
1036  * After calling edge split - the duplicated edges have the same selection state as the
1037  * original, so all we do is de-select the far side from the mouse and we have a
1038  * useful selection for grabbing.
1039  */
1040 
1042 
1043  /* BM_ELEM_SELECT --> BM_ELEM_TAG */
1044  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
1046  }
1047 
1048  /* split 2 main parts of this operator out into vertex and edge ripping */
1049  if (singlesel) {
1050  ret = edbm_rip_invoke__vert(C, event, obedit, do_fill);
1051  }
1052  else {
1053  ret = edbm_rip_invoke__edge(C, event, obedit, do_fill);
1054  }
1055 
1056  if (ret != OPERATOR_FINISHED) {
1057  continue;
1058  }
1059 
1061 
1062  BLI_assert(singlesel ? (bm->totvertsel > 0) : (bm->totedgesel > 0));
1063 
1064  if (bm->totvertsel == 0) {
1065  continue;
1066  }
1067  error_rip_failed = false;
1068 
1069  EDBM_update(obedit->data,
1070  &(const struct EDBMUpdate_Params){
1071  .calc_looptri = true,
1072  .calc_normals = true,
1073  .is_destructive = true,
1074  });
1075  }
1076 
1077  MEM_freeN(objects);
1078 
1079  if (no_vertex_selected) {
1080  /* Ignore it. */
1081  return OPERATOR_CANCELLED;
1082  }
1083  if (error_face_selected) {
1084  BKE_report(op->reports, RPT_ERROR, "Cannot rip selected faces");
1085  return OPERATOR_CANCELLED;
1086  }
1087  if (error_disconnected_vertices) {
1088  BKE_report(op->reports, RPT_ERROR, "Cannot rip multiple disconnected vertices");
1089  return OPERATOR_CANCELLED;
1090  }
1091  if (error_rip_failed) {
1092  BKE_report(op->reports, RPT_ERROR, "Rip failed");
1093  return OPERATOR_CANCELLED;
1094  }
1095  /* No errors, everything went fine. */
1096  return OPERATOR_FINISHED;
1097 }
1098 
1100 {
1101  /* identifiers */
1102  ot->name = "Rip";
1103  ot->idname = "MESH_OT_rip";
1104  ot->description = "Disconnect vertex or edges from connected geometry";
1105 
1106  /* api callbacks */
1109 
1110  /* flags */
1112 
1113  /* to give to transform */
1115  RNA_def_boolean(ot->srna, "use_fill", false, "Fill", "Fill the ripped region");
1116 }
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct View3D * CTX_wm_view3d(const bContext *C)
Definition: context.c:784
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct RegionView3D * CTX_wm_region_view3d(const bContext *C)
Definition: context.c:793
BMEditMesh * BKE_editmesh_from_object(struct Object *ob)
Return the BMEditMesh for a given object.
Definition: editmesh.c:58
#define BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, v3d, r_len)
Definition: BKE_layer.h:542
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
A (mainly) macro array library.
#define BLI_array_append_ret(arr)
Definition: BLI_array.h:111
#define BLI_array_declare(arr)
Definition: BLI_array.h:50
#define BLI_assert(a)
Definition: BLI_assert.h:46
float dist_to_line_v2(const float p[2], const float l1[2], const float l2[2])
Definition: math_geom.c:278
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition: math_geom.c:283
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
Definition: math_vector.c:14
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])
Definition: math_vector.c:237
MINLINE float len_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2_length(float r[2], float unit_scale)
MINLINE void add_v3_v3(float r[3], const float a[3])
unsigned int uint
Definition: BLI_sys_types.h:67
#define SWAP(type, a, b)
#define ELEM(...)
Object is a sort of wrapper for general info.
#define SCE_SELECT_EDGE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
void EDBM_update(struct Mesh *me, const struct EDBMUpdate_Params *params)
#define P_PROPORTIONAL
Definition: ED_transform.h:110
void Transform_Properties(struct wmOperatorType *ot, int flags)
#define P_MIRROR_DUMMY
Definition: ED_transform.h:109
void ED_view3d_ob_project_mat_get(const struct RegionView3D *v3d, const struct Object *ob, float r_pmat[4][4])
void ED_view3d_project_float_v2_m4(const struct ARegion *region, const float co[3], float r_co[2], const float mat[4][4])
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint i1
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:25
@ OPTYPE_DEPENDS_ON_CURSOR
Definition: WM_types.h:184
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define BM_FACE_FIRST_LOOP(p)
Definition: bmesh_class.h:622
@ BM_LOOP
Definition: bmesh_class.h:385
@ BM_VERT
Definition: bmesh_class.h:383
@ BM_ELEM_HIDDEN
Definition: bmesh_class.h:472
@ BM_ELEM_SELECT
Definition: bmesh_class.h:471
@ BM_ELEM_TAG
Definition: bmesh_class.h:484
void BM_elem_attrs_copy(BMesh *bm_src, BMesh *bm_dst, const void *ele_src, void *ele_dst)
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)
Definition: bmesh_core.c:464
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
Definition: bmesh_core.c:2046
void bmesh_kernel_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, const bool copy_select)
Separate Vert.
Definition: bmesh_core.c:2082
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.
Definition: bmesh_core.c:123
@ BM_CREATE_NOP
Definition: bmesh_core.h:12
void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, const bool copy_select)
#define BM_elem_index_get(ele)
Definition: bmesh_inline.h:110
#define BM_elem_flag_set(ele, hflag, val)
Definition: bmesh_inline.h:16
#define BM_elem_index_set(ele, index)
Definition: bmesh_inline.h:111
#define BM_elem_flag_test(ele, hflag)
Definition: bmesh_inline.h:12
#define BM_elem_flag_enable(ele, hflag)
Definition: bmesh_inline.h:14
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_isolated(BMesh *bm, BMLoop *l_sep)
Definition: bmesh_mods.c:901
BMVert * BM_face_loop_separate_multi(BMesh *bm, BMLoop **larr, int larr_len)
Definition: bmesh_mods.c:906
bool BM_vert_is_wire(const BMVert *v)
Definition: bmesh_query.c:688
BMLoop * BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v)
Other Loop in Face Sharing a Vertex.
Definition: bmesh_query.c:39
BMEdge * BM_vert_other_disk_edge(BMVert *v, BMEdge *e_first)
Definition: bmesh_query.c:482
BMLoop * BM_vert_find_first_loop(BMVert *v)
Definition: bmesh_query.c:297
bool BM_vert_is_manifold_region(const BMVert *v)
Definition: bmesh_query.c:847
void BM_loop_calc_face_tangent(const BMLoop *l, float r_tangent[3])
BM_loop_calc_face_tangent.
Definition: bmesh_query.c:1299
bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb)
Definition: bmesh_query.c:553
BMVert * BM_edge_share_vert(BMEdge *e1, BMEdge *e2)
Definition: bmesh_query.c:1079
BMFace * BM_face_exists(BMVert **varr, int len)
Definition: bmesh_query.c:1612
BMLoop * BM_edge_vert_share_loop(BMLoop *l, BMVert *v)
Return the Loop Shared by Edge and Vert.
Definition: bmesh_query.c:1091
float BM_edge_calc_length(const BMEdge *e)
Definition: bmesh_query.c:528
BMLoop * BM_edge_other_loop(BMEdge *e, BMLoop *l)
Definition: bmesh_query.c:436
BMLoop * BM_loop_other_edge_loop(BMLoop *l, BMVert *v)
Definition: bmesh_query.c:33
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)
Definition: bmesh_query.h:239
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)
Definition: bmesh_query.h:255
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
#define INSET_DEFAULT
Definition: editmesh_rip.c:42
static int edbm_rip_invoke__edge(bContext *C, const wmEvent *event, Object *obedit, bool do_fill)
Definition: editmesh_rip.c:858
static float edbm_rip_edge_side_measure(BMEdge *e, BMLoop *e_l, ARegion *region, float projectMat[4][4], const float fmval[2])
Definition: editmesh_rip.c:98
static int edbm_rip_invoke__vert(bContext *C, const wmEvent *event, Object *obedit, bool do_fill)
Definition: editmesh_rip.c:499
static void edbm_tagged_loop_pairs_do_fill_faces(BMesh *bm, UnorderedLoopPair *uloop_pairs)
Definition: editmesh_rip.c:426
static BMEdge * edbm_ripsel_edge_uid_step(BMEdge *e_orig, BMVert **v_prev)
Definition: editmesh_rip.c:309
static float edbm_rip_edgedist_squared(ARegion *region, float mat[4][4], const float co1[3], const float co2[3], const float mvalf[2], const float inset)
Definition: editmesh_rip.c:43
struct EdgeLoopPair EdgeLoopPair
#define IS_VISIT_POSSIBLE(e)
Definition: editmesh_rip.c:181
static BMVert * edbm_ripsel_edloop_pair_start_vert(BMEdge *e)
Definition: editmesh_rip.c:325
#define INVALID_UID
Definition: editmesh_rip.c:183
static void edbm_calc_loop_co(BMLoop *l, float l_mid_co[3])
Definition: editmesh_rip.c:87
static UnorderedLoopPair * edbm_tagged_loop_pairs_to_fill(BMesh *bm)
Definition: editmesh_rip.c:387
static void edbm_ripsel_deselect_helper(BMesh *bm, EdgeLoopPair *eloop_pairs, ARegion *region, float projectMat[4][4], const float fmval[2])
Definition: editmesh_rip.c:332
struct UnorderedLoopPair UnorderedLoopPair
void MESH_OT_rip(wmOperatorType *ot)
@ ULP_FLIP_0
Definition: editmesh_rip.c:383
@ ULP_FLIP_1
Definition: editmesh_rip.c:384
static EdgeLoopPair * edbm_ripsel_looptag_helper(BMesh *bm)
Definition: editmesh_rip.c:211
#define IS_VISIT_DONE(e)
Definition: editmesh_rip.c:182
static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition: editmesh_rip.c:988
static BMEdge * edbm_ripsel_edge_mark_step(BMVert *v, const int uid)
Definition: editmesh_rip.c:186
bool EDBM_view3d_poll(bContext *C)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
size_t(* MEM_allocN_len)(const void *vmemh)
Definition: mallocn.c:26
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
bool isfinite(uchar)
Definition: scene/image.cpp:31
SymEdge< T > * prev(const SymEdge< T > *se)
Definition: delaunay_2d.cc:105
return ret
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
BMVert * v1
Definition: bmesh_class.h:122
BMVert * v2
Definition: bmesh_class.h:122
struct BMLoop * l
Definition: bmesh_class.h:128
struct BMesh * bm
Definition: BKE_editmesh.h:40
struct BMVert * v
Definition: bmesh_class.h:153
struct BMEdge * e
Definition: bmesh_class.h:164
struct BMLoop * radial_next
Definition: bmesh_class.h:204
struct BMLoop * prev
Definition: bmesh_class.h:233
struct BMFace * f
Definition: bmesh_class.h:171
struct BMLoop * next
Definition: bmesh_class.h:233
float co[3]
Definition: bmesh_class.h:87
struct BMEdge * e
Definition: bmesh_class.h:97
int totvert
Definition: bmesh_class.h:297
int totfacesel
Definition: bmesh_class.h:298
char elem_index_dirty
Definition: bmesh_class.h:305
int totedge
Definition: bmesh_class.h:297
int totvertsel
Definition: bmesh_class.h:298
int totedgesel
Definition: bmesh_class.h:298
BMLoop * l_a
Definition: editmesh_rip.c:207
BMLoop * l_b
Definition: editmesh_rip.c:208
void * data
BMLoop * l_pair[2]
Definition: editmesh_rip.c:379
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
struct ReportList * reports
struct PointerRNA * ptr
wmOperatorType * ot
Definition: wm_files.c:3479