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