Fix 37571: Knife Select should be disabled when no faces are selected
[blender-staging.git] / source / blender / editors / mesh / editmesh_bisect.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) 2013 by Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Campbell Barton
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/mesh/editmesh_bisect.c
27  *  \ingroup edmesh
28  */
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_object_types.h"
33
34 #include "BLI_math.h"
35
36 #include "BKE_global.h"
37 #include "BKE_context.h"
38 #include "BKE_editmesh.h"
39 #include "BKE_report.h"
40
41 #include "RNA_define.h"
42 #include "RNA_access.h"
43
44 #include "WM_api.h"
45 #include "WM_types.h"
46
47 #include "ED_mesh.h"
48 #include "ED_screen.h"
49 #include "ED_view3d.h"
50
51
52 #include "mesh_intern.h"  /* own include */
53
54 static int mesh_bisect_exec(bContext *C, wmOperator *op);
55
56 /* -------------------------------------------------------------------- */
57 /* Model Helpers */
58
59 typedef struct {
60         /* modal only */
61         BMBackup mesh_backup;
62         bool is_first;
63         short twtype;
64 } BisectData;
65
66 static bool mesh_bisect_interactive_calc(
67         bContext *C, wmOperator *op,
68         BMEditMesh *em,
69         float plane_co[3], float plane_no[3])
70 {
71         wmGesture *gesture = op->customdata;
72         BisectData *opdata;
73
74         ARegion *ar = CTX_wm_region(C);
75         RegionView3D *rv3d = ar->regiondata;
76
77         int x_start = RNA_int_get(op->ptr, "xstart");
78         int y_start = RNA_int_get(op->ptr, "ystart");
79         int x_end = RNA_int_get(op->ptr, "xend");
80         int y_end = RNA_int_get(op->ptr, "yend");
81
82         /* reference location (some point in front of the view) for finding a point on a plane */
83         const float *co_ref = rv3d->ofs;
84         float co_a_ss[2] = {x_start, y_start}, co_b_ss[2] = {x_end, y_end}, co_delta_ss[2];
85         float co_a[3], co_b[3];
86         const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL);
87
88         opdata = gesture->userdata;
89
90         /* view vector */
91         ED_view3d_win_to_vector(ar, co_a_ss, co_a);
92
93         /* view delta */
94         sub_v2_v2v2(co_delta_ss, co_a_ss, co_b_ss);
95         ED_view3d_win_to_delta(ar, co_delta_ss, co_b, zfac);
96
97         /* cross both to get a normal */
98         cross_v3_v3v3(plane_no, co_a, co_b);
99         normalize_v3(plane_no);  /* not needed but nicer for user */
100
101         /* point on plane, can use either start or endpoint */
102         ED_view3d_win_to_3d(ar, co_ref, co_a_ss, plane_co);
103
104         if (opdata->is_first == false)
105                 EDBM_redo_state_restore(opdata->mesh_backup, em, false);
106
107         opdata->is_first = false;
108
109         return true;
110 }
111
112 static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event)
113 {
114         Object *obedit = CTX_data_edit_object(C);
115         BMEditMesh *em = BKE_editmesh_from_object(obedit);
116         int ret;
117
118         if (em->bm->totedgesel == 0) {
119                 BKE_report(op->reports, RPT_ERROR, "Selected edges/faces required");
120                 return OPERATOR_CANCELLED;
121         }
122
123         /* if the properties are set or there is no rv3d,
124          * skip model and exec immediately */
125
126         if ((CTX_wm_region_view3d(C) == NULL) ||
127             (RNA_struct_property_is_set(op->ptr, "plane_co") &&
128              RNA_struct_property_is_set(op->ptr, "plane_no")))
129         {
130                 return mesh_bisect_exec(C, op);
131         }
132
133         ret = WM_gesture_straightline_invoke(C, op, event);
134         if (ret & OPERATOR_RUNNING_MODAL) {
135                 View3D *v3d = CTX_wm_view3d(C);
136
137                 wmGesture *gesture = op->customdata;
138                 BisectData *opdata;
139
140
141                 opdata = MEM_mallocN(sizeof(BisectData), "inset_operator_data");
142                 opdata->mesh_backup = EDBM_redo_state_store(em);
143                 opdata->is_first = true;
144                 gesture->userdata = opdata;
145
146                 /* misc other vars */
147                 G.moving = G_TRANSFORM_EDIT;
148                 opdata->twtype = v3d->twtype;
149                 v3d->twtype = 0;
150         }
151         return ret;
152 }
153
154 static void edbm_bisect_exit(bContext *C, BisectData *opdata)
155 {
156         View3D *v3d = CTX_wm_view3d(C);
157         EDBM_redo_state_free(&opdata->mesh_backup, NULL, false);
158         v3d->twtype = opdata->twtype;
159         G.moving = 0;
160 }
161
162 static int mesh_bisect_modal(bContext *C, wmOperator *op, const wmEvent *event)
163 {
164         wmGesture *gesture = op->customdata;
165         BisectData *opdata = gesture->userdata;
166         BisectData opdata_back = *opdata;  /* annoyance, WM_gesture_straightline_modal, frees */
167         int ret;
168
169         ret = WM_gesture_straightline_modal(C, op, event);
170
171         if (ret & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
172                 edbm_bisect_exit(C, &opdata_back);
173         }
174
175         return ret;
176 }
177
178 /* End Model Helpers */
179 /* -------------------------------------------------------------------- */
180
181
182
183 static int mesh_bisect_exec(bContext *C, wmOperator *op)
184 {
185         Scene *scene = CTX_data_scene(C);
186
187         /* both can be NULL, fallbacks values are used */
188         View3D *v3d = CTX_wm_view3d(C);
189         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
190
191         Object *obedit = CTX_data_edit_object(C);
192         BMEditMesh *em = BKE_editmesh_from_object(obedit);
193         BMesh *bm;
194         BMOperator bmop;
195         float plane_co[3];
196         float plane_no[3];
197         float imat[4][4];
198
199         const float thresh = RNA_float_get(op->ptr, "threshold");
200         const bool use_fill = RNA_boolean_get(op->ptr, "use_fill");
201         const bool clear_inner = RNA_boolean_get(op->ptr, "clear_inner");
202         const bool clear_outer = RNA_boolean_get(op->ptr, "clear_outer");
203
204         PropertyRNA *prop_plane_co;
205         PropertyRNA *prop_plane_no;
206
207         prop_plane_co = RNA_struct_find_property(op->ptr, "plane_co");
208         if (RNA_property_is_set(op->ptr, prop_plane_co)) {
209                 RNA_property_float_get_array(op->ptr, prop_plane_co, plane_co);
210         }
211         else {
212                 copy_v3_v3(plane_co, ED_view3d_cursor3d_get(scene, v3d));
213                 RNA_property_float_set_array(op->ptr, prop_plane_co, plane_co);
214         }
215
216         prop_plane_no = RNA_struct_find_property(op->ptr, "plane_no");
217         if (RNA_property_is_set(op->ptr, prop_plane_no)) {
218                 RNA_property_float_get_array(op->ptr, prop_plane_no, plane_no);
219         }
220         else {
221                 if (rv3d) {
222                         copy_v3_v3(plane_no, rv3d->viewinv[1]);
223                 }
224                 else {
225                         /* fallback... */
226                         plane_no[0] = plane_no[1] = 0.0f; plane_no[2] = 1.0f;
227                 }
228                 RNA_property_float_set_array(op->ptr, prop_plane_no, plane_no);
229         }
230
231
232
233         /* -------------------------------------------------------------------- */
234         /* Modal support */
235         /* Note: keep this isolated, exec can work wihout this */
236         if ((op->customdata != NULL) &&
237             mesh_bisect_interactive_calc(C, op, em, plane_co, plane_no))
238         {
239                 /* write back to the props */
240                 RNA_property_float_set_array(op->ptr, prop_plane_no, plane_no);
241                 RNA_property_float_set_array(op->ptr, prop_plane_co, plane_co);
242         }
243         /* End Modal */
244         /* -------------------------------------------------------------------- */
245
246
247
248         bm = em->bm;
249
250         invert_m4_m4(imat, obedit->obmat);
251         mul_m4_v3(imat, plane_co);
252         mul_mat3_m4_v3(imat, plane_no);
253
254         EDBM_op_init(em, &bmop, op,
255                      "bisect_plane geom=%hvef plane_co=%v plane_no=%v dist=%f clear_inner=%b clear_outer=%b",
256                      BM_ELEM_SELECT, plane_co, plane_no, thresh, clear_inner, clear_outer);
257         BMO_op_exec(bm, &bmop);
258
259         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
260
261         if (use_fill) {
262                 float normal_fill[3];
263                 BMOperator bmop_fill;
264                 BMOperator bmop_attr;
265
266                 normalize_v3_v3(normal_fill, plane_no);
267                 if (clear_outer == true && clear_inner == false) {
268                         negate_v3(normal_fill);
269                 }
270
271                 /* Fill */
272                 BMO_op_initf(
273                         bm, &bmop_fill, op->flag,
274                         "triangle_fill edges=%S normal=%v use_dissolve=%b",
275                         &bmop, "geom_cut.out", normal_fill, true);
276                 BMO_op_exec(bm, &bmop_fill);
277
278                 /* Copy Attributes */
279                 BMO_op_initf(bm, &bmop_attr, op->flag,
280                              "face_attribute_fill faces=%S use_normals=%b use_data=%b",
281                              &bmop_fill, "geom.out", false, true);
282                 BMO_op_exec(bm, &bmop_attr);
283
284                 BMO_slot_buffer_hflag_enable(bm, bmop_fill.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true);
285
286                 BMO_op_finish(bm, &bmop_attr);
287                 BMO_op_finish(bm, &bmop_fill);
288         }
289
290         BMO_slot_buffer_hflag_enable(bm, bmop.slots_out, "geom_cut.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true);
291
292         if (!EDBM_op_finish(em, &bmop, op, true)) {
293                 return OPERATOR_CANCELLED;
294         }
295         else {
296                 EDBM_update_generic(em, true, true);
297                 EDBM_selectmode_flush(em);
298                 return OPERATOR_FINISHED;
299         }
300 }
301
302
303 void MESH_OT_bisect(struct wmOperatorType *ot)
304 {
305         PropertyRNA *prop;
306
307         /* identifiers */
308         ot->name = "Bisect";
309         ot->description = "Cut geometry along a plane (click-drag to define plane)";
310         ot->idname = "MESH_OT_bisect";
311
312         /* api callbacks */
313         ot->exec = mesh_bisect_exec;
314         ot->invoke = mesh_bisect_invoke;
315         ot->modal = mesh_bisect_modal;
316         ot->cancel = WM_gesture_straightline_cancel;
317         ot->poll = ED_operator_editmesh;
318
319         /* flags */
320         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
321
322
323         prop = RNA_def_float_vector(ot->srna, "plane_co", 3, NULL, -FLT_MAX, FLT_MAX,
324                                     "Plane Point", "A point on the plane", -FLT_MAX, FLT_MAX);
325         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
326         prop = RNA_def_float_vector(ot->srna, "plane_no", 3, NULL, -FLT_MAX, FLT_MAX,
327                                     "Plane Normal", "The direction the plane points", -FLT_MAX, FLT_MAX);
328         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
329
330         RNA_def_boolean(ot->srna, "use_fill", false, "Fill", "Fill in the cut");
331         RNA_def_boolean(ot->srna, "clear_inner", false, "Clear Inner", "Remove geometry behind the plane");
332         RNA_def_boolean(ot->srna, "clear_outer", false, "Clear Outer", "Remove geometry in front of the plane");
333
334         RNA_def_float(ot->srna, "threshold", 0.0001, 0.0, 10.0, "Axis Threshold", "", 0.00001, 0.1);
335
336         WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
337 }