Merge branch 'blender2.7' into master.
[blender.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 "BLT_translation.h"
37
38 #include "BKE_global.h"
39 #include "BKE_context.h"
40 #include "BKE_editmesh.h"
41 #include "BKE_layer.h"
42 #include "BKE_report.h"
43
44 #include "RNA_define.h"
45 #include "RNA_access.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "ED_mesh.h"
51 #include "ED_screen.h"
52 #include "ED_view3d.h"
53 #include "ED_gizmo_utils.h"
54
55 #include "UI_resources.h"
56
57 #include "mesh_intern.h"  /* own include */
58
59 #define USE_GIZMO
60
61 #ifdef USE_GIZMO
62 #include "ED_gizmo_library.h"
63 #include "ED_undo.h"
64 #endif
65
66 static int mesh_bisect_exec(bContext *C, wmOperator *op);
67
68 /* -------------------------------------------------------------------- */
69 /* Model Helpers */
70
71 typedef struct {
72         /* modal only */
73
74         /* Aligned with objects array. */
75         struct {
76                 BMBackup mesh;
77                 bool is_valid;
78                 bool is_dirty;
79         } *backup;
80         int backup_len;
81         short gizmo_flag;
82 } BisectData;
83
84 static void mesh_bisect_interactive_calc(
85         bContext *C, wmOperator *op,
86         float plane_co[3], float plane_no[3])
87 {
88         View3D *v3d = CTX_wm_view3d(C);
89         ARegion *ar = CTX_wm_region(C);
90         RegionView3D *rv3d = ar->regiondata;
91
92         int x_start = RNA_int_get(op->ptr, "xstart");
93         int y_start = RNA_int_get(op->ptr, "ystart");
94         int x_end = RNA_int_get(op->ptr, "xend");
95         int y_end = RNA_int_get(op->ptr, "yend");
96
97         /* reference location (some point in front of the view) for finding a point on a plane */
98         const float *co_ref = rv3d->ofs;
99         float co_a_ss[2] = {x_start, y_start}, co_b_ss[2] = {x_end, y_end}, co_delta_ss[2];
100         float co_a[3], co_b[3];
101         const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL);
102
103         /* view vector */
104         ED_view3d_win_to_vector(ar, co_a_ss, co_a);
105
106         /* view delta */
107         sub_v2_v2v2(co_delta_ss, co_a_ss, co_b_ss);
108         ED_view3d_win_to_delta(ar, co_delta_ss, co_b, zfac);
109
110         /* cross both to get a normal */
111         cross_v3_v3v3(plane_no, co_a, co_b);
112         normalize_v3(plane_no);  /* not needed but nicer for user */
113
114         /* point on plane, can use either start or endpoint */
115         ED_view3d_win_to_3d(v3d, ar, co_ref, co_a_ss, plane_co);
116 }
117
118 static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event)
119 {
120         ViewLayer *view_layer = CTX_data_view_layer(C);
121         int valid_objects = 0;
122
123         /* If the properties are set or there is no rv3d,
124          * skip model and exec immediately. */
125         if ((CTX_wm_region_view3d(C) == NULL) ||
126             (RNA_struct_property_is_set(op->ptr, "plane_co") &&
127              RNA_struct_property_is_set(op->ptr, "plane_no")))
128         {
129                 return mesh_bisect_exec(C, op);
130         }
131
132         uint objects_len = 0;
133         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
134         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
135                 Object *obedit = objects[ob_index];
136                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
137
138                 if (em->bm->totedgesel != 0) {
139                         valid_objects++;
140                 }
141         }
142
143         if (valid_objects == 0) {
144                 BKE_report(op->reports, RPT_ERROR, "Selected edges/faces required");
145                 MEM_freeN(objects);
146                 return OPERATOR_CANCELLED;
147         }
148
149         int ret = WM_gesture_straightline_invoke(C, op, event);
150         if (ret & OPERATOR_RUNNING_MODAL) {
151                 View3D *v3d = CTX_wm_view3d(C);
152
153                 wmGesture *gesture = op->customdata;
154                 BisectData *opdata;
155
156                 opdata = MEM_mallocN(sizeof(BisectData), "inset_operator_data");
157                 gesture->userdata = opdata;
158
159                 opdata->backup_len = objects_len;
160                 opdata->backup = MEM_callocN(sizeof(*opdata->backup) * objects_len, __func__);
161
162                 /* Store the mesh backups. */
163                 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
164                         Object *obedit = objects[ob_index];
165                         BMEditMesh *em = BKE_editmesh_from_object(obedit);
166
167                         if (em->bm->totedgesel != 0) {
168                                 opdata->backup[ob_index].is_valid = true;
169                                 opdata->backup[ob_index].mesh = EDBM_redo_state_store(em);
170                         }
171                 }
172
173                 /* Misc other vars. */
174                 G.moving = G_TRANSFORM_EDIT;
175                 opdata->gizmo_flag = v3d->gizmo_flag;
176                 v3d->gizmo_flag = V3D_GIZMO_HIDE;
177
178                 /* Initialize modal callout. */
179                 ED_workspace_status_text(C, IFACE_("LMB: Click and drag to draw cut line"));
180         }
181         MEM_freeN(objects);
182         return ret;
183 }
184
185 static void edbm_bisect_exit(bContext *C, BisectData *opdata)
186 {
187         View3D *v3d = CTX_wm_view3d(C);
188         v3d->gizmo_flag = opdata->gizmo_flag;
189         G.moving = 0;
190
191         for (int ob_index = 0; ob_index < opdata->backup_len; ob_index++) {
192                 if (opdata->backup[ob_index].is_valid) {
193                         EDBM_redo_state_free(&opdata->backup[ob_index].mesh, NULL, false);
194                 }
195         }
196         MEM_freeN(opdata->backup);
197 }
198
199 static int mesh_bisect_modal(bContext *C, wmOperator *op, const wmEvent *event)
200 {
201         wmGesture *gesture = op->customdata;
202         BisectData *opdata = gesture->userdata;
203         BisectData opdata_back = *opdata;  /* annoyance, WM_gesture_straightline_modal, frees */
204         int ret;
205
206         ret = WM_gesture_straightline_modal(C, op, event);
207
208         /* update or clear modal callout */
209         if (event->type == EVT_MODAL_MAP) {
210                 if (event->val == GESTURE_MODAL_BEGIN) {
211                         ED_workspace_status_text(C, IFACE_("LMB: Release to confirm cut line"));
212                 }
213                 else {
214                         ED_workspace_status_text(C, NULL);
215                 }
216         }
217
218         if (ret & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
219                 edbm_bisect_exit(C, &opdata_back);
220
221 #ifdef USE_GIZMO
222                 /* Setup gizmos */
223                 {
224                         View3D *v3d = CTX_wm_view3d(C);
225                         if (v3d && (v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) {
226                                 WM_gizmo_group_type_ensure("MESH_GGT_bisect");
227                         }
228                 }
229 #endif
230         }
231
232         return ret;
233 }
234
235 /* End Model Helpers */
236 /* -------------------------------------------------------------------- */
237
238
239
240 static int mesh_bisect_exec(bContext *C, wmOperator *op)
241 {
242         Scene *scene = CTX_data_scene(C);
243
244         /* both can be NULL, fallbacks values are used */
245         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
246
247         int ret = OPERATOR_CANCELLED;
248
249         float plane_co[3];
250         float plane_no[3];
251         float imat[4][4];
252
253         const float thresh = RNA_float_get(op->ptr, "threshold");
254         const bool use_fill = RNA_boolean_get(op->ptr, "use_fill");
255         const bool clear_inner = RNA_boolean_get(op->ptr, "clear_inner");
256         const bool clear_outer = RNA_boolean_get(op->ptr, "clear_outer");
257
258         PropertyRNA *prop_plane_co;
259         PropertyRNA *prop_plane_no;
260
261         prop_plane_co = RNA_struct_find_property(op->ptr, "plane_co");
262         if (RNA_property_is_set(op->ptr, prop_plane_co)) {
263                 RNA_property_float_get_array(op->ptr, prop_plane_co, plane_co);
264         }
265         else {
266                 copy_v3_v3(plane_co, scene->cursor.location);
267                 RNA_property_float_set_array(op->ptr, prop_plane_co, plane_co);
268         }
269
270         prop_plane_no = RNA_struct_find_property(op->ptr, "plane_no");
271         if (RNA_property_is_set(op->ptr, prop_plane_no)) {
272                 RNA_property_float_get_array(op->ptr, prop_plane_no, plane_no);
273         }
274         else {
275                 if (rv3d) {
276                         copy_v3_v3(plane_no, rv3d->viewinv[1]);
277                 }
278                 else {
279                         /* fallback... */
280                         plane_no[0] = plane_no[1] = 0.0f; plane_no[2] = 1.0f;
281                 }
282                 RNA_property_float_set_array(op->ptr, prop_plane_no, plane_no);
283         }
284
285         wmGesture *gesture = op->customdata;
286         BisectData *opdata = (gesture != NULL) ? gesture->userdata : NULL;
287
288         /* -------------------------------------------------------------------- */
289         /* Modal support */
290         /* Note: keep this isolated, exec can work without this */
291         if (opdata != NULL) {
292                 mesh_bisect_interactive_calc(C, op, plane_co, plane_no);
293                 /* Write back to the props. */
294                 RNA_property_float_set_array(op->ptr, prop_plane_no, plane_no);
295                 RNA_property_float_set_array(op->ptr, prop_plane_co, plane_co);
296         }
297         /* End Modal */
298         /* -------------------------------------------------------------------- */
299
300         uint objects_len = 0;
301         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);
302
303         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
304                 Object *obedit = objects[ob_index];
305                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
306                 BMesh *bm = em->bm;
307
308                 if (opdata != NULL) {
309                         if (opdata->backup[ob_index].is_dirty) {
310                                 EDBM_redo_state_restore(opdata->backup[ob_index].mesh, em, false);
311                                 opdata->backup[ob_index].is_dirty = false;
312                         }
313                 }
314
315                 if (bm->totedgesel == 0) {
316                         continue;
317                 }
318
319                 if (opdata != NULL) {
320                         if (opdata->backup[ob_index].is_valid) {
321                                 opdata->backup[ob_index].is_dirty = true;
322                         }
323                 }
324
325                 float plane_co_local[3];
326                 float plane_no_local[3];
327                 copy_v3_v3(plane_co_local, plane_co);
328                 copy_v3_v3(plane_no_local, plane_no);
329
330                 invert_m4_m4(imat, obedit->obmat);
331                 mul_m4_v3(imat, plane_co_local);
332                 mul_transposed_mat3_m4_v3(obedit->obmat, plane_no_local);
333
334                 BMOperator bmop;
335                 EDBM_op_init(em, &bmop, op,
336                              "bisect_plane geom=%hvef plane_co=%v plane_no=%v dist=%f clear_inner=%b clear_outer=%b",
337                              BM_ELEM_SELECT, plane_co_local, plane_no_local, thresh, clear_inner, clear_outer);
338                 BMO_op_exec(bm, &bmop);
339
340                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
341
342                 if (use_fill) {
343                         float normal_fill[3];
344                         BMOperator bmop_fill;
345                         BMOperator bmop_attr;
346
347                         normalize_v3_v3(normal_fill, plane_no_local);
348                         if (clear_outer == true && clear_inner == false) {
349                                 negate_v3(normal_fill);
350                         }
351
352                         /* Fill */
353                         BMO_op_initf(
354                                     bm, &bmop_fill, 0,
355                                     "triangle_fill edges=%S normal=%v use_dissolve=%b",
356                                     &bmop, "geom_cut.out", normal_fill, true);
357                         BMO_op_exec(bm, &bmop_fill);
358
359                         /* Copy Attributes */
360                         BMO_op_initf(bm, &bmop_attr, 0,
361                                      "face_attribute_fill faces=%S use_normals=%b use_data=%b",
362                                      &bmop_fill, "geom.out", false, true);
363                         BMO_op_exec(bm, &bmop_attr);
364
365                         BMO_slot_buffer_hflag_enable(bm, bmop_fill.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true);
366
367                         BMO_op_finish(bm, &bmop_attr);
368                         BMO_op_finish(bm, &bmop_fill);
369                 }
370
371                 BMO_slot_buffer_hflag_enable(bm, bmop.slots_out, "geom_cut.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true);
372
373                 if (EDBM_op_finish(em, &bmop, op, true)) {
374                         EDBM_update_generic(em, true, true);
375                         EDBM_selectmode_flush(em);
376                         ret = OPERATOR_FINISHED;
377                 }
378         }
379         MEM_freeN(objects);
380         return ret;
381 }
382
383 #ifdef USE_GIZMO
384 static void MESH_GGT_bisect(struct wmGizmoGroupType *gzgt);
385 #endif
386
387 void MESH_OT_bisect(struct wmOperatorType *ot)
388 {
389         PropertyRNA *prop;
390
391         /* identifiers */
392         ot->name = "Bisect";
393         ot->description = "Cut geometry along a plane (click-drag to define plane)";
394         ot->idname = "MESH_OT_bisect";
395
396         /* api callbacks */
397         ot->exec = mesh_bisect_exec;
398         ot->invoke = mesh_bisect_invoke;
399         ot->modal = mesh_bisect_modal;
400         ot->cancel = WM_gesture_straightline_cancel;
401         ot->poll = ED_operator_editmesh;
402
403         /* flags */
404         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
405
406
407         prop = RNA_def_float_vector(ot->srna, "plane_co", 3, NULL, -1e12f, 1e12f,
408                                     "Plane Point", "A point on the plane", -1e4f, 1e4f);
409         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
410         prop = RNA_def_float_vector(ot->srna, "plane_no", 3, NULL, -1.0f, 1.0f,
411                                     "Plane Normal", "The direction the plane points", -1.0f, 1.0f);
412         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
413
414         RNA_def_boolean(ot->srna, "use_fill", false, "Fill", "Fill in the cut");
415         RNA_def_boolean(ot->srna, "clear_inner", false, "Clear Inner", "Remove geometry behind the plane");
416         RNA_def_boolean(ot->srna, "clear_outer", false, "Clear Outer", "Remove geometry in front of the plane");
417
418         RNA_def_float(ot->srna, "threshold", 0.0001, 0.0, 10.0, "Axis Threshold",
419                       "Preserves the existing geometry along the cut plane", 0.00001, 0.1);
420
421         WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
422
423 #ifdef USE_GIZMO
424         WM_gizmogrouptype_append(MESH_GGT_bisect);
425 #endif
426 }
427
428
429 #ifdef USE_GIZMO
430
431 /* -------------------------------------------------------------------- */
432
433 /** \name Bisect Gizmo
434  * \{ */
435
436 typedef struct GizmoGroup {
437         /* Arrow to change plane depth. */
438         struct wmGizmo *translate_z;
439         /* Translate XYZ */
440         struct wmGizmo *translate_c;
441         /* For grabbing the gizmo and moving freely. */
442         struct wmGizmo *rotate_c;
443
444         /* We could store more vars here! */
445         struct {
446                 bContext *context;
447                 wmOperator *op;
448                 PropertyRNA *prop_plane_co;
449                 PropertyRNA *prop_plane_no;
450
451                 float rotate_axis[3];
452                 float rotate_up[3];
453         } data;
454 } GizmoGroup;
455
456 /**
457  * XXX. calling redo from property updates is not great.
458  * This is needed because changing the RNA doesn't cause a redo
459  * and we're not using operator UI which does just this.
460  */
461 static void gizmo_bisect_exec(GizmoGroup *ggd)
462 {
463         wmOperator *op = ggd->data.op;
464         if (op == WM_operator_last_redo((bContext *)ggd->data.context)) {
465                 ED_undo_operator_repeat((bContext *)ggd->data.context, op);
466         }
467 }
468
469 static void gizmo_mesh_bisect_update_from_op(GizmoGroup *ggd)
470 {
471         wmOperator *op = ggd->data.op;
472
473         float plane_co[3], plane_no[3];
474
475         RNA_property_float_get_array(op->ptr, ggd->data.prop_plane_co, plane_co);
476         RNA_property_float_get_array(op->ptr, ggd->data.prop_plane_no, plane_no);
477
478         WM_gizmo_set_matrix_location(ggd->translate_z, plane_co);
479         WM_gizmo_set_matrix_location(ggd->rotate_c, plane_co);
480         /* translate_c location comes from the property. */
481
482         WM_gizmo_set_matrix_rotation_from_z_axis(ggd->translate_z, plane_no);
483
484         WM_gizmo_set_scale(ggd->translate_c, 0.2);
485
486         RegionView3D *rv3d = ED_view3d_context_rv3d(ggd->data.context);
487         if (rv3d) {
488                 normalize_v3_v3(ggd->data.rotate_axis, rv3d->viewinv[2]);
489                 normalize_v3_v3(ggd->data.rotate_up, rv3d->viewinv[1]);
490
491                 /* ensure its orthogonal */
492                 project_plane_normalized_v3_v3v3(ggd->data.rotate_up, ggd->data.rotate_up, ggd->data.rotate_axis);
493                 normalize_v3(ggd->data.rotate_up);
494
495                 WM_gizmo_set_matrix_rotation_from_z_axis(ggd->translate_c, plane_no);
496
497                 float plane_no_cross[3];
498                 cross_v3_v3v3(plane_no_cross, plane_no, ggd->data.rotate_axis);
499
500                 WM_gizmo_set_matrix_offset_rotation_from_yz_axis(ggd->rotate_c, plane_no_cross, ggd->data.rotate_axis);
501                 RNA_enum_set(ggd->rotate_c->ptr, "draw_options",
502                              ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_MIRROR |
503                              ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_START_Y);
504         }
505 }
506
507 /* depth callbacks */
508 static void gizmo_bisect_prop_depth_get(
509         const wmGizmo *gz, wmGizmoProperty *gz_prop,
510         void *value_p)
511 {
512         GizmoGroup *ggd = gz->parent_gzgroup->customdata;
513         wmOperator *op = ggd->data.op;
514         float *value = value_p;
515
516         BLI_assert(gz_prop->type->array_length == 1);
517         UNUSED_VARS_NDEBUG(gz_prop);
518
519         float plane_co[3], plane_no[3];
520         RNA_property_float_get_array(op->ptr, ggd->data.prop_plane_co, plane_co);
521         RNA_property_float_get_array(op->ptr, ggd->data.prop_plane_no, plane_no);
522
523         value[0] = dot_v3v3(plane_no, plane_co) - dot_v3v3(plane_no, gz->matrix_basis[3]);
524 }
525
526 static void gizmo_bisect_prop_depth_set(
527         const wmGizmo *gz, wmGizmoProperty *gz_prop,
528         const void *value_p)
529 {
530         GizmoGroup *ggd = gz->parent_gzgroup->customdata;
531         wmOperator *op = ggd->data.op;
532         const float *value = value_p;
533
534         BLI_assert(gz_prop->type->array_length == 1);
535         UNUSED_VARS_NDEBUG(gz_prop);
536
537         float plane_co[3], plane[4];
538         RNA_property_float_get_array(op->ptr, ggd->data.prop_plane_co, plane_co);
539         RNA_property_float_get_array(op->ptr, ggd->data.prop_plane_no, plane);
540         normalize_v3(plane);
541
542         plane[3] = -value[0] - dot_v3v3(plane, gz->matrix_basis[3]);
543
544         /* Keep our location, may be offset simply to be inside the viewport. */
545         closest_to_plane_normalized_v3(plane_co, plane, plane_co);
546
547         RNA_property_float_set_array(op->ptr, ggd->data.prop_plane_co, plane_co);
548
549         gizmo_bisect_exec(ggd);
550 }
551
552 /* translate callbacks */
553 static void gizmo_bisect_prop_translate_get(
554         const wmGizmo *gz, wmGizmoProperty *gz_prop,
555         void *value_p)
556 {
557         GizmoGroup *ggd = gz->parent_gzgroup->customdata;
558         wmOperator *op = ggd->data.op;
559
560         BLI_assert(gz_prop->type->array_length == 3);
561         UNUSED_VARS_NDEBUG(gz_prop);
562
563         RNA_property_float_get_array(op->ptr, ggd->data.prop_plane_co, value_p);
564 }
565
566 static void gizmo_bisect_prop_translate_set(
567         const wmGizmo *gz, wmGizmoProperty *gz_prop,
568         const void *value_p)
569 {
570         GizmoGroup *ggd = gz->parent_gzgroup->customdata;
571         wmOperator *op = ggd->data.op;
572
573         BLI_assert(gz_prop->type->array_length == 3);
574         UNUSED_VARS_NDEBUG(gz_prop);
575
576         RNA_property_float_set_array(op->ptr, ggd->data.prop_plane_co, value_p);
577
578         gizmo_bisect_exec(ggd);
579 }
580
581 /* angle callbacks */
582 static void gizmo_bisect_prop_angle_get(
583         const wmGizmo *gz, wmGizmoProperty *gz_prop,
584         void *value_p)
585 {
586         GizmoGroup *ggd = gz->parent_gzgroup->customdata;
587         wmOperator *op = ggd->data.op;
588         float *value = value_p;
589
590         BLI_assert(gz_prop->type->array_length == 1);
591         UNUSED_VARS_NDEBUG(gz_prop);
592
593         float plane_no[4];
594         RNA_property_float_get_array(op->ptr, ggd->data.prop_plane_no, plane_no);
595         normalize_v3(plane_no);
596
597         float plane_no_proj[3];
598         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, ggd->data.rotate_axis);
599
600         if (!is_zero_v3(plane_no_proj)) {
601                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, ggd->data.rotate_up, ggd->data.rotate_axis);
602                 value[0] = angle;
603         }
604         else {
605                 value[0] = 0.0f;
606         }
607 }
608
609 static void gizmo_bisect_prop_angle_set(
610         const wmGizmo *gz, wmGizmoProperty *gz_prop,
611         const void *value_p)
612 {
613         GizmoGroup *ggd = gz->parent_gzgroup->customdata;
614         wmOperator *op = ggd->data.op;
615         const float *value = value_p;
616
617         BLI_assert(gz_prop->type->array_length == 1);
618         UNUSED_VARS_NDEBUG(gz_prop);
619
620         float plane_no[4];
621         RNA_property_float_get_array(op->ptr, ggd->data.prop_plane_no, plane_no);
622         normalize_v3(plane_no);
623
624         float plane_no_proj[3];
625         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, ggd->data.rotate_axis);
626
627         if (!is_zero_v3(plane_no_proj)) {
628                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, ggd->data.rotate_up, ggd->data.rotate_axis);
629                 const float angle_delta = angle - angle_compat_rad(value[0], angle);
630                 if (angle_delta != 0.0f) {
631                         float mat[3][3];
632                         axis_angle_normalized_to_mat3(mat, ggd->data.rotate_axis, angle_delta);
633                         mul_m3_v3(mat, plane_no);
634
635                         /* re-normalize - seems acceptable */
636                         RNA_property_float_set_array(op->ptr, ggd->data.prop_plane_no, plane_no);
637
638                         gizmo_bisect_exec(ggd);
639                 }
640         }
641 }
642
643 static bool gizmo_mesh_bisect_poll(const bContext *C, wmGizmoGroupType *gzgt)
644 {
645         return ED_gizmo_poll_or_unlink_delayed_from_operator(C, gzgt, "MESH_OT_bisect");
646 }
647
648 static void gizmo_mesh_bisect_setup(const bContext *C, wmGizmoGroup *gzgroup)
649 {
650         wmOperator *op = WM_operator_last_redo(C);
651
652         if (op == NULL || !STREQ(op->type->idname, "MESH_OT_bisect")) {
653                 return;
654         }
655
656         struct GizmoGroup *ggd = MEM_callocN(sizeof(GizmoGroup), __func__);
657         gzgroup->customdata = ggd;
658
659         const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
660         const wmGizmoType *gzt_move = WM_gizmotype_find("GIZMO_GT_move_3d", true);
661         const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
662
663         ggd->translate_z = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
664         ggd->translate_c = WM_gizmo_new_ptr(gzt_move, gzgroup, NULL);
665         ggd->rotate_c = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL);
666
667         UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, ggd->translate_z->color);
668         UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, ggd->translate_c->color);
669         UI_GetThemeColor3fv(TH_GIZMO_SECONDARY, ggd->rotate_c->color);
670
671         RNA_enum_set(ggd->translate_z->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_NORMAL);
672         RNA_enum_set(ggd->translate_c->ptr, "draw_style", ED_GIZMO_MOVE_STYLE_RING_2D);
673
674         WM_gizmo_set_flag(ggd->translate_c, WM_GIZMO_DRAW_VALUE, true);
675         WM_gizmo_set_flag(ggd->rotate_c, WM_GIZMO_DRAW_VALUE, true);
676
677         {
678                 ggd->data.context = (bContext *)C;
679                 ggd->data.op = op;
680                 ggd->data.prop_plane_co = RNA_struct_find_property(op->ptr, "plane_co");
681                 ggd->data.prop_plane_no = RNA_struct_find_property(op->ptr, "plane_no");
682         }
683
684         gizmo_mesh_bisect_update_from_op(ggd);
685
686         /* Setup property callbacks */
687         {
688                 WM_gizmo_target_property_def_func(
689                         ggd->translate_z, "offset",
690                         &(const struct wmGizmoPropertyFnParams) {
691                             .value_get_fn = gizmo_bisect_prop_depth_get,
692                             .value_set_fn = gizmo_bisect_prop_depth_set,
693                             .range_get_fn = NULL,
694                             .user_data = NULL,
695                         });
696
697                 WM_gizmo_target_property_def_func(
698                         ggd->translate_c, "offset",
699                         &(const struct wmGizmoPropertyFnParams) {
700                             .value_get_fn = gizmo_bisect_prop_translate_get,
701                             .value_set_fn = gizmo_bisect_prop_translate_set,
702                             .range_get_fn = NULL,
703                             .user_data = NULL,
704                         });
705
706                 WM_gizmo_target_property_def_func(
707                         ggd->rotate_c, "offset",
708                         &(const struct wmGizmoPropertyFnParams) {
709                             .value_get_fn = gizmo_bisect_prop_angle_get,
710                             .value_set_fn = gizmo_bisect_prop_angle_set,
711                             .range_get_fn = NULL,
712                             .user_data = NULL,
713                         });
714         }
715 }
716
717 static void gizmo_mesh_bisect_draw_prepare(
718         const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
719 {
720         GizmoGroup *ggd = gzgroup->customdata;
721         if (ggd->data.op->next) {
722                 ggd->data.op = WM_operator_last_redo((bContext *)ggd->data.context);
723         }
724         gizmo_mesh_bisect_update_from_op(ggd);
725 }
726
727 static void MESH_GGT_bisect(struct wmGizmoGroupType *gzgt)
728 {
729         gzgt->name = "Mesh Bisect";
730         gzgt->idname = "MESH_GGT_bisect";
731
732         gzgt->flag = WM_GIZMOGROUPTYPE_3D;
733
734         gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
735         gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
736
737         gzgt->poll = gizmo_mesh_bisect_poll;
738         gzgt->setup = gizmo_mesh_bisect_setup;
739         gzgt->draw_prepare = gizmo_mesh_bisect_draw_prepare;
740 }
741
742 /** \} */
743
744 #endif  /* USE_GIZMO */