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