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