Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / mesh / editmesh_extrude_spin_gizmo.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  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/mesh/editmesh_extrude_spin_gizmo.c
22  *  \ingroup edmesh
23  */
24
25 #include "BLI_math.h"
26
27 #include "BKE_context.h"
28
29 #include "RNA_access.h"
30
31 #include "WM_api.h"
32 #include "WM_types.h"
33 #include "WM_message.h"
34
35 #include "ED_gizmo_utils.h"
36 #include "ED_screen.h"
37 #include "ED_view3d.h"
38
39 #include "UI_resources.h"
40
41 #include "MEM_guardedalloc.h"
42
43 #include "mesh_intern.h"  /* own include */
44
45 #include "ED_transform.h"
46
47 #include "ED_gizmo_library.h"
48 #include "ED_undo.h"
49
50
51 /* -------------------------------------------------------------------- */
52 /** \name Spin Tool Gizmo
53  * \{ */
54
55 typedef struct GizmoGroupData_SpinInit {
56         struct {
57                 wmGizmo *xyz_view[4];
58         } gizmos;
59
60         /* Only for view orientation. */
61         struct {
62                 float viewinv_m3[3][3];
63         } prev;
64
65         /* We could store more vars here! */
66         struct {
67                 wmOperatorType *ot_spin;
68         } data;
69 } GizmoGroupData_SpinInit;
70
71 static void gizmo_mesh_spin_init_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
72 {
73         /* alpha values for normal/highlighted states */
74         const float alpha = 0.6f;
75         const float alpha_hi = 1.0f;
76
77         GizmoGroupData_SpinInit *ggd = MEM_callocN(sizeof(*ggd), __func__);
78         gzgroup->customdata = ggd;
79         const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
80
81         for (int i = 0; i < ARRAY_SIZE(ggd->gizmos.xyz_view); i++) {
82                 wmGizmo *gz = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL);
83                 UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, gz->color);
84                 WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_VALUE, true);
85                 ggd->gizmos.xyz_view[i] = gz;
86         }
87
88         for (int i = 0; i < 3; i++) {
89                 wmGizmo *gz = ggd->gizmos.xyz_view[i];
90                 RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
91                 WM_gizmo_set_line_width(gz, 3.0f);
92                 float color[4];
93                 UI_GetThemeColor3fv(TH_AXIS_X + i, color);
94                 color[3] = alpha;
95                 WM_gizmo_set_color(gz, color);
96                 color[3] = alpha_hi;
97                 WM_gizmo_set_color_highlight(gz, color);
98         }
99
100         {
101                 wmGizmo *gz = ggd->gizmos.xyz_view[3];
102                 WM_gizmo_set_line_width(gz, 2.0f);
103                 float color[4];
104                 copy_v3_fl(color, 1.0f);
105                 color[3] = alpha;
106                 WM_gizmo_set_color(gz, color);
107                 color[3] = alpha_hi;
108                 WM_gizmo_set_color_highlight(gz, color);
109         }
110
111         WM_gizmo_set_scale(ggd->gizmos.xyz_view[3], 1.2f);
112
113         ggd->data.ot_spin = WM_operatortype_find("MESH_OT_spin", true);
114 }
115
116 static void gizmo_mesh_spin_init_refresh(const bContext *C, wmGizmoGroup *gzgroup);
117
118 static void gizmo_mesh_spin_init_refresh_axis_orientation(
119         wmGizmoGroup *gzgroup,
120         int axis_index, const float axis_vec[3])
121 {
122         GizmoGroupData_SpinInit *ggd = gzgroup->customdata;
123         wmGizmo *gz = ggd->gizmos.xyz_view[axis_index];
124         WM_gizmo_set_matrix_rotation_from_z_axis(gz, axis_vec);
125
126         PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ggd->data.ot_spin, NULL);
127         RNA_float_set_array(ptr, "axis", axis_vec);
128 }
129
130
131 static void gizmo_mesh_spin_init_draw_prepare(
132         const bContext *C, wmGizmoGroup *gzgroup)
133 {
134         GizmoGroupData_SpinInit *ggd = gzgroup->customdata;
135         RegionView3D *rv3d = CTX_wm_region_view3d(C);
136         float viewinv_m3[3][3];
137         copy_m3_m4(viewinv_m3, rv3d->viewinv);
138
139         {
140                 Scene *scene = CTX_data_scene(C);
141                 switch (scene->orientation_type) {
142                         case V3D_MANIP_VIEW:
143                         {
144                                 if (!equals_m3m3(viewinv_m3, ggd->prev.viewinv_m3)) {
145                                         /* Take care calling refresh from draw_prepare,
146                                          * this should be OK because it's only adjusting the cage orientation. */
147                                         gizmo_mesh_spin_init_refresh(C, gzgroup);
148                                 }
149                                 break;
150                         }
151                 }
152         }
153
154         /* Refresh handled above when using view orientation. */
155         if (!equals_m3m3(viewinv_m3, ggd->prev.viewinv_m3)) {
156                 gizmo_mesh_spin_init_refresh_axis_orientation(gzgroup, 3, rv3d->viewinv[2]);
157                 copy_m3_m4(ggd->prev.viewinv_m3, rv3d->viewinv);
158         }
159 }
160
161 static void gizmo_mesh_spin_init_refresh(const bContext *C, wmGizmoGroup *gzgroup)
162 {
163         GizmoGroupData_SpinInit *ggd = gzgroup->customdata;
164         RegionView3D *rv3d = ED_view3d_context_rv3d((bContext *)C);
165
166         {
167                 Scene *scene = CTX_data_scene(C);
168                 View3D *v3d = CTX_wm_view3d(C);
169                 const View3DCursor *cursor = ED_view3d_cursor3d_get(scene, v3d);
170                 for (int i = 0; i < ARRAY_SIZE(ggd->gizmos.xyz_view); i++) {
171                         wmGizmo *gz = ggd->gizmos.xyz_view[i];
172                         WM_gizmo_set_matrix_location(gz, cursor->location);
173                 }
174         }
175
176         float mat[3][3];
177         ED_transform_calc_orientation_from_type(C, mat);
178         for (int i = 0; i < 3; i++) {
179                 gizmo_mesh_spin_init_refresh_axis_orientation(gzgroup, i, mat[i]);
180         }
181
182         {
183                 gizmo_mesh_spin_init_refresh_axis_orientation(gzgroup, 3, rv3d->viewinv[2]);
184         }
185
186         /* Needed to test view orientation changes. */
187         copy_m3_m4(ggd->prev.viewinv_m3, rv3d->viewinv);
188 }
189
190
191 static void gizmo_mesh_spin_init_message_subscribe(
192         const bContext *C, wmGizmoGroup *gzgroup, struct wmMsgBus *mbus)
193 {
194         Scene *scene = CTX_data_scene(C);
195         ARegion *ar = CTX_wm_region(C);
196
197         /* Subscribe to view properties */
198         wmMsgSubscribeValue msg_sub_value_gz_tag_refresh = {
199                 .owner = ar,
200                 .user_data = gzgroup->parent_gzmap,
201                 .notify = WM_gizmo_do_msg_notify_tag_refresh,
202         };
203
204         PointerRNA scene_ptr;
205         RNA_id_pointer_create(&scene->id, &scene_ptr);
206
207         {
208                 extern PropertyRNA rna_Scene_transform_orientation;
209                 extern PropertyRNA rna_Scene_cursor_location;
210                 const PropertyRNA *props[] = {
211                         &rna_Scene_transform_orientation,
212                         &rna_Scene_cursor_location,
213                 };
214                 for (int i = 0; i < ARRAY_SIZE(props); i++) {
215                         WM_msg_subscribe_rna(mbus, &scene_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
216                 }
217         }
218 }
219
220 void MESH_GGT_spin(struct wmGizmoGroupType *gzgt)
221 {
222         gzgt->name = "Mesh Spin Init";
223         gzgt->idname = "MESH_GGT_spin";
224
225         gzgt->flag = WM_GIZMOGROUPTYPE_3D;
226
227         gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
228         gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
229
230         gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool;
231         gzgt->setup = gizmo_mesh_spin_init_setup;
232         gzgt->refresh = gizmo_mesh_spin_init_refresh;
233         gzgt->message_subscribe = gizmo_mesh_spin_init_message_subscribe;
234         gzgt->draw_prepare = gizmo_mesh_spin_init_draw_prepare;
235 }
236
237 /** \} */
238
239 /* -------------------------------------------------------------------- */
240 /** \name Spin Redo Gizmo
241  * \{ */
242
243 /**
244  * Don't show these for now, because they overlay the tools #MESH_GGT_spin_redo gizmos.
245  * this means we cant rotate the currently running gizmo.
246  */
247 // #define USE_EXTRA_CONTROLS
248 /**
249  * Orient the dial so the 'arc' starts where the mouse cursor is,
250  * this is simply to keep the gizmo displaying where the cursor starts.
251  * It's not needed for practical functionality.
252  */
253 #define USE_ANGLE_Z_ORIENT
254
255 typedef struct GizmoGroupData_SpinRedo {
256         /* Arrow to change plane depth. */
257         struct wmGizmo *translate_z;
258         /* Translate XYZ */
259         struct wmGizmo *translate_c;
260         /* For grabbing the gizmo and moving freely. */
261         struct wmGizmo *rotate_c;
262         /* Spin angle */
263         struct wmGizmo *angle_z;
264
265         /* We could store more vars here! */
266         struct {
267                 bContext *context;
268                 wmOperatorType *ot;
269                 wmOperator *op;
270                 PropertyRNA *prop_axis_co;
271                 PropertyRNA *prop_axis_no;
272                 PropertyRNA *prop_angle;
273
274                 float rotate_axis[3];
275                 float rotate_up[3];
276 #ifdef USE_ANGLE_Z_ORIENT
277                 float orient_axis[3];
278 #endif
279         } data;
280 } GizmoGroupData_SpinRedo;
281
282 /**
283  * XXX. calling redo from property updates is not great.
284  * This is needed because changing the RNA doesn't cause a redo
285  * and we're not using operator UI which does just this.
286  */
287 static void gizmo_spin_exec(GizmoGroupData_SpinRedo *ggd)
288 {
289         wmOperator *op = ggd->data.op;
290         if (op == WM_operator_last_redo((bContext *)ggd->data.context)) {
291                 ED_undo_operator_repeat((bContext *)ggd->data.context, op);
292         }
293 }
294
295 static void gizmo_mesh_spin_redo_update_from_op(GizmoGroupData_SpinRedo *ggd)
296 {
297         wmOperator *op = ggd->data.op;
298
299         float plane_co[3], plane_no[3];
300
301         RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_co, plane_co);
302         RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_no, plane_no);
303
304         WM_gizmo_set_matrix_location(ggd->translate_z, plane_co);
305         WM_gizmo_set_matrix_location(ggd->rotate_c, plane_co);
306         WM_gizmo_set_matrix_location(ggd->angle_z, plane_co);
307         /* translate_c location comes from the property. */
308
309         WM_gizmo_set_matrix_rotation_from_z_axis(ggd->translate_z, plane_no);
310 #ifdef USE_ANGLE_Z_ORIENT
311         {
312                 float plane_tan[3];
313                 project_plane_normalized_v3_v3v3(plane_tan, ggd->data.orient_axis, plane_no);
314                 if (normalize_v3(plane_tan) != 0.0f) {
315                         WM_gizmo_set_matrix_rotation_from_yz_axis(ggd->angle_z, plane_tan, plane_no);
316                 }
317                 else {
318                         WM_gizmo_set_matrix_rotation_from_z_axis(ggd->angle_z, plane_no);
319                 }
320         }
321 #else
322         WM_gizmo_set_matrix_rotation_from_z_axis(ggd->angle_z, plane_no);
323 #endif
324
325         WM_gizmo_set_scale(ggd->translate_c, 0.2);
326
327         WM_gizmo_set_scale(ggd->angle_z, 2.0f);
328         WM_gizmo_set_line_width(ggd->angle_z, 1.0f);
329
330         RegionView3D *rv3d = ED_view3d_context_rv3d(ggd->data.context);
331         if (rv3d) {
332                 normalize_v3_v3(ggd->data.rotate_axis, rv3d->viewinv[2]);
333                 normalize_v3_v3(ggd->data.rotate_up, rv3d->viewinv[1]);
334
335                 /* ensure its orthogonal */
336                 project_plane_normalized_v3_v3v3(ggd->data.rotate_up, ggd->data.rotate_up, ggd->data.rotate_axis);
337                 normalize_v3(ggd->data.rotate_up);
338
339                 WM_gizmo_set_matrix_rotation_from_z_axis(ggd->translate_c, plane_no);
340                 WM_gizmo_set_matrix_rotation_from_yz_axis(ggd->rotate_c, plane_no, ggd->data.rotate_axis);
341
342                 /* show the axis instead of mouse cursor */
343                 RNA_enum_set(ggd->rotate_c->ptr, "draw_options",
344                              ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_MIRROR |
345                              ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_START_Y);
346
347         }
348 }
349
350 /* depth callbacks */
351 static void gizmo_spin_prop_depth_get(
352         const wmGizmo *gz, wmGizmoProperty *gz_prop,
353         void *value_p)
354 {
355         GizmoGroupData_SpinRedo *ggd = gz->parent_gzgroup->customdata;
356         wmOperator *op = ggd->data.op;
357         float *value = value_p;
358
359         BLI_assert(gz_prop->type->array_length == 1);
360         UNUSED_VARS_NDEBUG(gz_prop);
361
362         float plane_co[3], plane_no[3];
363         RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_co, plane_co);
364         RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_no, plane_no);
365
366         value[0] = dot_v3v3(plane_no, plane_co) - dot_v3v3(plane_no, gz->matrix_basis[3]);
367 }
368
369 static void gizmo_spin_prop_depth_set(
370         const wmGizmo *gz, wmGizmoProperty *gz_prop,
371         const void *value_p)
372 {
373         GizmoGroupData_SpinRedo *ggd = gz->parent_gzgroup->customdata;
374         wmOperator *op = ggd->data.op;
375         const float *value = value_p;
376
377         BLI_assert(gz_prop->type->array_length == 1);
378         UNUSED_VARS_NDEBUG(gz_prop);
379
380         float plane_co[3], plane[4];
381         RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_co, plane_co);
382         RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_no, plane);
383         normalize_v3(plane);
384
385         plane[3] = -value[0] - dot_v3v3(plane, gz->matrix_basis[3]);
386
387         /* Keep our location, may be offset simply to be inside the viewport. */
388         closest_to_plane_normalized_v3(plane_co, plane, plane_co);
389
390         RNA_property_float_set_array(op->ptr, ggd->data.prop_axis_co, plane_co);
391
392         gizmo_spin_exec(ggd);
393 }
394
395 /* translate callbacks */
396 static void gizmo_spin_prop_translate_get(
397         const wmGizmo *gz, wmGizmoProperty *gz_prop,
398         void *value_p)
399 {
400         GizmoGroupData_SpinRedo *ggd = gz->parent_gzgroup->customdata;
401         wmOperator *op = ggd->data.op;
402         float *value = value_p;
403
404         BLI_assert(gz_prop->type->array_length == 3);
405         UNUSED_VARS_NDEBUG(gz_prop);
406
407         RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_co, value);
408 }
409
410 static void gizmo_spin_prop_translate_set(
411         const wmGizmo *gz, wmGizmoProperty *gz_prop,
412         const void *value)
413 {
414         GizmoGroupData_SpinRedo *ggd = gz->parent_gzgroup->customdata;
415         wmOperator *op = ggd->data.op;
416
417         BLI_assert(gz_prop->type->array_length == 3);
418         UNUSED_VARS_NDEBUG(gz_prop);
419
420         RNA_property_float_set_array(op->ptr, ggd->data.prop_axis_co, value);
421
422         gizmo_spin_exec(ggd);
423 }
424
425 /* angle callbacks */
426 static void gizmo_spin_prop_axis_angle_get(
427         const wmGizmo *gz, wmGizmoProperty *gz_prop,
428         void *value_p)
429 {
430         GizmoGroupData_SpinRedo *ggd = gz->parent_gzgroup->customdata;
431         wmOperator *op = ggd->data.op;
432         float *value = value_p;
433
434         BLI_assert(gz_prop->type->array_length == 1);
435         UNUSED_VARS_NDEBUG(gz_prop);
436
437         float plane_no[4];
438         RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_no, plane_no);
439         normalize_v3(plane_no);
440
441         float plane_no_proj[3];
442         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, ggd->data.rotate_axis);
443
444         if (!is_zero_v3(plane_no_proj)) {
445                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, ggd->data.rotate_up, ggd->data.rotate_axis);
446                 value[0] = angle;
447         }
448         else {
449                 value[0] = 0.0f;
450         }
451 }
452
453 static void gizmo_spin_prop_axis_angle_set(
454         const wmGizmo *gz, wmGizmoProperty *gz_prop,
455         const void *value_p)
456 {
457         GizmoGroupData_SpinRedo *ggd = gz->parent_gzgroup->customdata;
458         wmOperator *op = ggd->data.op;
459         const float *value = value_p;
460
461         BLI_assert(gz_prop->type->array_length == 1);
462         UNUSED_VARS_NDEBUG(gz_prop);
463
464         float plane_no[4];
465         RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_no, plane_no);
466         normalize_v3(plane_no);
467
468         float plane_no_proj[3];
469         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, ggd->data.rotate_axis);
470
471         if (!is_zero_v3(plane_no_proj)) {
472                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, ggd->data.rotate_up, ggd->data.rotate_axis);
473                 const float angle_delta = angle - angle_compat_rad(value[0], angle);
474                 if (angle_delta != 0.0f) {
475                         float mat[3][3];
476                         axis_angle_normalized_to_mat3(mat, ggd->data.rotate_axis, angle_delta);
477                         mul_m3_v3(mat, plane_no);
478
479                         /* re-normalize - seems acceptable */
480                         RNA_property_float_set_array(op->ptr, ggd->data.prop_axis_no, plane_no);
481
482                         gizmo_spin_exec(ggd);
483                 }
484         }
485 }
486
487 /* angle callbacks */
488 static void gizmo_spin_prop_angle_get(
489         const wmGizmo *gz, wmGizmoProperty *gz_prop,
490         void *value_p)
491 {
492         GizmoGroupData_SpinRedo *ggd = gz->parent_gzgroup->customdata;
493         wmOperator *op = ggd->data.op;
494         float *value = value_p;
495
496         BLI_assert(gz_prop->type->array_length == 1);
497         UNUSED_VARS_NDEBUG(gz_prop);
498         value[0] = RNA_property_float_get(op->ptr, ggd->data.prop_angle);
499 }
500
501 static void gizmo_spin_prop_angle_set(
502         const wmGizmo *gz, wmGizmoProperty *gz_prop,
503         const void *value_p)
504 {
505         GizmoGroupData_SpinRedo *ggd = gz->parent_gzgroup->customdata;
506         wmOperator *op = ggd->data.op;
507         BLI_assert(gz_prop->type->array_length == 1);
508         UNUSED_VARS_NDEBUG(gz_prop);
509         const float *value = value_p;
510         RNA_property_float_set(op->ptr, ggd->data.prop_angle, value[0]);
511
512         gizmo_spin_exec(ggd);
513 }
514
515 static bool gizmo_mesh_spin_redo_poll(const bContext *C, wmGizmoGroupType *gzgt)
516 {
517         return ED_gizmo_poll_or_unlink_delayed_from_operator(C, gzgt, "MESH_OT_spin");
518 }
519
520
521 static void gizmo_mesh_spin_redo_modal_from_setup(
522         const bContext *C, wmGizmoGroup *gzgroup)
523 {
524         /* Start off dragging. */
525         GizmoGroupData_SpinRedo *ggd = gzgroup->customdata;
526         wmWindow *win = CTX_wm_window(C);
527         wmGizmo *gz = ggd->angle_z;
528         wmGizmoMap *gzmap = gzgroup->parent_gzmap;
529
530
531 #ifdef USE_ANGLE_Z_ORIENT
532         {
533                 wmOperator *op = ggd->data.op;
534                 View3D *v3d = CTX_wm_view3d(C);
535                 ARegion *ar = CTX_wm_region(C);
536                 const wmEvent *event = win->eventstate;
537                 float plane_co[3], plane_no[3];
538                 RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_co, plane_co);
539                 RNA_property_float_get_array(op->ptr, ggd->data.prop_axis_no, plane_no);
540                 float cursor_co[3];
541                 const int mval[2] = {event->x - ar->winrct.xmin, event->y - ar->winrct.ymin};
542                 float plane[4];
543                 plane_from_point_normal_v3(plane, plane_co, plane_no);
544                 if (UNLIKELY(!ED_view3d_win_to_3d_on_plane_int(ar, plane, mval, false, cursor_co))) {
545                         ED_view3d_win_to_3d_int(v3d, ar, plane, mval, cursor_co);
546                 }
547                 sub_v3_v3v3(ggd->data.orient_axis, cursor_co, plane_co);
548                 normalize_v3(ggd->data.orient_axis);
549         }
550 #endif
551
552         WM_gizmo_modal_set_from_setup(
553                 gzmap, (bContext *)C, gz, 0, win->eventstate);
554 }
555
556 static void gizmo_mesh_spin_redo_setup(const bContext *C, wmGizmoGroup *gzgroup)
557 {
558         wmOperatorType *ot = WM_operatortype_find("MESH_OT_spin", true);
559         wmOperator *op = WM_operator_last_redo(C);
560
561         if ((op == NULL) || (op->type != ot)) {
562                 return;
563         }
564
565         GizmoGroupData_SpinRedo *ggd = MEM_callocN(sizeof(*ggd), __func__);
566         gzgroup->customdata = ggd;
567
568         const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
569         const wmGizmoType *gzt_move = WM_gizmotype_find("GIZMO_GT_move_3d", true);
570         const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
571
572         ggd->translate_z = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
573         ggd->translate_c = WM_gizmo_new_ptr(gzt_move, gzgroup, NULL);
574         ggd->rotate_c = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL);
575         ggd->angle_z = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL);
576
577         UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, ggd->translate_z->color);
578         UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, ggd->translate_c->color);
579         UI_GetThemeColor3fv(TH_GIZMO_SECONDARY, ggd->rotate_c->color);
580         copy_v3_v3(ggd->angle_z->color, ggd->angle_z->color_hi);
581         ggd->angle_z->color[3] = 0.5f;
582
583         RNA_enum_set(ggd->translate_z->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_NORMAL);
584         RNA_enum_set(ggd->translate_c->ptr, "draw_style", ED_GIZMO_MOVE_STYLE_RING_2D);
585
586         RNA_boolean_set(ggd->angle_z->ptr, "wrap_angle", false);
587         RNA_enum_set(ggd->angle_z->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE);
588         RNA_float_set(ggd->angle_z->ptr, "arc_inner_factor", 0.9f);
589
590         WM_gizmo_set_flag(ggd->translate_c, WM_GIZMO_DRAW_VALUE, true);
591         WM_gizmo_set_flag(ggd->rotate_c, WM_GIZMO_DRAW_VALUE, true);
592         WM_gizmo_set_flag(ggd->angle_z, WM_GIZMO_DRAW_VALUE, true);
593
594         WM_gizmo_set_scale(ggd->rotate_c, 0.8f);
595
596         {
597                 ggd->data.context = (bContext *)C;
598                 ggd->data.ot = ot;
599                 ggd->data.op = op;
600                 ggd->data.prop_axis_co = RNA_struct_type_find_property(ot->srna, "center");
601                 ggd->data.prop_axis_no = RNA_struct_type_find_property(ot->srna, "axis");
602                 ggd->data.prop_angle = RNA_struct_type_find_property(ot->srna, "angle");
603         }
604
605         gizmo_mesh_spin_redo_update_from_op(ggd);
606
607         /* Setup property callbacks */
608         {
609                 WM_gizmo_target_property_def_func(
610                         ggd->translate_z, "offset",
611                         &(const struct wmGizmoPropertyFnParams) {
612                             .value_get_fn = gizmo_spin_prop_depth_get,
613                             .value_set_fn = gizmo_spin_prop_depth_set,
614                             .range_get_fn = NULL,
615                             .user_data = NULL,
616                         });
617
618                 WM_gizmo_target_property_def_func(
619                         ggd->translate_c, "offset",
620                         &(const struct wmGizmoPropertyFnParams) {
621                             .value_get_fn = gizmo_spin_prop_translate_get,
622                             .value_set_fn = gizmo_spin_prop_translate_set,
623                             .range_get_fn = NULL,
624                             .user_data = NULL,
625                         });
626
627                 WM_gizmo_target_property_def_func(
628                         ggd->rotate_c, "offset",
629                         &(const struct wmGizmoPropertyFnParams) {
630                             .value_get_fn = gizmo_spin_prop_axis_angle_get,
631                             .value_set_fn = gizmo_spin_prop_axis_angle_set,
632                             .range_get_fn = NULL,
633                             .user_data = NULL,
634                         });
635
636                 WM_gizmo_target_property_def_func(
637                         ggd->angle_z, "offset",
638                         &(const struct wmGizmoPropertyFnParams) {
639                             .value_get_fn = gizmo_spin_prop_angle_get,
640                             .value_set_fn = gizmo_spin_prop_angle_set,
641                             .range_get_fn = NULL,
642                             .user_data = NULL,
643                         });
644
645         }
646
647 #ifndef USE_EXTRA_CONTROLS
648         /* Disable for now. */
649         WM_gizmo_set_flag(ggd->translate_z, WM_GIZMO_HIDDEN, true);
650         WM_gizmo_set_flag(ggd->translate_c, WM_GIZMO_HIDDEN, true);
651         WM_gizmo_set_flag(ggd->rotate_c, WM_GIZMO_HIDDEN, true);
652 #endif
653
654         /* Become modal as soon as it's started. */
655         gizmo_mesh_spin_redo_modal_from_setup(C, gzgroup);
656 }
657
658 static void gizmo_mesh_spin_redo_draw_prepare(
659         const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
660 {
661         GizmoGroupData_SpinRedo *ggd = gzgroup->customdata;
662         if (ggd->data.op->next) {
663                 ggd->data.op = WM_operator_last_redo((bContext *)ggd->data.context);
664         }
665         gizmo_mesh_spin_redo_update_from_op(ggd);
666 }
667
668 void MESH_GGT_spin_redo(struct wmGizmoGroupType *gzgt)
669 {
670         gzgt->name = "Mesh Spin Redo";
671         gzgt->idname = "MESH_GGT_spin_redo";
672
673         gzgt->flag = WM_GIZMOGROUPTYPE_3D;
674
675         gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
676         gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
677
678         gzgt->poll = gizmo_mesh_spin_redo_poll;
679         gzgt->setup = gizmo_mesh_spin_redo_setup;
680         gzgt->draw_prepare = gizmo_mesh_spin_redo_draw_prepare;
681 }
682
683 /** \} */