execute bisect immediately (without starting modal input) if the plane is already...
[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 static int mesh_bisect_exec(bContext *C, wmOperator *op);
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         normalize_v3(plane_no);  /* not needed but nicer for user */
98
99         /* point on plane, can use either start or endpoint */
100         ED_view3d_win_to_3d(ar, co_ref, co_a_ss, plane_co);
101
102         if (opdata->is_first == false)
103                 EDBM_redo_state_restore(opdata->mesh_backup, em, false);
104
105         opdata->is_first = false;
106
107         return true;
108 }
109
110 static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event)
111 {
112         int ret;
113
114         /* if the properties are set or there is no rv3d,
115          * skip model and exec immediately */
116
117         if ((CTX_wm_region_view3d(C) == NULL) ||
118             (RNA_struct_property_is_set(op->ptr, "plane_co") &&
119              RNA_struct_property_is_set(op->ptr, "plane_no")))
120         {
121                 return mesh_bisect_exec(C, op);
122         }
123
124         ret = WM_gesture_straightline_invoke(C, op, event);
125         if (ret & OPERATOR_RUNNING_MODAL) {
126                 View3D *v3d = CTX_wm_view3d(C);
127
128                 wmGesture *gesture = op->customdata;
129                 BisectData *opdata;
130
131                 Object *obedit = CTX_data_edit_object(C);
132                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
133
134                 opdata = MEM_mallocN(sizeof(BisectData), "inset_operator_data");
135                 opdata->mesh_backup = EDBM_redo_state_store(em);
136                 opdata->is_first = true;
137                 gesture->userdata = opdata;
138
139                 /* misc other vars */
140                 G.moving = G_TRANSFORM_EDIT;
141                 opdata->twtype = v3d->twtype;
142                 v3d->twtype = 0;
143         }
144         return ret;
145 }
146
147 static void edbm_bisect_exit(bContext *C, BisectData *opdata)
148 {
149         View3D *v3d = CTX_wm_view3d(C);
150         EDBM_redo_state_free(&opdata->mesh_backup, NULL, false);
151         v3d->twtype = opdata->twtype;
152         G.moving = 0;
153 }
154
155 static int mesh_bisect_modal(bContext *C, wmOperator *op, const wmEvent *event)
156 {
157         wmGesture *gesture = op->customdata;
158         BisectData *opdata = gesture->userdata;
159         BisectData opdata_back = *opdata;  /* annoyance, WM_gesture_straightline_modal, frees */
160         int ret;
161
162         ret = WM_gesture_straightline_modal(C, op, event);
163
164         if (ret & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
165                 edbm_bisect_exit(C, &opdata_back);
166         }
167
168         return ret;
169 }
170
171 /* End Model Helpers */
172 /* -------------------------------------------------------------------- */
173
174
175
176 static int mesh_bisect_exec(bContext *C, wmOperator *op)
177 {
178         Scene *scene = CTX_data_scene(C);
179
180         /* both can be NULL, fallbacks values are used */
181         View3D *v3d = CTX_wm_view3d(C);
182         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
183
184         Object *obedit = CTX_data_edit_object(C);
185         BMEditMesh *em = BKE_editmesh_from_object(obedit);
186         BMesh *bm;
187         BMOperator bmop;
188         float plane_co[3];
189         float plane_no[3];
190         float imat[4][4];
191
192         const float thresh = RNA_float_get(op->ptr, "threshold");
193         const bool use_fill = RNA_boolean_get(op->ptr, "use_fill");
194         const bool clear_inner = RNA_boolean_get(op->ptr, "clear_inner");
195         const bool clear_outer = RNA_boolean_get(op->ptr, "clear_outer");
196
197         PropertyRNA *prop_plane_co;
198         PropertyRNA *prop_plane_no;
199
200         prop_plane_co = RNA_struct_find_property(op->ptr, "plane_co");
201         if (RNA_property_is_set(op->ptr, prop_plane_co)) {
202                 RNA_property_float_get_array(op->ptr, prop_plane_co, plane_co);
203         }
204         else {
205                 copy_v3_v3(plane_co, give_cursor(scene, v3d));
206                 RNA_property_float_set_array(op->ptr, prop_plane_co, plane_co);
207         }
208
209         prop_plane_no = RNA_struct_find_property(op->ptr, "plane_no");
210         if (RNA_property_is_set(op->ptr, prop_plane_no)) {
211                 RNA_property_float_get_array(op->ptr, prop_plane_no, plane_no);
212         }
213         else {
214                 if (rv3d) {
215                         copy_v3_v3(plane_no, rv3d->viewinv[1]);
216                 }
217                 else {
218                         /* fallback... */
219                         plane_no[0] = plane_no[1] = 0.0f; plane_no[2] = 1.0f;
220                 }
221                 RNA_property_float_set_array(op->ptr, prop_plane_no, plane_no);
222         }
223
224
225
226         /* -------------------------------------------------------------------- */
227         /* Modal support */
228         /* Note: keep this isolated, exec can work wihout this */
229         if ((op->customdata != NULL) &&
230             mesh_bisect_interactive_calc(C, op, em, plane_co, plane_no))
231         {
232                 /* write back to the props */
233                 RNA_property_float_set_array(op->ptr, prop_plane_no, plane_no);
234                 RNA_property_float_set_array(op->ptr, prop_plane_co, plane_co);
235         }
236         /* End Modal */
237         /* -------------------------------------------------------------------- */
238
239
240
241         bm = em->bm;
242
243         invert_m4_m4(imat, obedit->obmat);
244         mul_m4_v3(imat, plane_co);
245         mul_mat3_m4_v3(imat, plane_no);
246
247         EDBM_op_init(em, &bmop, op,
248                      "bisect_plane geom=%hvef plane_co=%v plane_no=%v dist=%f clear_inner=%b clear_outer=%b",
249                      BM_ELEM_SELECT, plane_co, plane_no, thresh, clear_inner, clear_outer);
250         BMO_op_exec(bm, &bmop);
251
252         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
253
254         if (use_fill) {
255                 float normal_fill[3];
256                 BMOperator bmop_fill;
257                 BMOperator bmop_attr;
258
259                 normalize_v3_v3(normal_fill, plane_no);
260                 if (clear_outer == true && clear_inner == false) {
261                         negate_v3(normal_fill);
262                 }
263
264                 /* Fill */
265                 BMO_op_initf(
266                         bm, &bmop_fill, op->flag,
267                         "triangle_fill edges=%S normal=%v use_dissolve=%b",
268                         &bmop, "geom_cut.out", normal_fill, true);
269                 BMO_op_exec(bm, &bmop_fill);
270
271                 /* Copy Attributes */
272                 BMO_op_initf(bm, &bmop_attr, op->flag,
273                              "face_attribute_fill faces=%S use_normals=%b use_data=%b",
274                              &bmop_fill, "geom.out", false, true);
275                 BMO_op_exec(bm, &bmop_attr);
276
277                 BMO_slot_buffer_hflag_enable(bm, bmop_fill.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true);
278
279                 BMO_op_finish(bm, &bmop_attr);
280                 BMO_op_finish(bm, &bmop_fill);
281         }
282
283         BMO_slot_buffer_hflag_enable(bm, bmop.slots_out, "geom_cut.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true);
284
285         if (!EDBM_op_finish(em, &bmop, op, true)) {
286                 return OPERATOR_CANCELLED;
287         }
288         else {
289                 EDBM_update_generic(em, true, true);
290                 EDBM_selectmode_flush(em);
291                 return OPERATOR_FINISHED;
292         }
293 }
294
295
296 void MESH_OT_bisect(struct wmOperatorType *ot)
297 {
298         PropertyRNA *prop;
299
300         /* identifiers */
301         ot->name = "Bisect";
302         ot->description = "Enforce symmetry (both form and topological) across an axis";
303         ot->idname = "MESH_OT_bisect";
304
305         /* api callbacks */
306         ot->exec = mesh_bisect_exec;
307         ot->invoke = mesh_bisect_invoke;
308         ot->modal = mesh_bisect_modal;
309         ot->cancel = WM_gesture_straightline_cancel;
310         ot->poll = ED_operator_editmesh;
311
312         /* flags */
313         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
314
315
316         prop = RNA_def_float_vector(ot->srna, "plane_co", 3, NULL, -FLT_MAX, FLT_MAX,
317                                     "Plane Point", "A point on the plane", -FLT_MAX, FLT_MAX);
318         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
319         prop = RNA_def_float_vector(ot->srna, "plane_no", 3, NULL, -FLT_MAX, FLT_MAX,
320                                     "Plane Normal", "The direction the plane points", -FLT_MAX, FLT_MAX);
321         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
322
323         RNA_def_boolean(ot->srna, "use_fill", false, "Fill", "Fill in the cut");
324         RNA_def_boolean(ot->srna, "clear_inner", false, "Clear Inner", "Remove geometry behind the plane");
325         RNA_def_boolean(ot->srna, "clear_outer", false, "Clear Outer", "Remove geometry infront of the plane");
326
327         RNA_def_float(ot->srna, "threshold", 0.0001, 0.0, 10.0, "Axis Threshold", "", 0.00001, 0.1);
328
329         WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
330 }