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