code cleanup: change C naming convention (so py and C api match), eg:
[blender.git] / source / blender / editors / mesh / editmesh_rip.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2004 by Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Joseph Eagar
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/mesh/editmesh_rip.c
29  *  \ingroup edmesh
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_scene_types.h"
35 #include "DNA_object_types.h"
36
37 #include "RNA_define.h"
38 #include "RNA_access.h"
39
40 #include "BLI_math.h"
41 #include "BLI_array.h"
42
43 #include "BKE_context.h"
44 #include "BKE_object.h"
45 #include "BKE_report.h"
46 #include "BKE_tessmesh.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "ED_mesh.h"
52 #include "ED_screen.h"
53 #include "ED_transform.h"
54 #include "ED_view3d.h"
55
56 #include "mesh_intern.h"
57
58 /* helper to find edge for edge_rip */
59 static float edbm_rip_rip_edgedist(ARegion *ar, float mat[][4], float *co1, float *co2, const float mvalf[2])
60 {
61         float vec1[3], vec2[3];
62
63         ED_view3d_project_float_v2(ar, co1, vec1, mat);
64         ED_view3d_project_float_v2(ar, co2, vec2, mat);
65
66         return dist_to_line_segment_v2(mvalf, vec1, vec2);
67 }
68
69 static float edbm_rip_edge_side_measure(BMEdge *e, BMLoop *e_l,
70                                         ARegion *ar,
71                                         float projectMat[4][4], const float fmval[2])
72 {
73         float cent[3] = {0, 0, 0}, mid[3];
74
75         float vec[2];
76         float fmval_tweak[2];
77         float e_v1_co[2], e_v2_co[2];
78         float score;
79
80         BMVert *v1_other;
81         BMVert *v2_other;
82
83         BLI_assert(BM_vert_in_edge(e, e_l->v));
84
85         /* method for calculating distance:
86          *
87          * for each edge: calculate face center, then made a vector
88          * from edge midpoint to face center.  offset edge midpoint
89          * by a small amount along this vector. */
90
91         /* rather then the face center, get the middle of
92          * both edge verts connected to this one */
93         v1_other = BM_face_other_vert_loop(e_l->f, e->v2, e->v1)->v;
94         v2_other = BM_face_other_vert_loop(e_l->f, e->v1, e->v2)->v;
95         mid_v3_v3v3(cent, v1_other->co, v2_other->co);
96         mid_v3_v3v3(mid, e->v1->co, e->v2->co);
97
98         ED_view3d_project_float_v2(ar, cent, cent, projectMat);
99         ED_view3d_project_float_v2(ar, mid, mid, projectMat);
100
101         ED_view3d_project_float_v2(ar, e->v1->co, e_v1_co, projectMat);
102         ED_view3d_project_float_v2(ar, e->v2->co, e_v2_co, projectMat);
103
104         sub_v2_v2v2(vec, cent, mid);
105         normalize_v2(vec);
106         mul_v2_fl(vec, 0.01f);
107
108         /* rather then adding to both verts, subtract from the mouse */
109         sub_v2_v2v2(fmval_tweak, fmval, vec);
110
111         score = len_v2v2(e_v1_co, e_v2_co);
112
113         if (dist_to_line_segment_v2(fmval_tweak, e_v1_co, e_v2_co) >
114                 dist_to_line_segment_v2(fmval,       e_v1_co, e_v2_co))
115         {
116                 return  score;
117         }
118         else {
119                 return -score;
120         }
121 }
122
123
124 /* - Advanced selection handling 'ripsel' functions ----- */
125
126 /**
127  * How rip selection works
128  *
129  * Firstly - rip is basically edge split with side-selection & grab.
130  * Things would be much more simple if we didn't have to worry about side selection
131  *
132  * The method used for checking the side of selection is as follows...
133  * - First tag all rip-able edges.
134  * - Build a contiguous edge list by looping over tagged edges and following each ones tagged siblings in both
135  *   directions.
136  *   - The loops are not stored in an array, Instead both loops on either side of each edge has its index values set
137  *     to count down from the last edge, this way, once we have the 'last' edge its very easy to walk down the
138  *     connected edge loops.
139  *     The reason for using loops like this is because when the edges are split we don't which face user gets the newly
140  *     created edge (its as good as random so we cant assume new edges will be on once side).
141  *     After splittingm, its very simple to walk along boundary loops since each only has one edge from a single side.
142  * - The end loop pairs are stored in an array however to support multiple edge-selection-islands, so you can rip
143  *   multiple selections at once.
144  * - * Execute the split *
145  * - For each #EdgeLoopPair walk down both sides of the split using the loops and measure which is facing the mouse.
146  * - Deselect the edge loop facing away.
147  *
148  * Limitation!
149  * This currently works very poorly with intersecting edge islands (verts with more then 2 tagged edges)
150  * This is nice to but for now not essential.
151  *
152  * - campbell.
153  */
154
155
156 #define IS_VISIT_POSSIBLE(e)   (BM_edge_is_manifold(e) && BM_elem_flag_test(e, BM_ELEM_TAG))
157 #define IS_VISIT_DONE(e) ((e)->l && (BM_elem_index_get((e)->l) != INVALID_UID))
158 #define INVALID_UID INT_MIN
159
160 /* mark, assign uid and step */
161 static BMEdge *edbm_ripsel_edge_mark_step(BMVert *v, const int uid)
162 {
163         BMIter iter;
164         BMEdge *e;
165         BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
166                 if (IS_VISIT_POSSIBLE(e) && !IS_VISIT_DONE(e)) {
167                         BMLoop *l_a, *l_b;
168
169                         BM_edge_loop_pair(e, &l_a, &l_b); /* no need to check, we know this will be true */
170
171                         /* so (IS_VISIT_DONE == TRUE) */
172                         BM_elem_index_set(l_a, uid);
173                         BM_elem_index_set(l_b, uid);
174
175                         return e;
176                 }
177         }
178         return NULL;
179 }
180
181 typedef struct EdgeLoopPair {
182         BMLoop *l_a;
183         BMLoop *l_b;
184 } EdgeLoopPair;
185
186 static EdgeLoopPair *edbm_ripsel_looptag_helper(BMesh *bm)
187 {
188         BMIter fiter;
189         BMIter liter;
190
191         BMFace *f;
192         BMLoop *l;
193
194         int uid_start;
195         int uid_end;
196         int uid = bm->totedge; /* can start anywhere */
197
198         EdgeLoopPair *eloop_pairs = NULL;
199         BLI_array_declare(eloop_pairs);
200         EdgeLoopPair *lp;
201
202         /* initialize loops with dummy invalid index values */
203         BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
204                 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
205                         BM_elem_index_set(l, INVALID_UID);
206                 }
207         }
208
209         /* set contiguous loops ordered 'uid' values for walking after split */
210         while (TRUE) {
211                 int tot = 0;
212                 BMIter eiter;
213                 BMEdge *e_step;
214                 BMVert *v_step;
215                 BMEdge *e;
216                 BMEdge *e_first;
217                 BMEdge *e_last;
218
219                 e_first = NULL;
220                 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
221                         if (IS_VISIT_POSSIBLE(e) && !IS_VISIT_DONE(e)) {
222                                 e_first = e;
223                                 break;
224                         }
225                 }
226
227                 if (e_first == NULL) {
228                         break;
229                 }
230
231                 /* initialize  */
232                 e_first = e;
233                 v_step = e_first->v1;
234                 e_step = NULL; /* quiet warning, will never remain this value */
235
236                 uid_start = uid;
237                 while ((e = edbm_ripsel_edge_mark_step(v_step, uid))) {
238                         BM_elem_flag_disable(e, BM_ELEM_SMOOTH);
239                         v_step = BM_edge_other_vert((e_step = e), v_step);
240                         uid++; /* only different line */
241                         tot++;
242                 }
243
244                 /* this edges loops have the highest uid's, store this to walk down later */
245                 e_last = e_step;
246
247                 /* always store the highest 'uid' edge for the stride */
248                 uid_end = uid - 1;
249                 uid = uid_start - 1;
250
251                 /* initialize */
252                 v_step = e_first->v1;
253
254                 while ((e = edbm_ripsel_edge_mark_step(v_step, uid))) {
255                         BM_elem_flag_disable(e, BM_ELEM_SMOOTH);
256                         v_step = BM_edge_other_vert((e_step = e), v_step);
257                         uid--; /* only different line */
258                         tot++;
259                 }
260
261                 /* stride far enough not to _ever_ overlap range */
262                 uid_start = uid;
263                 uid = uid_end + bm->totedge;
264
265                 BLI_array_growone(eloop_pairs);
266                 lp = &eloop_pairs[BLI_array_count(eloop_pairs) - 1];
267                 BM_edge_loop_pair(e_last, &lp->l_a, &lp->l_b); /* no need to check, we know this will be true */
268
269
270                 BLI_assert(tot == uid_end - uid_start);
271
272 #if 0
273                 printf("%s: found contiguous edge loop of (%d)\n", __func__, uid_end - uid_start);
274 #endif
275
276         }
277
278         /* null terminate */
279         BLI_array_growone(eloop_pairs);
280         lp = &eloop_pairs[BLI_array_count(eloop_pairs) - 1];
281         lp->l_a = lp->l_b = NULL;
282
283         return eloop_pairs;
284 }
285
286
287 /* - De-Select the worst rip-edge side -------------------------------- */
288
289
290 static BMEdge *edbm_ripsel_edge_uid_step(BMEdge *e_orig, BMVert **v_prev)
291 {
292         BMIter eiter;
293         BMEdge *e;
294         BMVert *v = BM_edge_other_vert(e_orig, *v_prev);
295         const int uid_cmp = BM_elem_index_get(e_orig->l) - 1;
296
297         BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
298                 if (BM_elem_index_get(e->l) == uid_cmp) {
299                         *v_prev = v;
300                         return e;
301                 }
302         }
303         return NULL;
304 }
305
306 static BMVert *edbm_ripsel_edloop_pair_start_vert(BMEdge *e)
307 {
308         /* try step in a direction, if it fails we know do go the other way */
309         BMVert *v_test = e->v1;
310         return (edbm_ripsel_edge_uid_step(e, &v_test)) ? e->v1 : e->v2;
311 }
312
313 static void edbm_ripsel_deselect_helper(BMesh *bm, EdgeLoopPair *eloop_pairs,
314                                         ARegion *ar, float projectMat[4][4], float fmval[2])
315 {
316         EdgeLoopPair *lp;
317
318         for (lp = eloop_pairs; lp->l_a; lp++) {
319                 BMEdge *e;
320                 BMVert *v_prev;
321
322                 float score_a = 0.0f;
323                 float score_b = 0.0f;
324
325                 e = lp->l_a->e;
326                 v_prev = edbm_ripsel_edloop_pair_start_vert(e);
327                 for (; e; e = edbm_ripsel_edge_uid_step(e, &v_prev)) {
328                         score_a += edbm_rip_edge_side_measure(e, e->l, ar, projectMat, fmval);
329                 }
330                 e = lp->l_b->e;
331                 v_prev = edbm_ripsel_edloop_pair_start_vert(e);
332                 for (; e; e = edbm_ripsel_edge_uid_step(e, &v_prev)) {
333                         score_b += edbm_rip_edge_side_measure(e, e->l, ar, projectMat, fmval);
334                 }
335
336                 e = (score_a > score_b) ? lp->l_a->e : lp->l_b->e;
337                 v_prev = edbm_ripsel_edloop_pair_start_vert(e);
338                 for (; e; e = edbm_ripsel_edge_uid_step(e, &v_prev)) {
339                         BM_edge_select_set(bm, e, FALSE);
340                 }
341         }
342 }
343 /* --- end 'ripsel' selection handling code --- */
344
345 static int edbm_rip_call_edgesplit(BMEditMesh *em, wmOperator *op)
346 {
347         BMOperator bmop;
348
349         if (!EDBM_op_init(em, &bmop, op, "edgesplit edges=%he verts=%hv use_verts=%b",
350                                           BM_ELEM_TAG, BM_ELEM_SELECT, TRUE)) {
351                 return FALSE;
352         }
353         BMO_op_exec(em->bm, &bmop);
354         if (!EDBM_op_finish(em, &bmop, op, TRUE)) {
355                 return FALSE;
356         }
357
358         return TRUE;
359 }
360
361 /**
362  * This is the main vert ripping function (rip when one vertex is selected)
363  */
364 static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, wmEvent *event)
365 {
366         Object *obedit = CTX_data_edit_object(C);
367         ARegion *ar = CTX_wm_region(C);
368         RegionView3D *rv3d = CTX_wm_region_view3d(C);
369         BMEditMesh *em = BMEdit_FromObject(obedit);
370         BMesh *bm = em->bm;
371         BMIter iter, liter;
372         BMLoop *l;
373         BMEdge *e, *e2;
374         BMVert *v, *ripvert = NULL;
375         int i;
376         float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]};
377         float dist = FLT_MAX;
378         float d;
379
380         BMEditSelection ese;
381         int totboundary_edge = 0;
382
383         ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat);
384
385         /* find selected vert - same some time and check history first */
386         if (EDBM_editselection_active_get(em, &ese) && ese.htype == BM_VERT) {
387                 v = (BMVert *)ese.ele;
388         }
389         else {
390                 ese.ele = NULL;
391
392                 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
393                         if (BM_elem_flag_test(v, BM_ELEM_SELECT))
394                                 break;
395                 }
396         }
397
398         /* this should be impossible, but sanity checks are a good thing */
399         if (!v)
400                 return OPERATOR_CANCELLED;
401
402         e2 = NULL;
403
404         if (v->e) {
405                 /* find closest edge to mouse cursor */
406                 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
407                         int is_boundary = BM_edge_is_boundary(e);
408                         /* consider wire as boundary for this purpose,
409                          * otherwise we can't a face away from a wire edge */
410                         totboundary_edge += (is_boundary != 0 || BM_edge_is_wire(e));
411                         if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
412                                 if (is_boundary == FALSE && BM_edge_is_manifold(e)) {
413                                         d = edbm_rip_rip_edgedist(ar, projectMat, e->v1->co, e->v2->co, fmval);
414                                         if (d < dist) {
415                                                 dist = d;
416                                                 e2 = e;
417                                         }
418                                 }
419                         }
420                 }
421
422         }
423
424         /* should we go ahead with edge rip or do we need to do special case, split off vertex?:
425          * split off vertex if...
426          * - we cant find an edge - this means we are ripping a faces vert that is connected to other
427          *   geometry only at the vertex.
428          * - the boundary edge total is greater then 2,
429          *   in this case edge split _can_ work but we get far nicer results if we use this special case. */
430         if (totboundary_edge > 2) {
431                 BMVert **vout;
432                 int vout_len;
433
434                 BM_vert_select_set(bm, v, FALSE);
435                 bmesh_vert_separate(bm, v, &vout, &vout_len);
436
437                 if (vout_len < 2) {
438                         /* set selection back to avoid active-unselected vertex */
439                         BM_vert_select_set(bm, v, TRUE);
440                         /* should never happen */
441                         BKE_report(op->reports, RPT_ERROR, "Error ripping vertex from faces");
442                         return OPERATOR_CANCELLED;
443                 }
444                 else {
445                         int vi_best = 0;
446
447                         if (ese.ele) {
448                                 EDBM_editselection_remove(em, &ese.ele->head);
449                         }
450
451                         dist = FLT_MAX;
452
453                         for (i = 0; i < vout_len; i++) {
454                                 BM_ITER_ELEM (l, &iter, vout[i], BM_LOOPS_OF_VERT) {
455                                         if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) {
456                                                 float l_mid_co[3];
457                                                 BM_loop_calc_face_tangent(l, l_mid_co);
458
459                                                 /* scale to average of surrounding edge size, only needs to be approx */
460                                                 mul_v3_fl(l_mid_co, (BM_edge_calc_length(l->e) + BM_edge_calc_length(l->prev->e)) / 2.0f);
461                                                 add_v3_v3(l_mid_co, v->co);
462
463                                                 d = edbm_rip_rip_edgedist(ar, projectMat, v->co, l_mid_co, fmval);
464
465                                                 if (d < dist) {
466                                                         dist = d;
467                                                         vi_best = i;
468                                                 }
469                                         }
470                                 }
471                         }
472
473                         /* select the vert from the best region */
474                         v = vout[vi_best];
475                         BM_vert_select_set(bm, v, TRUE);
476
477                         if (ese.ele) {
478                                 EDBM_editselection_store(em, &v->head);
479                         }
480
481                         /* splice all others back together */
482                         if (vout_len > 2) {
483
484                                 /* vout[0]  == best
485                                  * vout[1]  == glue
486                                  * vout[2+] == splice with glue
487                                  */
488                                 if (vi_best != 0) {
489                                         SWAP(BMVert *, vout[0], vout[vi_best]);
490                                         vi_best = 0;
491                                 }
492
493                                 for (i = 2; i < vout_len; i++) {
494                                         BM_vert_splice(bm, vout[i], vout[1]);
495                                 }
496                         }
497
498                         MEM_freeN(vout);
499
500                         return OPERATOR_FINISHED;
501                 }
502         }
503
504         if (!e2) {
505                 BKE_report(op->reports, RPT_ERROR, "Selected vertex has no edge/face pairs attached");
506                 return OPERATOR_CANCELLED;
507         }
508
509         /* rip two adjacent edges */
510         if (BM_edge_is_boundary(e2) || BM_vert_face_count(v) == 2) {
511                 l = e2->l;
512                 ripvert = BM_face_vert_separate(bm, l->f, v);
513
514                 BLI_assert(ripvert);
515                 if (!ripvert) {
516                         return OPERATOR_CANCELLED;
517                 }
518         }
519         else if (BM_edge_is_manifold(e2)) {
520                 l = e2->l;
521                 e = BM_face_other_edge_loop(l->f, e2, v)->e;
522                 BM_elem_flag_enable(e, BM_ELEM_TAG);
523
524                 l = e2->l->radial_next;
525                 e = BM_face_other_edge_loop(l->f, e2, v)->e;
526                 BM_elem_flag_enable(e, BM_ELEM_TAG);
527         }
528
529         dist = FLT_MAX;
530
531         if (!edbm_rip_call_edgesplit(em, op)) {
532                 return OPERATOR_CANCELLED;
533         }
534         else {
535                 /* --- select which vert --- */
536                 BMVert *v_best = NULL;
537                 float l_prev_co[3], l_next_co[3], l_corner_co[3];
538                 float scale;
539
540                 dist = FLT_MAX;
541                 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
542                         if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
543                                 /* disable by default, re-enable winner at end */
544                                 BM_vert_select_set(bm, v, FALSE);
545
546                                 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
547                                         /* calculate a point in the face, rather then calculate the middle,
548                                          * make a vector pointing between the 2 edges attached to this loop */
549                                         sub_v3_v3v3(l_prev_co, l->prev->v->co, l->v->co);
550                                         sub_v3_v3v3(l_next_co, l->next->v->co, l->v->co);
551
552                                         scale = normalize_v3(l_prev_co) + normalize_v3(l_next_co);
553                                         mul_v3_fl(l_prev_co, scale);
554                                         mul_v3_fl(l_next_co, scale);
555
556                                         add_v3_v3v3(l_corner_co, l_prev_co, l_next_co);
557                                         add_v3_v3(l_corner_co, l->v->co);
558
559                                         d = edbm_rip_rip_edgedist(ar, projectMat, l->v->co, l_corner_co, fmval);
560                                         if (d < dist) {
561                                                 v_best = v;
562                                                 dist = d;
563                                         }
564                                 }
565                         }
566                 }
567
568                 if (v_best) {
569                         BM_vert_select_set(bm, v_best, TRUE);
570                         if (ese.ele) {
571                                 EDBM_editselection_store(em, &v_best->head);
572                         }
573                 }
574         }
575
576         return OPERATOR_FINISHED;
577 }
578
579 /**
580  * This is the main edge ripping function
581  */
582 static int edbm_rip_invoke__edge(bContext *C, wmOperator *op, wmEvent *event)
583 {
584         Object *obedit = CTX_data_edit_object(C);
585         ARegion *ar = CTX_wm_region(C);
586         RegionView3D *rv3d = CTX_wm_region_view3d(C);
587         BMEditMesh *em = BMEdit_FromObject(obedit);
588         BMesh *bm = em->bm;
589         BMIter iter, eiter;
590         BMLoop *l;
591         BMEdge *e, *e2;
592         BMVert *v;
593         int i;
594         float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]};
595
596         int totedge;
597         int all_minifold;
598
599         EdgeLoopPair *eloop_pairs;
600
601         ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat);
602
603         /* important this runs on the original selection, before tempering with tagging */
604         eloop_pairs = edbm_ripsel_looptag_helper(bm);
605
606         /* expand edge selection */
607         BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
608                 e2 = NULL;
609                 i = 0;
610                 totedge = 0;
611                 all_minifold = TRUE;
612                 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
613
614                         if (!BM_edge_is_wire(e) &&
615                             !BM_elem_flag_test(e, BM_ELEM_HIDDEN))
616                         {
617                                 /* important to check selection rather then tag here
618                                  * else we get feedback loop */
619                                 if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
620                                         e2 = e;
621                                         i++;
622                                 }
623                                 totedge++;
624                         }
625
626                         /** #BM_vert_other_disk_edge has no hidden checks so don't check hidden here */
627                         if ((all_minifold == TRUE) && (BM_edge_is_manifold(e) == FALSE)) {
628                                 all_minifold = FALSE;
629                         }
630                 }
631
632                 /* single edge, extend */
633                 if (i == 1 && e2->l) {
634                         if ((totedge == 4) || (all_minifold == FALSE)) {
635                                 BMLoop *l_a = e2->l;
636                                 BMLoop *l_b = l_a->radial_next;
637
638                                 /* find the best face to follow, this what the edge won't point away from
639                                  * the mouse when there are more then 4 (takes the shortest face fan around) */
640                                 l = (edbm_rip_edge_side_measure(e2, l_a, ar, projectMat, fmval) <
641                                      edbm_rip_edge_side_measure(e2, l_b, ar, projectMat, fmval)) ? l_a : l_b;
642
643                                 l = BM_face_other_edge_loop(l->f, e2, v);
644                                 l = l->radial_next;
645                                 l = BM_face_other_edge_loop(l->f, l->e, v);
646
647                                 if (l) {
648                                         BM_elem_flag_enable(l->e, BM_ELEM_TAG);
649                                 }
650                         }
651                         else {
652                                 e = BM_vert_other_disk_edge(v, e2);
653
654                                 if (e) {
655                                         BM_elem_flag_enable(e, BM_ELEM_TAG);
656                                 }
657                         }
658                 }
659         }
660
661         if (!edbm_rip_call_edgesplit(em, op)) {
662                 return OPERATOR_CANCELLED;
663         }
664
665         /* note: the output of the bmesh operator is ignored, since we built
666          * the contiguous loop pairs to split already, its possible that some
667          * edge did not split even though it was tagged which would not work
668          * as expected (but not crash), however there are checks to ensure
669          * tagged edges will split. So far its not been an issue. */
670         edbm_ripsel_deselect_helper(bm, eloop_pairs,
671                                     ar, projectMat, fmval);
672         MEM_freeN(eloop_pairs);
673
674
675         return OPERATOR_FINISHED;
676 }
677
678 /* based on mouse cursor position, it defines how is being ripped */
679 static int edbm_rip_invoke(bContext *C, wmOperator *op, wmEvent *event)
680 {
681         Object *obedit = CTX_data_edit_object(C);
682         BMEditMesh *em = BMEdit_FromObject(obedit);
683         BMesh *bm = em->bm;
684         BMIter iter;
685         BMEdge *e;
686         int singlesel = (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0);
687         const int totedge_orig = bm->totedge;
688         int ret;
689
690         /* running in face mode hardly makes sense, so convert to region loop and rip */
691         if (em->bm->totfacesel) {
692                 /* highly nifty but hard to sypport since the operator can fail and we're left
693                  * with modified selection */
694                 // WM_operator_name_call(C, "MESH_OT_region_to_loop", WM_OP_INVOKE_DEFAULT, NULL);
695
696                 BKE_report(op->reports, RPT_ERROR, "Can't rip selected faces");
697                 return OPERATOR_CANCELLED;
698         }
699
700         /* note on selection:
701          * When calling edge split we operate on tagged edges rather then selected
702          * this is important because the edges to operate on are extended by one,
703          * but the selection is left alone.
704          *
705          * After calling edge split - the duplicated edges have the same selection state as the
706          * original, so all we do is de-select the far side from the mouse and we have a
707          * useful selection for grabbing.
708          */
709
710         /* BM_ELEM_SELECT --> BM_ELEM_TAG */
711         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
712                 BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT));
713         }
714
715         /* split 2 main parts of this operator out into vertex and edge ripping */
716         if (singlesel) {
717                 ret = edbm_rip_invoke__vert(C, op, event);
718         }
719         else {
720                 ret = edbm_rip_invoke__edge(C, op, event);
721         }
722
723         if (ret == OPERATOR_CANCELLED) {
724                 return OPERATOR_CANCELLED;
725         }
726
727         EDBM_selectmode_flush(em);
728
729         if (totedge_orig == bm->totedge) {
730                 BKE_report(op->reports, RPT_ERROR, "No edges could be ripped");
731                 return OPERATOR_CANCELLED;
732         }
733
734         BLI_assert(singlesel ? (bm->totvertsel > 0) : (bm->totedgesel > 0));
735
736         if (bm->totvertsel == 0) {
737                 return OPERATOR_CANCELLED;
738         }
739
740         EDBM_update_generic(C, em, TRUE);
741
742         return OPERATOR_FINISHED;
743 }
744
745
746 void MESH_OT_rip(wmOperatorType *ot)
747 {
748         /* identifiers */
749         ot->name = "Rip";
750         ot->idname = "MESH_OT_rip";
751         ot->description = "Disconnect vertex or edges from connected geometry";
752
753         /* api callbacks */
754         ot->invoke = edbm_rip_invoke;
755         ot->poll = EM_view3d_poll;
756
757         /* flags */
758         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
759
760         /* to give to transform */
761         Transform_Properties(ot, P_PROPORTIONAL);
762         RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
763 }