Merge branch 'blender2.7' into master.
[blender.git] / source / blender / editors / mesh / editmesh_rip_edge.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  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/mesh/editmesh_rip_edge.c
22  *  \ingroup edmesh
23  *
24  * based on mouse cursor position, split of vertices along the closest edge.
25  */
26
27 #include "MEM_guardedalloc.h"
28
29 #include "DNA_object_types.h"
30
31 #include "BLI_math.h"
32
33 #include "BKE_context.h"
34 #include "BKE_report.h"
35 #include "BKE_editmesh.h"
36 #include "BKE_layer.h"
37
38 #include "WM_types.h"
39
40 #include "ED_mesh.h"
41 #include "ED_screen.h"
42 #include "ED_transform.h"
43 #include "ED_view3d.h"
44
45 #include "bmesh.h"
46
47 #include "mesh_intern.h"  /* own include */
48
49 /* uses total number of selected edges around a vertex to choose how to extend */
50 #define USE_TRICKY_EXTEND
51
52 static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
53 {
54         ARegion *ar = CTX_wm_region(C);
55         RegionView3D *rv3d = CTX_wm_region_view3d(C);
56         ViewLayer *view_layer = CTX_data_view_layer(C);
57         uint objects_len = 0;
58         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
59
60         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
61                 Object *obedit = objects[ob_index];
62                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
63                 BMesh *bm = em->bm;
64
65                 BMIter viter;
66                 BMVert *v;
67                 const float mval_fl[2] = { UNPACK2(event->mval) };
68                 float cent_sco[2];
69                 int cent_tot;
70                 bool changed = false;
71
72                 /* mouse direction to view center */
73                 float mval_dir[2];
74
75                 float projectMat[4][4];
76
77                 if (bm->totvertsel == 0)
78                         continue;
79
80                 ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat);
81
82                 zero_v2(cent_sco);
83                 cent_tot = 0;
84
85                 /* clear tags and calc screen center */
86                 BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
87                         BM_elem_flag_disable(v, BM_ELEM_TAG);
88
89                         if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
90                                 float v_sco[2];
91                                 ED_view3d_project_float_v2_m4(ar, v->co, v_sco, projectMat);
92
93                                 add_v2_v2(cent_sco, v_sco);
94                                 cent_tot += 1;
95                         }
96                 }
97                 mul_v2_fl(cent_sco, 1.0f / (float)cent_tot);
98
99                 /* not essential, but gives more expected results with edge selection */
100                 if (bm->totedgesel) {
101                         /* angle against center can give odd result,
102                          * try re-position the center to the closest edge */
103                         BMIter eiter;
104                         BMEdge *e;
105                         float dist_sq_best = len_squared_v2v2(cent_sco, mval_fl);
106
107                         BM_ITER_MESH(e, &eiter, bm, BM_EDGES_OF_MESH) {
108                                 if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
109                                         float e_sco[2][2];
110                                         float cent_sco_test[2];
111                                         float dist_sq_test;
112
113                                         ED_view3d_project_float_v2_m4(ar, e->v1->co, e_sco[0], projectMat);
114                                         ED_view3d_project_float_v2_m4(ar, e->v2->co, e_sco[1], projectMat);
115
116                                         closest_to_line_segment_v2(cent_sco_test, mval_fl, e_sco[0], e_sco[1]);
117                                         dist_sq_test = len_squared_v2v2(cent_sco_test, mval_fl);
118                                         if (dist_sq_test < dist_sq_best) {
119                                                 dist_sq_best = dist_sq_test;
120
121                                                 /* we have a new screen center */
122                                                 copy_v2_v2(cent_sco, cent_sco_test);
123                                         }
124                                 }
125                         }
126                 }
127
128                 sub_v2_v2v2(mval_dir, mval_fl, cent_sco);
129                 normalize_v2(mval_dir);
130
131                 /* operate on selected verts */
132                 BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
133                         BMIter eiter;
134                         BMEdge *e;
135                         float v_sco[2];
136
137                         if (BM_elem_flag_test(v, BM_ELEM_SELECT) &&
138                             BM_elem_flag_test(v, BM_ELEM_TAG) == false)
139                         {
140                                 /* Rules for */
141                                 float angle_best = FLT_MAX;
142                                 BMEdge *e_best = NULL;
143
144 #ifdef USE_TRICKY_EXTEND
145                                 /* first check if we can select the edge to split based on selection-only */
146                                 int tot_sel = 0, tot = 0;
147
148                                 BM_ITER_ELEM(e, &eiter, v, BM_EDGES_OF_VERT) {
149                                         if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
150                                                 if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
151                                                         e_best = e;
152                                                         tot_sel += 1;
153                                                 }
154                                                 tot += 1;
155                                         }
156                                 }
157                                 if (tot_sel != 1) {
158                                         e_best = NULL;
159                                 }
160
161                                 /* only one edge selected, operate on that */
162                                 if (e_best) {
163                                         goto found_edge;
164                                 }
165                                 /* none selected, fall through and find one */
166                                 else if (tot_sel == 0) {
167                                         /* pass */
168                                 }
169                                 /* selection not 0 or 1, do nothing */
170                                 else {
171                                         goto found_edge;
172                                 }
173 #endif
174                                 ED_view3d_project_float_v2_m4(ar, v->co, v_sco, projectMat);
175
176                                 BM_ITER_ELEM(e, &eiter, v, BM_EDGES_OF_VERT) {
177                                         if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
178                                                 BMVert *v_other = BM_edge_other_vert(e, v);
179                                                 float v_other_sco[2];
180                                                 float angle_test;
181
182                                                 ED_view3d_project_float_v2_m4(ar, v_other->co, v_other_sco, projectMat);
183
184                                                 /* avoid comparing with view-axis aligned edges (less than a pixel) */
185                                                 if (len_squared_v2v2(v_sco, v_other_sco) > 1.0f) {
186                                                         float v_dir[2];
187
188                                                         sub_v2_v2v2(v_dir, v_other_sco, v_sco);
189                                                         normalize_v2(v_dir);
190
191                                                         angle_test = angle_normalized_v2v2(mval_dir, v_dir);
192
193                                                         if (angle_test < angle_best) {
194                                                                 angle_best = angle_test;
195                                                                 e_best = e;
196                                                         }
197                                                 }
198                                         }
199                                 }
200
201 #ifdef USE_TRICKY_EXTEND
202 found_edge :
203 #endif
204                                 if (e_best) {
205                                         const bool e_select = BM_elem_flag_test_bool(e_best, BM_ELEM_SELECT);
206                                         BMVert *v_new;
207                                         BMEdge *e_new;
208
209                                         v_new = BM_edge_split(bm, e_best, v, &e_new, 0.0f);
210
211                                         BM_vert_select_set(bm, v, false);
212                                         BM_edge_select_set(bm, e_new, false);
213
214                                         BM_vert_select_set(bm, v_new, true);
215                                         if (e_select) {
216                                                 BM_edge_select_set(bm, e_best, true);
217                                         }
218                                         BM_elem_flag_enable(v_new, BM_ELEM_TAG);  /* prevent further splitting */
219
220                                         changed = true;
221                                 }
222                         }
223                 }
224
225                 if (changed) {
226                         BM_select_history_clear(bm);
227
228                         BM_mesh_select_mode_flush(bm);
229
230                         EDBM_update_generic(em, true, true);
231                 }
232         }
233
234         MEM_freeN(objects);
235
236         return OPERATOR_FINISHED;
237 }
238
239
240 void MESH_OT_rip_edge(wmOperatorType *ot)
241 {
242         /* identifiers */
243         ot->name = "Extend Vertices";
244         ot->idname = "MESH_OT_rip_edge";
245         ot->description = "Extend vertices along the edge closest to the cursor";
246
247         /* api callbacks */
248         ot->invoke = edbm_rip_edge_invoke;
249         ot->poll = EDBM_view3d_poll;
250
251         /* flags */
252         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
253
254         /* to give to transform */
255         Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
256 }