Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / transform / transform_gizmo_3d.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
17 /** \file
18  * \ingroup edtransform
19  *
20  * \name 3D Transform Gizmo
21  *
22  * Used for 3D View
23  */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <math.h>
28 #include <float.h>
29
30 #include "DNA_armature_types.h"
31 #include "DNA_curve_types.h"
32 #include "DNA_gpencil_types.h"
33 #include "DNA_lattice_types.h"
34 #include "DNA_meta_types.h"
35 #include "DNA_screen_types.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_view3d_types.h"
38
39 #include "BLI_array_utils.h"
40 #include "BLI_listbase.h"
41 #include "BLI_math.h"
42 #include "BLI_utildefines.h"
43 #include "BLI_string.h"
44
45 #include "BKE_action.h"
46 #include "BKE_context.h"
47 #include "BKE_curve.h"
48 #include "BKE_global.h"
49 #include "BKE_layer.h"
50 #include "BKE_particle.h"
51 #include "BKE_pointcache.h"
52 #include "BKE_editmesh.h"
53 #include "BKE_lattice.h"
54 #include "BKE_gpencil.h"
55 #include "BKE_scene.h"
56 #include "BKE_workspace.h"
57 #include "BKE_object.h"
58
59 #include "DEG_depsgraph.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63 #include "WM_message.h"
64 #include "WM_toolsystem.h"
65 #include "wm.h"
66
67 #include "ED_armature.h"
68 #include "ED_curve.h"
69 #include "ED_object.h"
70 #include "ED_particle.h"
71 #include "ED_view3d.h"
72 #include "ED_gpencil.h"
73 #include "ED_screen.h"
74 #include "ED_gizmo_library.h"
75 #include "ED_gizmo_utils.h"
76
77 #include "UI_interface.h"
78 #include "UI_resources.h"
79
80 #include "RNA_access.h"
81 #include "RNA_define.h"
82
83 /* local module include */
84 #include "transform.h"
85
86 #include "MEM_guardedalloc.h"
87
88 #include "GPU_select.h"
89 #include "GPU_state.h"
90 #include "GPU_immediate.h"
91 #include "GPU_matrix.h"
92
93 #include "DEG_depsgraph_query.h"
94
95 /* return codes for select, and drawing flags */
96
97 #define MAN_TRANS_X (1 << 0)
98 #define MAN_TRANS_Y (1 << 1)
99 #define MAN_TRANS_Z (1 << 2)
100 #define MAN_TRANS_C (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z)
101
102 #define MAN_ROT_X (1 << 3)
103 #define MAN_ROT_Y (1 << 4)
104 #define MAN_ROT_Z (1 << 5)
105 #define MAN_ROT_C (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z)
106
107 #define MAN_SCALE_X (1 << 8)
108 #define MAN_SCALE_Y (1 << 9)
109 #define MAN_SCALE_Z (1 << 10)
110 #define MAN_SCALE_C (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z)
111
112 /* threshold for testing view aligned gizmo axis */
113 static struct {
114   float min, max;
115 } g_tw_axis_range[2] = {
116     /* Regular range */
117     {0.02f, 0.1f},
118     /* Use a different range because we flip the dot product,
119      * also the view aligned planes are harder to see so hiding early is preferred. */
120     {0.175f, 0.25f},
121 };
122
123 /* axes as index */
124 enum {
125   MAN_AXIS_TRANS_X = 0,
126   MAN_AXIS_TRANS_Y,
127   MAN_AXIS_TRANS_Z,
128   MAN_AXIS_TRANS_C,
129
130   MAN_AXIS_TRANS_XY,
131   MAN_AXIS_TRANS_YZ,
132   MAN_AXIS_TRANS_ZX,
133 #define MAN_AXIS_RANGE_TRANS_START MAN_AXIS_TRANS_X
134 #define MAN_AXIS_RANGE_TRANS_END (MAN_AXIS_TRANS_ZX + 1)
135
136   MAN_AXIS_ROT_X,
137   MAN_AXIS_ROT_Y,
138   MAN_AXIS_ROT_Z,
139   MAN_AXIS_ROT_C,
140   MAN_AXIS_ROT_T, /* trackball rotation */
141 #define MAN_AXIS_RANGE_ROT_START MAN_AXIS_ROT_X
142 #define MAN_AXIS_RANGE_ROT_END (MAN_AXIS_ROT_T + 1)
143
144   MAN_AXIS_SCALE_X,
145   MAN_AXIS_SCALE_Y,
146   MAN_AXIS_SCALE_Z,
147   MAN_AXIS_SCALE_C,
148   MAN_AXIS_SCALE_XY,
149   MAN_AXIS_SCALE_YZ,
150   MAN_AXIS_SCALE_ZX,
151 #define MAN_AXIS_RANGE_SCALE_START MAN_AXIS_SCALE_X
152 #define MAN_AXIS_RANGE_SCALE_END (MAN_AXIS_SCALE_ZX + 1)
153
154   MAN_AXIS_LAST = MAN_AXIS_SCALE_ZX + 1,
155 };
156
157 /* axis types */
158 enum {
159   MAN_AXES_ALL = 0,
160   MAN_AXES_TRANSLATE,
161   MAN_AXES_ROTATE,
162   MAN_AXES_SCALE,
163 };
164
165 typedef struct GizmoGroup {
166   bool all_hidden;
167   int twtype;
168
169   /* Users may change the twtype, detect changes to re-setup gizmo options. */
170   int twtype_init;
171   int twtype_prev;
172   int use_twtype_refresh;
173
174   /* Only for view orientation. */
175   struct {
176     float viewinv_m3[3][3];
177   } prev;
178
179   struct wmGizmo *gizmos[MAN_AXIS_LAST];
180 } GizmoGroup;
181
182 /* -------------------------------------------------------------------- */
183 /** \name Utilities
184  * \{ */
185
186 /* loop over axes */
187 #define MAN_ITER_AXES_BEGIN(axis, axis_idx) \
188   { \
189     wmGizmo *axis; \
190     int axis_idx; \
191     for (axis_idx = 0; axis_idx < MAN_AXIS_LAST; axis_idx++) { \
192       axis = gizmo_get_axis_from_index(ggd, axis_idx);
193
194 #define MAN_ITER_AXES_END \
195   } \
196   } \
197   ((void)0)
198
199 static wmGizmo *gizmo_get_axis_from_index(const GizmoGroup *ggd, const short axis_idx)
200 {
201   BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN_AXIS_TRANS_X, (float)MAN_AXIS_LAST));
202   return ggd->gizmos[axis_idx];
203 }
204
205 static short gizmo_get_axis_type(const int axis_idx)
206 {
207   if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
208     return MAN_AXES_TRANSLATE;
209   }
210   if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
211     return MAN_AXES_ROTATE;
212   }
213   if (axis_idx >= MAN_AXIS_RANGE_SCALE_START && axis_idx < MAN_AXIS_RANGE_SCALE_END) {
214     return MAN_AXES_SCALE;
215   }
216   BLI_assert(0);
217   return -1;
218 }
219
220 static uint gizmo_orientation_axis(const int axis_idx, bool *r_is_plane)
221 {
222   switch (axis_idx) {
223     case MAN_AXIS_TRANS_YZ:
224     case MAN_AXIS_SCALE_YZ:
225       if (r_is_plane) {
226         *r_is_plane = true;
227       }
228       ATTR_FALLTHROUGH;
229     case MAN_AXIS_TRANS_X:
230     case MAN_AXIS_ROT_X:
231     case MAN_AXIS_SCALE_X:
232       return 0;
233
234     case MAN_AXIS_TRANS_ZX:
235     case MAN_AXIS_SCALE_ZX:
236       if (r_is_plane) {
237         *r_is_plane = true;
238       }
239       ATTR_FALLTHROUGH;
240     case MAN_AXIS_TRANS_Y:
241     case MAN_AXIS_ROT_Y:
242     case MAN_AXIS_SCALE_Y:
243       return 1;
244
245     case MAN_AXIS_TRANS_XY:
246     case MAN_AXIS_SCALE_XY:
247       if (r_is_plane) {
248         *r_is_plane = true;
249       }
250       ATTR_FALLTHROUGH;
251     case MAN_AXIS_TRANS_Z:
252     case MAN_AXIS_ROT_Z:
253     case MAN_AXIS_SCALE_Z:
254       return 2;
255   }
256   return 3;
257 }
258
259 static bool gizmo_is_axis_visible(const RegionView3D *rv3d,
260                                   const int twtype,
261                                   const float idot[3],
262                                   const int axis_type,
263                                   const int axis_idx)
264 {
265   if ((axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) == 0) {
266     bool is_plane = false;
267     const uint aidx_norm = gizmo_orientation_axis(axis_idx, &is_plane);
268     /* don't draw axis perpendicular to the view */
269     if (aidx_norm < 3) {
270       float idot_axis = idot[aidx_norm];
271       if (is_plane) {
272         idot_axis = 1.0f - idot_axis;
273       }
274       if (idot_axis < g_tw_axis_range[is_plane].min) {
275         return false;
276       }
277     }
278   }
279
280   if ((axis_type == MAN_AXES_TRANSLATE && !(twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE)) ||
281       (axis_type == MAN_AXES_ROTATE && !(twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE)) ||
282       (axis_type == MAN_AXES_SCALE && !(twtype & V3D_GIZMO_SHOW_OBJECT_SCALE))) {
283     return false;
284   }
285
286   switch (axis_idx) {
287     case MAN_AXIS_TRANS_X:
288       return (rv3d->twdrawflag & MAN_TRANS_X);
289     case MAN_AXIS_TRANS_Y:
290       return (rv3d->twdrawflag & MAN_TRANS_Y);
291     case MAN_AXIS_TRANS_Z:
292       return (rv3d->twdrawflag & MAN_TRANS_Z);
293     case MAN_AXIS_TRANS_C:
294       return (rv3d->twdrawflag & MAN_TRANS_C);
295     case MAN_AXIS_ROT_X:
296       return (rv3d->twdrawflag & MAN_ROT_X);
297     case MAN_AXIS_ROT_Y:
298       return (rv3d->twdrawflag & MAN_ROT_Y);
299     case MAN_AXIS_ROT_Z:
300       return (rv3d->twdrawflag & MAN_ROT_Z);
301     case MAN_AXIS_ROT_C:
302     case MAN_AXIS_ROT_T:
303       return (rv3d->twdrawflag & MAN_ROT_C);
304     case MAN_AXIS_SCALE_X:
305       return (rv3d->twdrawflag & MAN_SCALE_X);
306     case MAN_AXIS_SCALE_Y:
307       return (rv3d->twdrawflag & MAN_SCALE_Y);
308     case MAN_AXIS_SCALE_Z:
309       return (rv3d->twdrawflag & MAN_SCALE_Z);
310     case MAN_AXIS_SCALE_C:
311       return (rv3d->twdrawflag & MAN_SCALE_C && (twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0);
312     case MAN_AXIS_TRANS_XY:
313       return (rv3d->twdrawflag & MAN_TRANS_X && rv3d->twdrawflag & MAN_TRANS_Y &&
314               (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
315     case MAN_AXIS_TRANS_YZ:
316       return (rv3d->twdrawflag & MAN_TRANS_Y && rv3d->twdrawflag & MAN_TRANS_Z &&
317               (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
318     case MAN_AXIS_TRANS_ZX:
319       return (rv3d->twdrawflag & MAN_TRANS_Z && rv3d->twdrawflag & MAN_TRANS_X &&
320               (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
321     case MAN_AXIS_SCALE_XY:
322       return (rv3d->twdrawflag & MAN_SCALE_X && rv3d->twdrawflag & MAN_SCALE_Y &&
323               (twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0 &&
324               (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
325     case MAN_AXIS_SCALE_YZ:
326       return (rv3d->twdrawflag & MAN_SCALE_Y && rv3d->twdrawflag & MAN_SCALE_Z &&
327               (twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0 &&
328               (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
329     case MAN_AXIS_SCALE_ZX:
330       return (rv3d->twdrawflag & MAN_SCALE_Z && rv3d->twdrawflag & MAN_SCALE_X &&
331               (twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0 &&
332               (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
333   }
334   return false;
335 }
336
337 static void gizmo_get_axis_color(const int axis_idx,
338                                  const float idot[3],
339                                  float r_col[4],
340                                  float r_col_hi[4])
341 {
342   /* alpha values for normal/highlighted states */
343   const float alpha = 0.6f;
344   const float alpha_hi = 1.0f;
345   float alpha_fac;
346
347   if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
348     /* Never fade rotation rings. */
349     /* trackball rotation axis is a special case, we only draw a slight overlay */
350     alpha_fac = (axis_idx == MAN_AXIS_ROT_T) ? 0.1f : 1.0f;
351   }
352   else {
353     bool is_plane = false;
354     const int axis_idx_norm = gizmo_orientation_axis(axis_idx, &is_plane);
355     /* Get alpha fac based on axis angle,
356      * to fade axis out when hiding it because it points towards view. */
357     if (axis_idx_norm < 3) {
358       const float idot_min = g_tw_axis_range[is_plane].min;
359       const float idot_max = g_tw_axis_range[is_plane].max;
360       float idot_axis = idot[axis_idx_norm];
361       if (is_plane) {
362         idot_axis = 1.0f - idot_axis;
363       }
364       alpha_fac = ((idot_axis > idot_max) ?
365                        1.0f :
366                        (idot_axis < idot_min) ? 0.0f :
367                                                 ((idot_axis - idot_min) / (idot_max - idot_min)));
368     }
369     else {
370       alpha_fac = 1.0f;
371     }
372   }
373
374   switch (axis_idx) {
375     case MAN_AXIS_TRANS_X:
376     case MAN_AXIS_ROT_X:
377     case MAN_AXIS_SCALE_X:
378     case MAN_AXIS_TRANS_YZ:
379     case MAN_AXIS_SCALE_YZ:
380       UI_GetThemeColor4fv(TH_AXIS_X, r_col);
381       break;
382     case MAN_AXIS_TRANS_Y:
383     case MAN_AXIS_ROT_Y:
384     case MAN_AXIS_SCALE_Y:
385     case MAN_AXIS_TRANS_ZX:
386     case MAN_AXIS_SCALE_ZX:
387       UI_GetThemeColor4fv(TH_AXIS_Y, r_col);
388       break;
389     case MAN_AXIS_TRANS_Z:
390     case MAN_AXIS_ROT_Z:
391     case MAN_AXIS_SCALE_Z:
392     case MAN_AXIS_TRANS_XY:
393     case MAN_AXIS_SCALE_XY:
394       UI_GetThemeColor4fv(TH_AXIS_Z, r_col);
395       break;
396     case MAN_AXIS_TRANS_C:
397     case MAN_AXIS_ROT_C:
398     case MAN_AXIS_SCALE_C:
399     case MAN_AXIS_ROT_T:
400       copy_v4_fl(r_col, 1.0f);
401       break;
402   }
403
404   copy_v4_v4(r_col_hi, r_col);
405
406   r_col[3] = alpha * alpha_fac;
407   r_col_hi[3] = alpha_hi * alpha_fac;
408 }
409
410 static void gizmo_get_axis_constraint(const int axis_idx, bool r_axis[3])
411 {
412   ARRAY_SET_ITEMS(r_axis, 0, 0, 0);
413
414   switch (axis_idx) {
415     case MAN_AXIS_TRANS_X:
416     case MAN_AXIS_ROT_X:
417     case MAN_AXIS_SCALE_X:
418       r_axis[0] = 1;
419       break;
420     case MAN_AXIS_TRANS_Y:
421     case MAN_AXIS_ROT_Y:
422     case MAN_AXIS_SCALE_Y:
423       r_axis[1] = 1;
424       break;
425     case MAN_AXIS_TRANS_Z:
426     case MAN_AXIS_ROT_Z:
427     case MAN_AXIS_SCALE_Z:
428       r_axis[2] = 1;
429       break;
430     case MAN_AXIS_TRANS_XY:
431     case MAN_AXIS_SCALE_XY:
432       r_axis[0] = r_axis[1] = 1;
433       break;
434     case MAN_AXIS_TRANS_YZ:
435     case MAN_AXIS_SCALE_YZ:
436       r_axis[1] = r_axis[2] = 1;
437       break;
438     case MAN_AXIS_TRANS_ZX:
439     case MAN_AXIS_SCALE_ZX:
440       r_axis[2] = r_axis[0] = 1;
441       break;
442     default:
443       break;
444   }
445 }
446
447 /* **************** Preparation Stuff **************** */
448
449 static void reset_tw_center(struct TransformBounds *tbounds)
450 {
451   INIT_MINMAX(tbounds->min, tbounds->max);
452   zero_v3(tbounds->center);
453
454   for (int i = 0; i < 3; i++) {
455     tbounds->axis_min[i] = +FLT_MAX;
456     tbounds->axis_max[i] = -FLT_MAX;
457   }
458 }
459
460 /* transform widget center calc helper for below */
461 static void calc_tw_center(struct TransformBounds *tbounds, const float co[3])
462 {
463   minmax_v3v3_v3(tbounds->min, tbounds->max, co);
464   add_v3_v3(tbounds->center, co);
465
466   for (int i = 0; i < 3; i++) {
467     const float d = dot_v3v3(tbounds->axis[i], co);
468     tbounds->axis_min[i] = min_ff(d, tbounds->axis_min[i]);
469     tbounds->axis_max[i] = max_ff(d, tbounds->axis_max[i]);
470   }
471 }
472
473 static void calc_tw_center_with_matrix(struct TransformBounds *tbounds,
474                                        const float co[3],
475                                        const bool use_matrix,
476                                        const float matrix[4][4])
477 {
478   float co_world[3];
479   if (use_matrix) {
480     mul_v3_m4v3(co_world, matrix, co);
481     co = co_world;
482   }
483   calc_tw_center(tbounds, co);
484 }
485
486 static void protectflag_to_drawflags(short protectflag, short *drawflags)
487 {
488   if (protectflag & OB_LOCK_LOCX) {
489     *drawflags &= ~MAN_TRANS_X;
490   }
491   if (protectflag & OB_LOCK_LOCY) {
492     *drawflags &= ~MAN_TRANS_Y;
493   }
494   if (protectflag & OB_LOCK_LOCZ) {
495     *drawflags &= ~MAN_TRANS_Z;
496   }
497
498   if (protectflag & OB_LOCK_ROTX) {
499     *drawflags &= ~MAN_ROT_X;
500   }
501   if (protectflag & OB_LOCK_ROTY) {
502     *drawflags &= ~MAN_ROT_Y;
503   }
504   if (protectflag & OB_LOCK_ROTZ) {
505     *drawflags &= ~MAN_ROT_Z;
506   }
507
508   if (protectflag & OB_LOCK_SCALEX) {
509     *drawflags &= ~MAN_SCALE_X;
510   }
511   if (protectflag & OB_LOCK_SCALEY) {
512     *drawflags &= ~MAN_SCALE_Y;
513   }
514   if (protectflag & OB_LOCK_SCALEZ) {
515     *drawflags &= ~MAN_SCALE_Z;
516   }
517 }
518
519 /* for pose mode */
520 static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan)
521 {
522   protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
523 }
524
525 /* for editmode*/
526 static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo)
527 {
528   if (ebo->flag & BONE_EDITMODE_LOCKED) {
529     protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag);
530   }
531 }
532
533 /* could move into BLI_math however this is only useful for display/editing purposes */
534 static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
535 {
536   /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */
537
538   float cross_vec[3];
539   float quat[4];
540
541   /* this is an un-scientific method to get a vector to cross with
542    * XYZ intentionally YZX */
543   cross_vec[0] = axis[1];
544   cross_vec[1] = axis[2];
545   cross_vec[2] = axis[0];
546
547   /* X-axis */
548   cross_v3_v3v3(gmat[0], cross_vec, axis);
549   normalize_v3(gmat[0]);
550   axis_angle_to_quat(quat, axis, angle);
551   mul_qt_v3(quat, gmat[0]);
552
553   /* Y-axis */
554   axis_angle_to_quat(quat, axis, M_PI_2);
555   copy_v3_v3(gmat[1], gmat[0]);
556   mul_qt_v3(quat, gmat[1]);
557
558   /* Z-axis */
559   copy_v3_v3(gmat[2], axis);
560
561   normalize_m3(gmat);
562 }
563
564 static bool test_rotmode_euler(short rotmode)
565 {
566   return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1;
567 }
568
569 bool gimbal_axis(Object *ob, float gmat[3][3])
570 {
571   if (ob->mode & OB_MODE_POSE) {
572     bPoseChannel *pchan = BKE_pose_channel_active(ob);
573
574     if (pchan) {
575       float mat[3][3], tmat[3][3], obmat[3][3];
576       if (test_rotmode_euler(pchan->rotmode)) {
577         eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
578       }
579       else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
580         axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
581       }
582       else { /* quat */
583         return 0;
584       }
585
586       /* apply bone transformation */
587       mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
588
589       if (pchan->parent) {
590         float parent_mat[3][3];
591
592         copy_m3_m4(parent_mat, pchan->parent->pose_mat);
593         mul_m3_m3m3(mat, parent_mat, tmat);
594
595         /* needed if object transformation isn't identity */
596         copy_m3_m4(obmat, ob->obmat);
597         mul_m3_m3m3(gmat, obmat, mat);
598       }
599       else {
600         /* needed if object transformation isn't identity */
601         copy_m3_m4(obmat, ob->obmat);
602         mul_m3_m3m3(gmat, obmat, tmat);
603       }
604
605       normalize_m3(gmat);
606       return 1;
607     }
608   }
609   else {
610     if (test_rotmode_euler(ob->rotmode)) {
611       eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
612     }
613     else if (ob->rotmode == ROT_MODE_AXISANGLE) {
614       axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle);
615     }
616     else { /* quat */
617       return 0;
618     }
619
620     if (ob->parent) {
621       float parent_mat[3][3];
622       copy_m3_m4(parent_mat, ob->parent->obmat);
623       normalize_m3(parent_mat);
624       mul_m3_m3m3(gmat, parent_mat, gmat);
625     }
626     return 1;
627   }
628
629   return 0;
630 }
631
632 void ED_transform_calc_orientation_from_type(const bContext *C, float r_mat[3][3])
633 {
634   ARegion *ar = CTX_wm_region(C);
635   Scene *scene = CTX_data_scene(C);
636   ViewLayer *view_layer = CTX_data_view_layer(C);
637   Object *obedit = CTX_data_edit_object(C);
638   RegionView3D *rv3d = ar->regiondata;
639   Object *ob = OBACT(view_layer);
640   const short orientation_type = scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
641   const short orientation_index_custom = scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom;
642   const int pivot_point = scene->toolsettings->transform_pivot_point;
643
644   ED_transform_calc_orientation_from_type_ex(
645       C, r_mat, scene, rv3d, ob, obedit, orientation_type, orientation_index_custom, pivot_point);
646 }
647
648 void ED_transform_calc_orientation_from_type_ex(const bContext *C,
649                                                 float r_mat[3][3],
650                                                 /* extra args (can be accessed from context) */
651                                                 Scene *scene,
652                                                 RegionView3D *rv3d,
653                                                 Object *ob,
654                                                 Object *obedit,
655                                                 const short orientation_type,
656                                                 int orientation_index_custom,
657                                                 const int pivot_point)
658 {
659   bool ok = false;
660
661   switch (orientation_type) {
662     case V3D_ORIENT_GLOBAL: {
663       break; /* nothing to do */
664     }
665     case V3D_ORIENT_GIMBAL: {
666       if (gimbal_axis(ob, r_mat)) {
667         ok = true;
668         break;
669       }
670       /* if not gimbal, fall through to normal */
671       ATTR_FALLTHROUGH;
672     }
673     case V3D_ORIENT_NORMAL: {
674       if (obedit || ob->mode & OB_MODE_POSE) {
675         ED_getTransformOrientationMatrix(C, r_mat, pivot_point);
676         ok = true;
677         break;
678       }
679       /* no break we define 'normal' as 'local' in Object mode */
680       ATTR_FALLTHROUGH;
681     }
682     case V3D_ORIENT_LOCAL: {
683       if (ob->mode & OB_MODE_POSE) {
684         /* each bone moves on its own local axis, but  to avoid confusion,
685          * use the active pones axis for display [#33575], this works as expected on a single bone
686          * and users who select many bones will understand what's going on and what local means
687          * when they start transforming */
688         ED_getTransformOrientationMatrix(C, r_mat, pivot_point);
689         ok = true;
690         break;
691       }
692       copy_m3_m4(r_mat, ob->obmat);
693       normalize_m3(r_mat);
694       ok = true;
695       break;
696     }
697     case V3D_ORIENT_VIEW: {
698       if (rv3d != NULL) {
699         copy_m3_m4(r_mat, rv3d->viewinv);
700         normalize_m3(r_mat);
701         ok = true;
702       }
703       break;
704     }
705     case V3D_ORIENT_CURSOR: {
706       ED_view3d_cursor3d_calc_mat3(scene, r_mat);
707       ok = true;
708       break;
709     }
710     case V3D_ORIENT_CUSTOM: {
711       TransformOrientation *custom_orientation = BKE_scene_transform_orientation_find(
712           scene, orientation_index_custom);
713       if (applyTransformOrientation(custom_orientation, r_mat, NULL)) {
714         ok = true;
715       }
716       break;
717     }
718   }
719
720   if (!ok) {
721     unit_m3(r_mat);
722   }
723 }
724
725 /* centroid, boundbox, of selection */
726 /* returns total items selected */
727 int ED_transform_calc_gizmo_stats(const bContext *C,
728                                   const struct TransformCalcParams *params,
729                                   struct TransformBounds *tbounds)
730 {
731   ScrArea *sa = CTX_wm_area(C);
732   ARegion *ar = CTX_wm_region(C);
733   Scene *scene = CTX_data_scene(C);
734   Depsgraph *depsgraph = CTX_data_depsgraph(C);
735   ViewLayer *view_layer = CTX_data_view_layer(C);
736   View3D *v3d = sa->spacedata.first;
737   Object *obedit = CTX_data_edit_object(C);
738   RegionView3D *rv3d = ar->regiondata;
739   Base *base;
740   Object *ob = OBACT(view_layer);
741   bGPdata *gpd = CTX_data_gpencil_data(C);
742   const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
743   int a, totsel = 0;
744   const int pivot_point = scene->toolsettings->transform_pivot_point;
745
746   /* transform widget matrix */
747   unit_m4(rv3d->twmat);
748
749   unit_m3(rv3d->tw_axis_matrix);
750   zero_v3(rv3d->tw_axis_min);
751   zero_v3(rv3d->tw_axis_max);
752
753   rv3d->twdrawflag = 0xFFFF;
754
755   /* global, local or normal orientation?
756    * if we could check 'totsel' now, this should be skipped with no selection. */
757   if (ob) {
758     const short orientation_type = params->orientation_type ?
759                                        (params->orientation_type - 1) :
760                                        scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
761     const short orientation_index_custom =
762         params->orientation_type ? params->orientation_index_custom :
763                                    scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom;
764     float mat[3][3];
765     ED_transform_calc_orientation_from_type_ex(
766         C, mat, scene, rv3d, ob, obedit, orientation_type, orientation_index_custom, pivot_point);
767     copy_m4_m3(rv3d->twmat, mat);
768   }
769
770   /* transform widget centroid/center */
771   reset_tw_center(tbounds);
772
773   copy_m3_m4(tbounds->axis, rv3d->twmat);
774   if (params->use_local_axis && (ob && ob->mode & OB_MODE_EDIT)) {
775     float diff_mat[3][3];
776     copy_m3_m4(diff_mat, ob->obmat);
777     normalize_m3(diff_mat);
778     invert_m3(diff_mat);
779     mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat);
780     normalize_m3(tbounds->axis);
781   }
782
783   if (is_gp_edit) {
784     float diff_mat[4][4];
785     const bool use_mat_local = true;
786     for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
787       /* only editable and visible layers are considered */
788
789       if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
790
791         /* calculate difference matrix */
792         ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat);
793
794         for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
795           /* skip strokes that are invalid for current view */
796           if (ED_gpencil_stroke_can_use(C, gps) == false) {
797             continue;
798           }
799
800           /* we're only interested in selected points here... */
801           if (gps->flag & GP_STROKE_SELECT) {
802             bGPDspoint *pt;
803             int i;
804
805             /* Change selection status of all points, then make the stroke match */
806             for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
807               if (pt->flag & GP_SPOINT_SELECT) {
808                 calc_tw_center_with_matrix(tbounds, &pt->x, use_mat_local, diff_mat);
809                 totsel++;
810               }
811             }
812           }
813         }
814       }
815     }
816
817     /* selection center */
818     if (totsel) {
819       mul_v3_fl(tbounds->center, 1.0f / (float)totsel); /* centroid! */
820     }
821   }
822   else if (obedit) {
823
824 #define FOREACH_EDIT_OBJECT_BEGIN(ob_iter, use_mat_local) \
825   { \
826     invert_m4_m4(obedit->imat, obedit->obmat); \
827     uint objects_len = 0; \
828     Object **objects = BKE_view_layer_array_from_objects_in_edit_mode( \
829         view_layer, CTX_wm_view3d(C), &objects_len); \
830     for (uint ob_index = 0; ob_index < objects_len; ob_index++) { \
831       Object *ob_iter = objects[ob_index]; \
832       const bool use_mat_local = (ob_iter != obedit);
833
834 #define FOREACH_EDIT_OBJECT_END() \
835   } \
836   MEM_freeN(objects); \
837   } \
838   ((void)0)
839
840     ob = obedit;
841     if (obedit->type == OB_MESH) {
842       FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
843         BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
844         BMesh *bm = em_iter->bm;
845
846         if (bm->totvertsel == 0) {
847           continue;
848         }
849
850         BMVert *eve;
851         BMIter iter;
852
853         float mat_local[4][4];
854         if (use_mat_local) {
855           mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
856         }
857
858         BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
859           if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
860             if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
861               calc_tw_center_with_matrix(tbounds, eve->co, use_mat_local, mat_local);
862               totsel++;
863             }
864           }
865         }
866       }
867       FOREACH_EDIT_OBJECT_END();
868     } /* end editmesh */
869     else if (obedit->type == OB_ARMATURE) {
870       FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
871         bArmature *arm = ob_iter->data;
872
873         float mat_local[4][4];
874         if (use_mat_local) {
875           mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
876         }
877         for (EditBone *ebo = arm->edbo->first; ebo; ebo = ebo->next) {
878           if (EBONE_VISIBLE(arm, ebo)) {
879             if (ebo->flag & BONE_TIPSEL) {
880               calc_tw_center_with_matrix(tbounds, ebo->tail, use_mat_local, mat_local);
881               totsel++;
882             }
883             if ((ebo->flag & BONE_ROOTSEL) &&
884                 /* don't include same point multiple times */
885                 ((ebo->flag & BONE_CONNECTED) && (ebo->parent != NULL) &&
886                  (ebo->parent->flag & BONE_TIPSEL) && EBONE_VISIBLE(arm, ebo->parent)) == 0) {
887               calc_tw_center_with_matrix(tbounds, ebo->head, use_mat_local, mat_local);
888               totsel++;
889             }
890             if (ebo->flag & BONE_SELECTED) {
891               protectflag_to_drawflags_ebone(rv3d, ebo);
892             }
893           }
894         }
895       }
896       FOREACH_EDIT_OBJECT_END();
897     }
898     else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
899       FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
900         Curve *cu = ob_iter->data;
901         Nurb *nu;
902         BezTriple *bezt;
903         BPoint *bp;
904         ListBase *nurbs = BKE_curve_editNurbs_get(cu);
905
906         float mat_local[4][4];
907         if (use_mat_local) {
908           mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
909         }
910
911         nu = nurbs->first;
912         while (nu) {
913           if (nu->type == CU_BEZIER) {
914             bezt = nu->bezt;
915             a = nu->pntsu;
916             while (a--) {
917               /* exceptions
918                * if handles are hidden then only check the center points.
919                * If the center knot is selected then only use this as the center point.
920                */
921               if ((v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) {
922                 if (bezt->f2 & SELECT) {
923                   calc_tw_center_with_matrix(tbounds, bezt->vec[1], use_mat_local, mat_local);
924                   totsel++;
925                 }
926               }
927               else if (bezt->f2 & SELECT) {
928                 calc_tw_center_with_matrix(tbounds, bezt->vec[1], use_mat_local, mat_local);
929                 totsel++;
930               }
931               else {
932                 if (bezt->f1 & SELECT) {
933                   const float *co = bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0];
934                   calc_tw_center_with_matrix(tbounds, co, use_mat_local, mat_local);
935                   totsel++;
936                 }
937                 if (bezt->f3 & SELECT) {
938                   const float *co = bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2];
939                   calc_tw_center_with_matrix(tbounds, co, use_mat_local, mat_local);
940                   totsel++;
941                 }
942               }
943               bezt++;
944             }
945           }
946           else {
947             bp = nu->bp;
948             a = nu->pntsu * nu->pntsv;
949             while (a--) {
950               if (bp->f1 & SELECT) {
951                 calc_tw_center_with_matrix(tbounds, bp->vec, use_mat_local, mat_local);
952                 totsel++;
953               }
954               bp++;
955             }
956           }
957           nu = nu->next;
958         }
959       }
960       FOREACH_EDIT_OBJECT_END();
961     }
962     else if (obedit->type == OB_MBALL) {
963       FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
964         MetaBall *mb = (MetaBall *)ob_iter->data;
965
966         float mat_local[4][4];
967         if (use_mat_local) {
968           mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
969         }
970
971         for (MetaElem *ml = mb->editelems->first; ml; ml = ml->next) {
972           if (ml->flag & SELECT) {
973             calc_tw_center_with_matrix(tbounds, &ml->x, use_mat_local, mat_local);
974             totsel++;
975           }
976         }
977       }
978       FOREACH_EDIT_OBJECT_END();
979     }
980     else if (obedit->type == OB_LATTICE) {
981       FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
982         Lattice *lt = ((Lattice *)ob_iter->data)->editlatt->latt;
983         BPoint *bp = lt->def;
984         a = lt->pntsu * lt->pntsv * lt->pntsw;
985
986         float mat_local[4][4];
987         if (use_mat_local) {
988           mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
989         }
990
991         while (a--) {
992           if (bp->f1 & SELECT) {
993             calc_tw_center_with_matrix(tbounds, bp->vec, use_mat_local, mat_local);
994             totsel++;
995           }
996           bp++;
997         }
998       }
999       FOREACH_EDIT_OBJECT_END();
1000     }
1001
1002 #undef FOREACH_EDIT_OBJECT_BEGIN
1003 #undef FOREACH_EDIT_OBJECT_END
1004
1005     /* selection center */
1006     if (totsel) {
1007       mul_v3_fl(tbounds->center, 1.0f / (float)totsel);  // centroid!
1008       mul_m4_v3(obedit->obmat, tbounds->center);
1009       mul_m4_v3(obedit->obmat, tbounds->min);
1010       mul_m4_v3(obedit->obmat, tbounds->max);
1011     }
1012   }
1013   else if (ob && (ob->mode & OB_MODE_POSE)) {
1014     invert_m4_m4(ob->imat, ob->obmat);
1015     uint objects_len = 0;
1016     Object **objects = BKE_view_layer_array_from_objects_in_mode(
1017         view_layer, v3d, &objects_len, {.object_mode = OB_MODE_POSE});
1018     for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1019       Object *ob_iter = objects[ob_index];
1020       const bool use_mat_local = (ob_iter != ob);
1021       bPoseChannel *pchan;
1022
1023       /* mislead counting bones... bah. We don't know the gizmo mode, could be mixed */
1024       const int mode = TFM_ROTATION;
1025
1026       const int totsel_iter = count_set_pose_transflags(
1027           ob_iter, mode, V3D_AROUND_CENTER_BOUNDS, NULL);
1028
1029       if (totsel_iter) {
1030         float mat_local[4][4];
1031         if (use_mat_local) {
1032           mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat);
1033         }
1034
1035         /* use channels to get stats */
1036         for (pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) {
1037           Bone *bone = pchan->bone;
1038           if (bone && (bone->flag & BONE_TRANSFORM)) {
1039             calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local);
1040             protectflag_to_drawflags_pchan(rv3d, pchan);
1041           }
1042         }
1043         totsel += totsel_iter;
1044       }
1045     }
1046     MEM_freeN(objects);
1047
1048     if (totsel) {
1049       mul_v3_fl(tbounds->center, 1.0f / (float)totsel);  // centroid!
1050       mul_m4_v3(ob->obmat, tbounds->center);
1051       mul_m4_v3(ob->obmat, tbounds->min);
1052       mul_m4_v3(ob->obmat, tbounds->max);
1053     }
1054   }
1055   else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
1056     /* pass */
1057   }
1058   else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
1059     PTCacheEdit *edit = PE_get_current(scene, ob);
1060     PTCacheEditPoint *point;
1061     PTCacheEditKey *ek;
1062     int k;
1063
1064     if (edit) {
1065       point = edit->points;
1066       for (a = 0; a < edit->totpoint; a++, point++) {
1067         if (point->flag & PEP_HIDE) {
1068           continue;
1069         }
1070
1071         for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
1072           if (ek->flag & PEK_SELECT) {
1073             calc_tw_center(tbounds, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
1074             totsel++;
1075           }
1076         }
1077       }
1078
1079       /* selection center */
1080       if (totsel) {
1081         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);  // centroid!
1082       }
1083     }
1084   }
1085   else {
1086
1087     /* we need the one selected object, if its not active */
1088     base = BASACT(view_layer);
1089     ob = OBACT(view_layer);
1090     if (base && ((base->flag & BASE_SELECTED) == 0)) {
1091       ob = NULL;
1092     }
1093
1094     for (base = view_layer->object_bases.first; base; base = base->next) {
1095       if (!BASE_SELECTED_EDITABLE(v3d, base)) {
1096         continue;
1097       }
1098       if (ob == NULL) {
1099         ob = base->object;
1100       }
1101
1102       /* Get the boundbox out of the evaluated object. */
1103       const BoundBox *bb = NULL;
1104       if (params->use_only_center == false) {
1105         bb = BKE_object_boundbox_get(base->object);
1106       }
1107
1108       if (params->use_only_center || (bb == NULL)) {
1109         calc_tw_center(tbounds, base->object->obmat[3]);
1110       }
1111       else {
1112         for (uint j = 0; j < 8; j++) {
1113           float co[3];
1114           mul_v3_m4v3(co, base->object->obmat, bb->vec[j]);
1115           calc_tw_center(tbounds, co);
1116         }
1117       }
1118       protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
1119       totsel++;
1120     }
1121
1122     /* selection center */
1123     if (totsel) {
1124       mul_v3_fl(tbounds->center, 1.0f / (float)totsel);  // centroid!
1125     }
1126   }
1127
1128   if (totsel == 0) {
1129     unit_m4(rv3d->twmat);
1130   }
1131   else {
1132     copy_v3_v3(rv3d->tw_axis_min, tbounds->axis_min);
1133     copy_v3_v3(rv3d->tw_axis_max, tbounds->axis_max);
1134     copy_m3_m3(rv3d->tw_axis_matrix, tbounds->axis);
1135   }
1136
1137   return totsel;
1138 }
1139
1140 static void gizmo_get_idot(RegionView3D *rv3d, float r_idot[3])
1141 {
1142   float view_vec[3], axis_vec[3];
1143   ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
1144   for (int i = 0; i < 3; i++) {
1145     normalize_v3_v3(axis_vec, rv3d->twmat[i]);
1146     r_idot[i] = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec));
1147   }
1148 }
1149
1150 static void gizmo_prepare_mat(const bContext *C,
1151                               RegionView3D *rv3d,
1152                               const struct TransformBounds *tbounds)
1153 {
1154   Scene *scene = CTX_data_scene(C);
1155   ViewLayer *view_layer = CTX_data_view_layer(C);
1156
1157   switch (scene->toolsettings->transform_pivot_point) {
1158     case V3D_AROUND_CENTER_BOUNDS:
1159     case V3D_AROUND_ACTIVE: {
1160       mid_v3_v3v3(rv3d->twmat[3], tbounds->min, tbounds->max);
1161
1162       if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
1163         bGPdata *gpd = CTX_data_gpencil_data(C);
1164         Object *ob = OBACT(view_layer);
1165         if (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)) {
1166           /* pass */
1167         }
1168         else if (ob != NULL) {
1169           ED_object_calc_active_center(ob, false, rv3d->twmat[3]);
1170         }
1171       }
1172       break;
1173     }
1174     case V3D_AROUND_LOCAL_ORIGINS:
1175     case V3D_AROUND_CENTER_MEDIAN:
1176       copy_v3_v3(rv3d->twmat[3], tbounds->center);
1177       break;
1178     case V3D_AROUND_CURSOR:
1179       copy_v3_v3(rv3d->twmat[3], scene->cursor.location);
1180       break;
1181   }
1182 }
1183
1184 /**
1185  * Sets up \a r_start and \a r_len to define arrow line range.
1186  * Needed to adjust line drawing for combined gizmo axis types.
1187  */
1188 static void gizmo_line_range(const int twtype, const short axis_type, float *r_start, float *r_len)
1189 {
1190   const float ofs = 0.2f;
1191
1192   *r_start = 0.2f;
1193   *r_len = 1.0f;
1194
1195   switch (axis_type) {
1196     case MAN_AXES_TRANSLATE:
1197       if (twtype & V3D_GIZMO_SHOW_OBJECT_SCALE) {
1198         *r_start = *r_len - ofs + 0.075f;
1199       }
1200       if (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) {
1201         *r_len += ofs;
1202       }
1203       break;
1204     case MAN_AXES_SCALE:
1205       if (twtype & (V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE)) {
1206         *r_len -= ofs + 0.025f;
1207       }
1208       break;
1209   }
1210
1211   *r_len -= *r_start;
1212 }
1213
1214 static void gizmo_xform_message_subscribe(wmGizmoGroup *gzgroup,
1215                                           struct wmMsgBus *mbus,
1216                                           Scene *scene,
1217                                           bScreen *screen,
1218                                           ScrArea *sa,
1219                                           ARegion *ar,
1220                                           const void *type_fn)
1221 {
1222   /* Subscribe to view properties */
1223   wmMsgSubscribeValue msg_sub_value_gz_tag_refresh = {
1224       .owner = ar,
1225       .user_data = gzgroup->parent_gzmap,
1226       .notify = WM_gizmo_do_msg_notify_tag_refresh,
1227   };
1228
1229   int orient_flag = 0;
1230   if (type_fn == VIEW3D_GGT_xform_gizmo) {
1231     GizmoGroup *ggd = gzgroup->customdata;
1232     orient_flag = ggd->twtype_init;
1233   }
1234   else if (type_fn == VIEW3D_GGT_xform_cage) {
1235     orient_flag = V3D_GIZMO_SHOW_OBJECT_SCALE;
1236     /* pass */
1237   }
1238   else if (type_fn == VIEW3D_GGT_xform_shear) {
1239     orient_flag = V3D_GIZMO_SHOW_OBJECT_ROTATE;
1240   }
1241   TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(scene,
1242                                                                                    orient_flag);
1243   PointerRNA orient_ref_ptr;
1244   RNA_pointer_create(&scene->id, &RNA_TransformOrientationSlot, orient_slot, &orient_ref_ptr);
1245   const ToolSettings *ts = scene->toolsettings;
1246
1247   PointerRNA scene_ptr;
1248   RNA_id_pointer_create(&scene->id, &scene_ptr);
1249   {
1250     extern PropertyRNA rna_Scene_transform_orientation_slots;
1251     const PropertyRNA *props[] = {
1252         &rna_Scene_transform_orientation_slots,
1253     };
1254     for (int i = 0; i < ARRAY_SIZE(props); i++) {
1255       WM_msg_subscribe_rna(mbus, &scene_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
1256     }
1257   }
1258
1259   if ((ts->transform_pivot_point == V3D_AROUND_CURSOR) ||
1260       (orient_slot->type == V3D_ORIENT_CURSOR)) {
1261     /* We could be more specific here, for now subscribe to any cursor change. */
1262     PointerRNA cursor_ptr;
1263     RNA_pointer_create(&scene->id, &RNA_View3DCursor, &scene->cursor, &cursor_ptr);
1264     WM_msg_subscribe_rna(mbus, &cursor_ptr, NULL, &msg_sub_value_gz_tag_refresh, __func__);
1265   }
1266
1267   {
1268     extern PropertyRNA rna_TransformOrientationSlot_type;
1269     extern PropertyRNA rna_TransformOrientationSlot_use;
1270     const PropertyRNA *props[] = {
1271         &rna_TransformOrientationSlot_type,
1272         &rna_TransformOrientationSlot_use,
1273     };
1274     for (int i = 0; i < ARRAY_SIZE(props); i++) {
1275       if (props[i]) {
1276         WM_msg_subscribe_rna(
1277             mbus, &orient_ref_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
1278       }
1279     }
1280   }
1281
1282   PointerRNA toolsettings_ptr;
1283   RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &toolsettings_ptr);
1284
1285   if (type_fn == VIEW3D_GGT_xform_gizmo) {
1286     extern PropertyRNA rna_ToolSettings_transform_pivot_point;
1287     const PropertyRNA *props[] = {
1288         &rna_ToolSettings_transform_pivot_point,
1289     };
1290     for (int i = 0; i < ARRAY_SIZE(props); i++) {
1291       WM_msg_subscribe_rna(
1292           mbus, &toolsettings_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
1293     }
1294   }
1295
1296   PointerRNA view3d_ptr;
1297   RNA_pointer_create(&screen->id, &RNA_SpaceView3D, sa->spacedata.first, &view3d_ptr);
1298
1299   if (type_fn == VIEW3D_GGT_xform_gizmo) {
1300     GizmoGroup *ggd = gzgroup->customdata;
1301     if (ggd->use_twtype_refresh) {
1302       extern PropertyRNA rna_SpaceView3D_show_gizmo_object_translate;
1303       extern PropertyRNA rna_SpaceView3D_show_gizmo_object_rotate;
1304       extern PropertyRNA rna_SpaceView3D_show_gizmo_object_scale;
1305       const PropertyRNA *props[] = {
1306           &rna_SpaceView3D_show_gizmo_object_translate,
1307           &rna_SpaceView3D_show_gizmo_object_rotate,
1308           &rna_SpaceView3D_show_gizmo_object_scale,
1309       };
1310       for (int i = 0; i < ARRAY_SIZE(props); i++) {
1311         WM_msg_subscribe_rna(mbus, &view3d_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
1312       }
1313     }
1314   }
1315   else if (type_fn == VIEW3D_GGT_xform_cage) {
1316     /* pass */
1317   }
1318   else if (type_fn == VIEW3D_GGT_xform_shear) {
1319     /* pass */
1320   }
1321   else {
1322     BLI_assert(0);
1323   }
1324
1325   WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_gz_tag_refresh);
1326 }
1327
1328 void drawDial3d(const TransInfo *t)
1329 {
1330   if (t->mode == TFM_ROTATION && t->spacetype == SPACE_VIEW3D) {
1331     wmGizmo *gz = wm_gizmomap_modal_get(t->ar->gizmo_map);
1332     if (gz == NULL) {
1333       /* We only draw Dial3d if the operator has been called by a gizmo. */
1334       return;
1335     }
1336
1337     float mat_basis[4][4];
1338     float mat_final[4][4];
1339     float color[4];
1340     float increment;
1341     float line_with = GIZMO_AXIS_LINE_WIDTH + 1.0f;
1342     float scale = UI_DPI_FAC * U.gizmo_size;
1343
1344     int axis_idx;
1345
1346     const TransCon *tc = &(t->con);
1347     if (tc->mode & CON_APPLY) {
1348       if (tc->mode & CON_AXIS0) {
1349         axis_idx = MAN_AXIS_ROT_X;
1350         negate_v3_v3(mat_basis[2], tc->mtx[0]);
1351       }
1352       else if (tc->mode & CON_AXIS1) {
1353         axis_idx = MAN_AXIS_ROT_Y;
1354         negate_v3_v3(mat_basis[2], tc->mtx[1]);
1355       }
1356       else {
1357         BLI_assert((tc->mode & CON_AXIS2) != 0);
1358         axis_idx = MAN_AXIS_ROT_Z;
1359         negate_v3_v3(mat_basis[2], tc->mtx[2]);
1360       }
1361     }
1362     else {
1363       axis_idx = MAN_AXIS_ROT_C;
1364       negate_v3_v3(mat_basis[2], t->orient_matrix[t->orient_axis]);
1365       scale *= 1.2f;
1366       line_with -= 1.0f;
1367     }
1368
1369     copy_v3_v3(mat_basis[3], t->center_global);
1370     mat_basis[2][3] = -dot_v3v3(mat_basis[2], mat_basis[3]);
1371
1372     if (ED_view3d_win_to_3d_on_plane(
1373             t->ar, mat_basis[2], (float[2]){UNPACK2(t->mouse.imval)}, false, mat_basis[1])) {
1374       sub_v3_v3(mat_basis[1], mat_basis[3]);
1375       normalize_v3(mat_basis[1]);
1376       cross_v3_v3v3(mat_basis[0], mat_basis[1], mat_basis[2]);
1377     }
1378     else {
1379       /* The plane and the mouse direction are parallel.
1380        * Calculate a matrix orthogonal to the axis. */
1381       ortho_basis_v3v3_v3(mat_basis[0], mat_basis[1], mat_basis[2]);
1382     }
1383
1384     mat_basis[0][3] = 0.0f;
1385     mat_basis[1][3] = 0.0f;
1386     mat_basis[2][3] = 0.0f;
1387     mat_basis[3][3] = 1.0f;
1388
1389     copy_m4_m4(mat_final, mat_basis);
1390     scale *= ED_view3d_pixel_size_no_ui_scale(t->ar->regiondata, mat_final[3]);
1391     mul_mat3_m4_fl(mat_final, scale);
1392
1393     if ((t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) && activeSnap(t)) {
1394       increment = (t->modifiers & MOD_PRECISION) ? t->snap[2] : t->snap[1];
1395     }
1396     else {
1397       increment = t->snap[0];
1398     }
1399
1400     BLI_assert(axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END);
1401     gizmo_get_axis_color(axis_idx, NULL, color, color);
1402
1403     GPU_depth_test(false);
1404     GPU_blend(true);
1405     GPU_line_smooth(true);
1406
1407     ED_gizmotypes_dial_3d_draw_util(mat_basis,
1408                                     mat_final,
1409                                     line_with,
1410                                     color,
1411                                     false,
1412                                     &(struct Dial3dParams){
1413                                         .draw_options = ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE,
1414                                         .angle_delta = t->values[0],
1415                                         .angle_increment = increment,
1416                                     });
1417
1418     GPU_line_smooth(false);
1419     GPU_depth_test(true);
1420     GPU_blend(false);
1421   }
1422 }
1423
1424 /** \} */
1425
1426 /* -------------------------------------------------------------------- */
1427 /** \name Transform Gizmo
1428  * \{ */
1429
1430 static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup)
1431 {
1432   GizmoGroup *ggd;
1433
1434   ggd = MEM_callocN(sizeof(GizmoGroup), "gizmo_data");
1435
1436   const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
1437   const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
1438   const wmGizmoType *gzt_prim = WM_gizmotype_find("GIZMO_GT_primitive_3d", true);
1439
1440 #define GIZMO_NEW_ARROW(v, draw_style) \
1441   { \
1442     ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); \
1443     RNA_enum_set(ggd->gizmos[v]->ptr, "draw_style", draw_style); \
1444   } \
1445   ((void)0)
1446 #define GIZMO_NEW_DIAL(v, draw_options) \
1447   { \
1448     ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL); \
1449     RNA_enum_set(ggd->gizmos[v]->ptr, "draw_options", draw_options); \
1450   } \
1451   ((void)0)
1452 #define GIZMO_NEW_PRIM(v, draw_style) \
1453   { \
1454     ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_prim, gzgroup, NULL); \
1455     RNA_enum_set(ggd->gizmos[v]->ptr, "draw_style", draw_style); \
1456   } \
1457   ((void)0)
1458
1459   /* add/init widgets - order matters! */
1460   GIZMO_NEW_DIAL(MAN_AXIS_ROT_T, ED_GIZMO_DIAL_DRAW_FLAG_FILL);
1461
1462   GIZMO_NEW_DIAL(MAN_AXIS_SCALE_C, ED_GIZMO_DIAL_DRAW_FLAG_FILL_SELECT);
1463
1464   GIZMO_NEW_ARROW(MAN_AXIS_SCALE_X, ED_GIZMO_ARROW_STYLE_BOX);
1465   GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Y, ED_GIZMO_ARROW_STYLE_BOX);
1466   GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Z, ED_GIZMO_ARROW_STYLE_BOX);
1467
1468   GIZMO_NEW_PRIM(MAN_AXIS_SCALE_XY, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1469   GIZMO_NEW_PRIM(MAN_AXIS_SCALE_YZ, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1470   GIZMO_NEW_PRIM(MAN_AXIS_SCALE_ZX, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1471
1472   GIZMO_NEW_DIAL(MAN_AXIS_ROT_X, ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
1473   GIZMO_NEW_DIAL(MAN_AXIS_ROT_Y, ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
1474   GIZMO_NEW_DIAL(MAN_AXIS_ROT_Z, ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
1475
1476   /* init screen aligned widget last here, looks better, behaves better */
1477   GIZMO_NEW_DIAL(MAN_AXIS_ROT_C, ED_GIZMO_DIAL_DRAW_FLAG_NOP);
1478
1479   GIZMO_NEW_DIAL(MAN_AXIS_TRANS_C, ED_GIZMO_DIAL_DRAW_FLAG_FILL_SELECT);
1480
1481   GIZMO_NEW_ARROW(MAN_AXIS_TRANS_X, ED_GIZMO_ARROW_STYLE_NORMAL);
1482   GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Y, ED_GIZMO_ARROW_STYLE_NORMAL);
1483   GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Z, ED_GIZMO_ARROW_STYLE_NORMAL);
1484
1485   GIZMO_NEW_PRIM(MAN_AXIS_TRANS_XY, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1486   GIZMO_NEW_PRIM(MAN_AXIS_TRANS_YZ, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1487   GIZMO_NEW_PRIM(MAN_AXIS_TRANS_ZX, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1488
1489   ggd->gizmos[MAN_AXIS_ROT_T]->flag |= WM_GIZMO_SELECT_BACKGROUND;
1490
1491   return ggd;
1492 }
1493
1494 /**
1495  * Custom handler for gizmo widgets
1496  */
1497 static int gizmo_modal(bContext *C,
1498                        wmGizmo *widget,
1499                        const wmEvent *event,
1500                        eWM_GizmoFlagTweak UNUSED(tweak_flag))
1501 {
1502   /* Avoid unnecessary updates, partially address: T55458. */
1503   if (ELEM(event->type, TIMER, INBETWEEN_MOUSEMOVE)) {
1504     return OPERATOR_RUNNING_MODAL;
1505   }
1506
1507   ARegion *ar = CTX_wm_region(C);
1508   RegionView3D *rv3d = ar->regiondata;
1509   struct TransformBounds tbounds;
1510
1511   if (ED_transform_calc_gizmo_stats(C,
1512                                     &(struct TransformCalcParams){
1513                                         .use_only_center = true,
1514                                     },
1515                                     &tbounds)) {
1516     gizmo_prepare_mat(C, rv3d, &tbounds);
1517     WM_gizmo_set_matrix_location(widget, rv3d->twmat[3]);
1518   }
1519
1520   ED_region_tag_redraw(ar);
1521
1522   return OPERATOR_RUNNING_MODAL;
1523 }
1524
1525 static void gizmogroup_init_properties_from_twtype(wmGizmoGroup *gzgroup)
1526 {
1527   struct {
1528     wmOperatorType *translate, *rotate, *trackball, *resize;
1529   } ot_store = {NULL};
1530   GizmoGroup *ggd = gzgroup->customdata;
1531
1532   MAN_ITER_AXES_BEGIN (axis, axis_idx) {
1533     const short axis_type = gizmo_get_axis_type(axis_idx);
1534     bool constraint_axis[3] = {1, 0, 0};
1535     PointerRNA *ptr = NULL;
1536
1537     gizmo_get_axis_constraint(axis_idx, constraint_axis);
1538
1539     /* custom handler! */
1540     WM_gizmo_set_fn_custom_modal(axis, gizmo_modal);
1541
1542     switch (axis_idx) {
1543       case MAN_AXIS_TRANS_X:
1544       case MAN_AXIS_TRANS_Y:
1545       case MAN_AXIS_TRANS_Z:
1546       case MAN_AXIS_SCALE_X:
1547       case MAN_AXIS_SCALE_Y:
1548       case MAN_AXIS_SCALE_Z:
1549         if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
1550           int draw_options = 0;
1551           if ((ggd->twtype & (V3D_GIZMO_SHOW_OBJECT_ROTATE | V3D_GIZMO_SHOW_OBJECT_SCALE)) == 0) {
1552             draw_options |= ED_GIZMO_ARROW_DRAW_FLAG_STEM;
1553           }
1554           RNA_enum_set(axis->ptr, "draw_options", draw_options);
1555         }
1556
1557         WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH);
1558         break;
1559       case MAN_AXIS_ROT_X:
1560       case MAN_AXIS_ROT_Y:
1561       case MAN_AXIS_ROT_Z:
1562         /* increased line width for better display */
1563         WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH + 1.0f);
1564         WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true);
1565         break;
1566       case MAN_AXIS_TRANS_XY:
1567       case MAN_AXIS_TRANS_YZ:
1568       case MAN_AXIS_TRANS_ZX:
1569       case MAN_AXIS_SCALE_XY:
1570       case MAN_AXIS_SCALE_YZ:
1571       case MAN_AXIS_SCALE_ZX: {
1572         const float ofs_ax = 7.0f;
1573         const float ofs[3] = {ofs_ax, ofs_ax, 0.0f};
1574         WM_gizmo_set_scale(axis, 0.07f);
1575         WM_gizmo_set_matrix_offset_location(axis, ofs);
1576         WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
1577         break;
1578       }
1579       case MAN_AXIS_TRANS_C:
1580       case MAN_AXIS_ROT_C:
1581       case MAN_AXIS_SCALE_C:
1582       case MAN_AXIS_ROT_T:
1583         WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH);
1584         if (axis_idx == MAN_AXIS_ROT_T) {
1585           WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_HOVER, true);
1586         }
1587         else if (axis_idx == MAN_AXIS_ROT_C) {
1588           WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true);
1589           WM_gizmo_set_scale(axis, 1.2f);
1590         }
1591         else {
1592           WM_gizmo_set_scale(axis, 0.2f);
1593         }
1594         break;
1595     }
1596
1597     switch (axis_type) {
1598       case MAN_AXES_TRANSLATE:
1599         if (ot_store.translate == NULL) {
1600           ot_store.translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
1601         }
1602         ptr = WM_gizmo_operator_set(axis, 0, ot_store.translate, NULL);
1603         break;
1604       case MAN_AXES_ROTATE: {
1605         wmOperatorType *ot_rotate;
1606         if (axis_idx == MAN_AXIS_ROT_T) {
1607           if (ot_store.trackball == NULL) {
1608             ot_store.trackball = WM_operatortype_find("TRANSFORM_OT_trackball", true);
1609           }
1610           ot_rotate = ot_store.trackball;
1611         }
1612         else {
1613           if (ot_store.rotate == NULL) {
1614             ot_store.rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
1615           }
1616           ot_rotate = ot_store.rotate;
1617         }
1618         ptr = WM_gizmo_operator_set(axis, 0, ot_rotate, NULL);
1619         break;
1620       }
1621       case MAN_AXES_SCALE: {
1622         if (ot_store.resize == NULL) {
1623           ot_store.resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1624         }
1625         ptr = WM_gizmo_operator_set(axis, 0, ot_store.resize, NULL);
1626         break;
1627       }
1628     }
1629
1630     if (ptr) {
1631       PropertyRNA *prop;
1632       if (ELEM(true, UNPACK3(constraint_axis))) {
1633         if ((prop = RNA_struct_find_property(ptr, "constraint_axis"))) {
1634           RNA_property_boolean_set_array(ptr, prop, constraint_axis);
1635         }
1636       }
1637
1638       RNA_boolean_set(ptr, "release_confirm", 1);
1639     }
1640   }
1641   MAN_ITER_AXES_END;
1642 }
1643
1644 static void WIDGETGROUP_gizmo_setup(const bContext *C, wmGizmoGroup *gzgroup)
1645 {
1646   GizmoGroup *ggd = gizmogroup_init(gzgroup);
1647
1648   gzgroup->customdata = ggd;
1649
1650   {
1651     ScrArea *sa = CTX_wm_area(C);
1652     const bToolRef *tref = sa->runtime.tool;
1653
1654     ggd->twtype = 0;
1655     if (tref && STREQ(tref->idname, "builtin.move")) {
1656       ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_TRANSLATE;
1657     }
1658     else if (tref && STREQ(tref->idname, "builtin.rotate")) {
1659       ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_ROTATE;
1660     }
1661     else if (tref && STREQ(tref->idname, "builtin.scale")) {
1662       ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_SCALE;
1663     }
1664     else {
1665       /* Setup all gizmos, they can be toggled via 'ToolSettings.gizmo_flag' */
1666       ggd->twtype = V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE |
1667                     V3D_GIZMO_SHOW_OBJECT_SCALE;
1668       ggd->use_twtype_refresh = true;
1669     }
1670     BLI_assert(ggd->twtype != 0);
1671     ggd->twtype_init = ggd->twtype;
1672   }
1673
1674   /* *** set properties for axes *** */
1675   gizmogroup_init_properties_from_twtype(gzgroup);
1676 }
1677
1678 static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
1679 {
1680   GizmoGroup *ggd = gzgroup->customdata;
1681   Scene *scene = CTX_data_scene(C);
1682   ScrArea *sa = CTX_wm_area(C);
1683   View3D *v3d = sa->spacedata.first;
1684   ARegion *ar = CTX_wm_region(C);
1685   RegionView3D *rv3d = ar->regiondata;
1686   struct TransformBounds tbounds;
1687
1688   if (ggd->use_twtype_refresh) {
1689     ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init;
1690     if (ggd->twtype != ggd->twtype_prev) {
1691       ggd->twtype_prev = ggd->twtype;
1692       gizmogroup_init_properties_from_twtype(gzgroup);
1693     }
1694   }
1695
1696   const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(
1697       scene, ggd->twtype_init);
1698
1699   /* skip, we don't draw anything anyway */
1700   if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(
1701                               C,
1702                               &(struct TransformCalcParams){
1703                                   .use_only_center = true,
1704                                   .orientation_type = orient_slot->type + 1,
1705                                   .orientation_index_custom = orient_slot->index_custom,
1706                               },
1707                               &tbounds) == 0))) {
1708     return;
1709   }
1710
1711   gizmo_prepare_mat(C, rv3d, &tbounds);
1712
1713   /* *** set properties for axes *** */
1714
1715   MAN_ITER_AXES_BEGIN (axis, axis_idx) {
1716     const short axis_type = gizmo_get_axis_type(axis_idx);
1717     const int aidx_norm = gizmo_orientation_axis(axis_idx, NULL);
1718
1719     WM_gizmo_set_matrix_location(axis, rv3d->twmat[3]);
1720
1721     switch (axis_idx) {
1722       case MAN_AXIS_TRANS_X:
1723       case MAN_AXIS_TRANS_Y:
1724       case MAN_AXIS_TRANS_Z:
1725       case MAN_AXIS_SCALE_X:
1726       case MAN_AXIS_SCALE_Y:
1727       case MAN_AXIS_SCALE_Z: {
1728         float start_co[3] = {0.0f, 0.0f, 0.0f};
1729         float len;
1730
1731         gizmo_line_range(ggd->twtype, axis_type, &start_co[2], &len);
1732
1733         WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1734         RNA_float_set(axis->ptr, "length", len);
1735
1736         if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
1737           if (ggd->twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) {
1738             /* Avoid rotate and translate arrows overlap. */
1739             start_co[2] += 0.215f;
1740           }
1741         }
1742         WM_gizmo_set_matrix_offset_location(axis, start_co);
1743         WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
1744         break;
1745       }
1746       case MAN_AXIS_ROT_X:
1747       case MAN_AXIS_ROT_Y:
1748       case MAN_AXIS_ROT_Z:
1749         WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1750         break;
1751       case MAN_AXIS_TRANS_XY:
1752       case MAN_AXIS_TRANS_YZ:
1753       case MAN_AXIS_TRANS_ZX:
1754       case MAN_AXIS_SCALE_XY:
1755       case MAN_AXIS_SCALE_YZ:
1756       case MAN_AXIS_SCALE_ZX: {
1757         const float *y_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1];
1758         const float *z_axis = rv3d->twmat[aidx_norm];
1759         WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
1760         break;
1761       }
1762     }
1763   }
1764   MAN_ITER_AXES_END;
1765 }
1766
1767 static void WIDGETGROUP_gizmo_message_subscribe(const bContext *C,
1768                                                 wmGizmoGroup *gzgroup,
1769                                                 struct wmMsgBus *mbus)
1770 {
1771   Scene *scene = CTX_data_scene(C);
1772   bScreen *screen = CTX_wm_screen(C);
1773   ScrArea *sa = CTX_wm_area(C);
1774   ARegion *ar = CTX_wm_region(C);
1775   gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, VIEW3D_GGT_xform_gizmo);
1776 }
1777
1778 static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
1779 {
1780   GizmoGroup *ggd = gzgroup->customdata;
1781   // ScrArea *sa = CTX_wm_area(C);
1782   ARegion *ar = CTX_wm_region(C);
1783   // View3D *v3d = sa->spacedata.first;
1784   RegionView3D *rv3d = ar->regiondata;
1785   float viewinv_m3[3][3];
1786   copy_m3_m4(viewinv_m3, rv3d->viewinv);
1787   float idot[3];
1788
1789   /* when looking through a selected camera, the gizmo can be at the
1790    * exact same position as the view, skip so we don't break selection */
1791   if (ggd->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 1e-6f) {
1792     MAN_ITER_AXES_BEGIN (axis, axis_idx) {
1793       WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
1794     }
1795     MAN_ITER_AXES_END;
1796     return;
1797   }
1798   gizmo_get_idot(rv3d, idot);
1799
1800   /* *** set properties for axes *** */
1801   MAN_ITER_AXES_BEGIN (axis, axis_idx) {
1802     const short axis_type = gizmo_get_axis_type(axis_idx);
1803     /* XXX maybe unset _HIDDEN flag on redraw? */
1804
1805     if (gizmo_is_axis_visible(rv3d, ggd->twtype, idot, axis_type, axis_idx)) {
1806       WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false);
1807     }
1808     else {
1809       WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
1810       continue;
1811     }
1812
1813     float color[4], color_hi[4];
1814     gizmo_get_axis_color(axis_idx, idot, color, color_hi);
1815     WM_gizmo_set_color(axis, color);
1816     WM_gizmo_set_color_highlight(axis, color_hi);
1817
1818     switch (axis_idx) {
1819       case MAN_AXIS_TRANS_C:
1820       case MAN_AXIS_ROT_C:
1821       case MAN_AXIS_SCALE_C:
1822       case MAN_AXIS_ROT_T:
1823         WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]);
1824         break;
1825     }
1826   }
1827   MAN_ITER_AXES_END;
1828
1829   /* Refresh handled above when using view orientation. */
1830   if (!equals_m3m3(viewinv_m3, ggd->prev.viewinv_m3)) {
1831     {
1832       Scene *scene = CTX_data_scene(C);
1833       const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(
1834           scene, ggd->twtype_init);
1835       switch (orient_slot->type) {
1836         case V3D_ORIENT_VIEW: {
1837           WIDGETGROUP_gizmo_refresh(C, gzgroup);
1838           break;
1839         }
1840       }
1841     }
1842     copy_m3_m4(ggd->prev.viewinv_m3, rv3d->viewinv);
1843   }
1844 }
1845
1846 static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, wmGizmoGroup *gzgroup, wmGizmo *gz)
1847 {
1848
1849   GizmoGroup *ggd = gzgroup->customdata;
1850
1851   /* Support gizmo spesific orientation. */
1852   if (gz != ggd->gizmos[MAN_AXIS_ROT_T]) {
1853     Scene *scene = CTX_data_scene(C);
1854     wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0);
1855     PointerRNA *ptr = &gzop->ptr;
1856     PropertyRNA *prop_orient_type = RNA_struct_find_property(ptr, "orient_type");
1857     const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(
1858         scene, ggd->twtype_init);
1859     if (orient_slot == &scene->orientation_slots[SCE_ORIENT_DEFAULT]) {
1860       RNA_property_unset(ptr, prop_orient_type);
1861     }
1862     else {
1863       /* TODO: APIfunction */
1864       int index = BKE_scene_orientation_slot_get_index(orient_slot);
1865       RNA_property_enum_set(ptr, prop_orient_type, index);
1866     }
1867   }
1868
1869   /* Support shift click to constrain axis. */
1870   const int axis_idx = BLI_array_findindex(ggd->gizmos, ARRAY_SIZE(ggd->gizmos), &gz);
1871   int axis = -1;
1872   switch (axis_idx) {
1873     case MAN_AXIS_TRANS_X:
1874     case MAN_AXIS_TRANS_Y:
1875     case MAN_AXIS_TRANS_Z:
1876       axis = axis_idx - MAN_AXIS_TRANS_X;
1877       break;
1878     case MAN_AXIS_SCALE_X:
1879     case MAN_AXIS_SCALE_Y:
1880     case MAN_AXIS_SCALE_Z:
1881       axis = axis_idx - MAN_AXIS_SCALE_X;
1882       break;
1883   }
1884
1885   if (axis != -1) {
1886     wmWindow *win = CTX_wm_window(C);
1887     /* Swap single axis for two-axis constraint. */
1888     bool flip = win->eventstate->shift;
1889     BLI_assert(axis_idx != -1);
1890     const short axis_type = gizmo_get_axis_type(axis_idx);
1891     if (axis_type != MAN_AXES_ROTATE) {
1892       wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0);
1893       PointerRNA *ptr = &gzop->ptr;
1894       PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
1895       if (prop_constraint_axis) {
1896         bool constraint[3] = {false};
1897         constraint[axis] = true;
1898         if (flip) {
1899           for (int i = 0; i < ARRAY_SIZE(constraint); i++) {
1900             constraint[i] = !constraint[i];
1901           }
1902         }
1903         RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint);
1904       }
1905     }
1906   }
1907 }
1908
1909 static bool WIDGETGROUP_gizmo_poll_generic(View3D *v3d)
1910 {
1911   if (v3d->gizmo_flag & V3D_GIZMO_HIDE) {
1912     return false;
1913   }
1914   if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) {
1915     return false;
1916   }
1917   return true;
1918 }
1919
1920 static bool WIDGETGROUP_gizmo_poll_context(const struct bContext *C,
1921                                            struct wmGizmoGroupType *UNUSED(gzgt))
1922 {
1923   ScrArea *sa = CTX_wm_area(C);
1924   View3D *v3d = sa->spacedata.first;
1925   if (!WIDGETGROUP_gizmo_poll_generic(v3d)) {
1926     return false;
1927   }
1928
1929   const bToolRef *tref = sa->runtime.tool;
1930   if (v3d->gizmo_flag & V3D_GIZMO_HIDE_CONTEXT) {
1931     return false;
1932   }
1933   if ((v3d->gizmo_show_object & (V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE |
1934                                  V3D_GIZMO_SHOW_OBJECT_SCALE)) == 0) {
1935     return false;
1936   }
1937
1938   /* Don't show if the tool has a gizmo. */
1939   if (tref && tref->runtime && tref->runtime->gizmo_group[0]) {
1940     return false;
1941   }
1942   return true;
1943 }
1944
1945 static bool WIDGETGROUP_gizmo_poll_tool(const struct bContext *C, struct wmGizmoGroupType *gzgt)
1946 {
1947   if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
1948     return false;
1949   }
1950
1951   return true;
1952   ScrArea *sa = CTX_wm_area(C);
1953   View3D *v3d = sa->spacedata.first;
1954   if (!WIDGETGROUP_gizmo_poll_generic(v3d)) {
1955     return false;
1956   }
1957
1958   if (v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) {
1959     return false;
1960   }
1961
1962   return true;
1963 }
1964
1965 /* Expose as multiple gizmos so tools use one, persistant context another.
1966  * Needed because they use different options which isn't so simple to dynamically update. */
1967
1968 void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt)
1969 {
1970   gzgt->name = "Transform Gizmo";
1971   gzgt->idname = "VIEW3D_GGT_xform_gizmo";
1972
1973   gzgt->flag = WM_GIZMOGROUPTYPE_3D;
1974
1975   gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
1976   gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
1977
1978   gzgt->poll = WIDGETGROUP_gizmo_poll_tool;
1979   gzgt->setup = WIDGETGROUP_gizmo_setup;
1980   gzgt->refresh = WIDGETGROUP_gizmo_refresh;
1981   gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe;
1982   gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare;
1983   gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare;
1984 }
1985
1986 /** Only poll, flag & gzmap_params differ. */
1987 void VIEW3D_GGT_xform_gizmo_context(wmGizmoGroupType *gzgt)
1988 {
1989   gzgt->name = "Transform Gizmo Context";
1990   gzgt->idname = "VIEW3D_GGT_xform_gizmo_context";
1991
1992   gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_PERSISTENT;
1993
1994   gzgt->poll = WIDGETGROUP_gizmo_poll_context;
1995   gzgt->setup = WIDGETGROUP_gizmo_setup;
1996   gzgt->refresh = WIDGETGROUP_gizmo_refresh;
1997   gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe;
1998   gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare;
1999   gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare;
2000 }
2001
2002 /** \} */
2003
2004 /* -------------------------------------------------------------------- */
2005 /** \name Scale Cage Gizmo
2006  * \{ */
2007
2008 struct XFormCageWidgetGroup {
2009   wmGizmo *gizmo;
2010   /* Only for view orientation. */
2011   struct {
2012     float viewinv_m3[3][3];
2013   } prev;
2014 };
2015
2016 static bool WIDGETGROUP_xform_cage_poll(const bContext *C, wmGizmoGroupType *gzgt)
2017 {
2018   if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
2019     return false;
2020   }
2021   View3D *v3d = CTX_wm_view3d(C);
2022   if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
2023     return false;
2024   }
2025   if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) {
2026     return false;
2027   }
2028   return true;
2029 }
2030
2031 static void WIDGETGROUP_xform_cage_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
2032 {
2033   struct XFormCageWidgetGroup *xgzgroup = MEM_mallocN(sizeof(struct XFormCageWidgetGroup),
2034                                                       __func__);
2035   const wmGizmoType *gzt_cage = WM_gizmotype_find("GIZMO_GT_cage_3d", true);
2036   xgzgroup->gizmo = WM_gizmo_new_ptr(gzt_cage, gzgroup, NULL);
2037   wmGizmo *gz = xgzgroup->gizmo;
2038
2039   RNA_enum_set(gz->ptr,
2040                "transform",
2041                ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE | ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE);
2042
2043   gz->color[0] = 1;
2044   gz->color_hi[0] = 1;
2045
2046   gzgroup->customdata = xgzgroup;
2047
2048   {
2049     wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
2050     PointerRNA *ptr;
2051
2052     /* assign operator */
2053     PropertyRNA *prop_release_confirm = NULL;
2054     PropertyRNA *prop_constraint_axis = NULL;
2055
2056     int i = ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
2057     for (int x = 0; x < 3; x++) {
2058       for (int y = 0; y < 3; y++) {
2059         for (int z = 0; z < 3; z++) {
2060           bool constraint[3] = {x != 1, y != 1, z != 1};
2061           ptr = WM_gizmo_operator_set(gz, i, ot_resize, NULL);
2062           if (prop_release_confirm == NULL) {
2063             prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm");
2064             prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
2065           }
2066           RNA_property_boolean_set(ptr, prop_release_confirm, true);
2067           RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint);
2068           i++;
2069         }
2070       }
2071     }
2072   }
2073 }
2074
2075 static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgroup)
2076 {
2077   ARegion *ar = CTX_wm_region(C);
2078   RegionView3D *rv3d = ar->regiondata;
2079   Scene *scene = CTX_data_scene(C);
2080
2081   struct XFormCageWidgetGroup *xgzgroup = gzgroup->customdata;
2082   wmGizmo *gz = xgzgroup->gizmo;
2083
2084   struct TransformBounds tbounds;
2085
2086   const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene,
2087                                                                                SCE_ORIENT_SCALE);
2088
2089   if ((ED_transform_calc_gizmo_stats(C,
2090                                      &(struct TransformCalcParams){
2091                                          .use_local_axis = true,
2092                                          .orientation_type = orient_slot->type + 1,
2093                                          .orientation_index_custom = orient_slot->index_custom,
2094                                      },
2095                                      &tbounds) == 0) ||
2096       equals_v3v3(rv3d->tw_axis_min, rv3d->tw_axis_max)) {
2097     WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
2098   }
2099   else {
2100     gizmo_prepare_mat(C, rv3d, &tbounds);
2101
2102     WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false);
2103     WM_gizmo_set_flag(gz, WM_GIZMO_MOVE_CURSOR, true);
2104
2105     float dims[3];
2106     sub_v3_v3v3(dims, rv3d->tw_axis_max, rv3d->tw_axis_min);
2107     RNA_float_set_array(gz->ptr, "dimensions", dims);
2108     mul_v3_fl(dims, 0.5f);
2109
2110     copy_m4_m3(gz->matrix_offset, rv3d->tw_axis_matrix);
2111     mid_v3_v3v3(gz->matrix_offset[3], rv3d->tw_axis_max, rv3d->tw_axis_min);
2112     mul_m3_v3(rv3d->tw_axis_matrix, gz->matrix_offset[3]);
2113
2114     float matrix_offset_global[4][4];
2115     mul_m4_m4m4(matrix_offset_global, gz->matrix_space, gz->matrix_offset);
2116
2117     PropertyRNA *prop_center_override = NULL;
2118     float center[3];
2119     float center_global[3];
2120     int i = ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
2121     for (int x = 0; x < 3; x++) {
2122       center[0] = (float)(1 - x) * dims[0];
2123       for (int y = 0; y < 3; y++) {
2124         center[1] = (float)(1 - y) * dims[1];
2125         for (int z = 0; z < 3; z++) {
2126           center[2] = (float)(1 - z) * dims[2];
2127           struct wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, i);
2128           if (prop_center_override == NULL) {
2129             prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override");
2130           }
2131           mul_v3_m4v3(center_global, matrix_offset_global, center);
2132           RNA_property_float_set_array(&gzop->ptr, prop_center_override, center_global);
2133           i++;
2134         }
2135       }
2136     }
2137   }
2138
2139   /* Needed to test view orientation changes. */
2140   copy_m3_m4(xgzgroup->prev.viewinv_m3, rv3d->viewinv);
2141 }
2142
2143 static void WIDGETGROUP_xform_cage_message_subscribe(const bContext *C,
2144                                                      wmGizmoGroup *gzgroup,
2145                                                      struct wmMsgBus *mbus)
2146 {
2147   Scene *scene = CTX_data_scene(C);
2148   bScreen *screen = CTX_wm_screen(C);
2149   ScrArea *sa = CTX_wm_area(C);
2150   ARegion *ar = CTX_wm_region(C);
2151   gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, VIEW3D_GGT_xform_cage);
2152 }
2153
2154 static void WIDGETGROUP_xform_cage_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
2155 {
2156   struct XFormCageWidgetGroup *xgzgroup = gzgroup->customdata;
2157   wmGizmo *gz = xgzgroup->gizmo;
2158   ViewLayer *view_layer = CTX_data_view_layer(C);
2159   Object *ob = OBACT(view_layer);
2160   if (ob && ob->mode & OB_MODE_EDIT) {
2161     copy_m4_m4(gz->matrix_space, ob->obmat);
2162   }
2163   else {
2164     unit_m4(gz->matrix_space);
2165   }
2166
2167   RegionView3D *rv3d = CTX_wm_region_view3d(C);
2168   {
2169     Scene *scene = CTX_data_scene(C);
2170     const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene,
2171                                                                                  SCE_ORIENT_SCALE);
2172     switch (orient_slot->type) {
2173       case V3D_ORIENT_VIEW: {
2174         float viewinv_m3[3][3];
2175         copy_m3_m4(viewinv_m3, rv3d->viewinv);
2176         if (!equals_m3m3(viewinv_m3, xgzgroup->prev.viewinv_m3)) {
2177           /* Take care calling refresh from draw_prepare,
2178            * this should be OK because it's only adjusting the cage orientation. */
2179           WIDGETGROUP_xform_cage_refresh(C, gzgroup);
2180         }
2181         break;
2182       }
2183     }
2184   }
2185 }
2186
2187 void VIEW3D_GGT_xform_cage(wmGizmoGroupType *gzgt)
2188 {
2189   gzgt->name = "Transform Cage";
2190   gzgt->idname = "VIEW3D_GGT_xform_cage";
2191
2192   gzgt->flag |= WM_GIZMOGROUPTYPE_3D;
2193
2194   gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
2195   gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
2196
2197   gzgt->poll = WIDGETGROUP_xform_cage_poll;
2198   gzgt->setup = WIDGETGROUP_xform_cage_setup;
2199   gzgt->refresh = WIDGETGROUP_xform_cage_refresh;
2200   gzgt->message_subscribe = WIDGETGROUP_xform_cage_message_subscribe;
2201   gzgt->draw_prepare = WIDGETGROUP_xform_cage_draw_prepare;
2202 }
2203
2204 /** \} */
2205
2206 /* -------------------------------------------------------------------- */
2207 /** \name Transform Shear Gizmo
2208  * \{ */
2209
2210 struct XFormShearWidgetGroup {
2211   wmGizmo *gizmo[3][2];
2212   /* Only for view orientation. */
2213   struct {
2214     float viewinv_m3[3][3];
2215   } prev;
2216 };
2217
2218 static bool WIDGETGROUP_xform_shear_poll(const bContext *C, wmGizmoGroupType *gzgt)
2219 {
2220   if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
2221     return false;
2222   }
2223   View3D *v3d = CTX_wm_view3d(C);
2224   if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
2225     return false;
2226   }
2227   return true;
2228 }
2229
2230 static void WIDGETGROUP_xform_shear_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
2231 {
2232   struct XFormShearWidgetGroup *xgzgroup = MEM_mallocN(sizeof(struct XFormShearWidgetGroup),
2233                                                        __func__);
2234   const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
2235   wmOperatorType *ot_shear = WM_operatortype_find("TRANSFORM_OT_shear", true);
2236
2237   float axis_color[3][3];
2238   for (int i = 0; i < 3; i++) {
2239     UI_GetThemeColor3fv(TH_AXIS_X + i, axis_color[i]);
2240   }
2241
2242   for (int i = 0; i < 3; i++) {
2243     for (int j = 0; j < 2; j++) {
2244       wmGizmo *gz = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
2245       RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX);
2246       const int i_ortho_a = (i + j + 1) % 3;
2247       const int i_ortho_b = (i + (1 - j) + 1) % 3;
2248       interp_v3_v3v3(gz->color, axis_color[i_ortho_a], axis_color[i_ortho_b], 0.75f);
2249       gz->color[3] = 0.5f;
2250       PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_shear, NULL);
2251       RNA_enum_set(ptr, "shear_axis", 0);
2252       RNA_boolean_set(ptr, "release_confirm", 1);
2253       xgzgroup->gizmo[i][j] = gz;
2254     }
2255   }
2256
2257   gzgroup->customdata = xgzgroup;
2258 }
2259
2260 static void WIDGETGROUP_xform_shear_refresh(const bContext *C, wmGizmoGroup *gzgroup)
2261 {
2262   Scene *scene = CTX_data_scene(C);
2263   ARegion *ar = CTX_wm_region(C);
2264   RegionView3D *rv3d = ar->regiondata;
2265
2266   struct XFormShearWidgetGroup *xgzgroup = gzgroup->customdata;
2267   struct TransformBounds tbounds;
2268
2269   const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene,
2270                                                                                SCE_ORIENT_ROTATE);
2271
2272   if (ED_transform_calc_gizmo_stats(C,
2273                                     &(struct TransformCalcParams){
2274                                         .use_local_axis = false,
2275                                         .orientation_type = orient_slot->type + 1,
2276                                         .orientation_index_custom = orient_slot->index_custom,
2277                                     },
2278                                     &tbounds) == 0) {
2279     for (int i = 0; i < 3; i++) {
2280       for (int j = 0; j < 2; j++) {
2281         wmGizmo *gz = xgzgroup->gizmo[i][j];
2282         WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
2283       }
2284     }
2285   }
2286   else {
2287     gizmo_prepare_mat(C, rv3d, &tbounds);
2288     for (int i = 0; i < 3; i++) {
2289       for (int j = 0; j < 2; j++) {
2290         wmGizmo *gz = xgzgroup->gizmo[i][j];
2291         WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false);
2292         WM_gizmo_set_flag(gz, WM_GIZMO_MOVE_CURSOR, true);
2293
2294         wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0);
2295         const int i_ortho_a = (i + j + 1) % 3;
2296         const int i_ortho_b = (i + (1 - j) + 1) % 3;
2297         WM_gizmo_set_matrix_rotation_from_yz_axis(gz, rv3d->twmat[i_ortho_a], rv3d->twmat[i]);
2298         WM_gizmo_set_matrix_location(gz, rv3d->twmat[3]);
2299
2300         RNA_float_set_array(&gzop->ptr, "orient_matrix", &tbounds.axis[0][0]);
2301         RNA_enum_set(&gzop->ptr, "orient_type", orient_slot->type);
2302
2303         RNA_enum_set(&gzop->ptr, "orient_axis", i_ortho_b);
2304         RNA_enum_set(&gzop->ptr, "orient_axis_ortho", i_ortho_a);
2305
2306         mul_v3_fl(gz->matrix_basis[0], 0.5f);
2307         mul_v3_fl(gz->matrix_basis[1], 6.0f);
2308       }
2309     }
2310   }
2311
2312   /* Needed to test view orientation changes. */
2313   copy_m3_m4(xgzgroup->prev.viewinv_m3, rv3d->viewinv);
2314 }
2315
2316 static void WIDGETGROUP_xform_shear_message_subscribe(const bContext *C,
2317                                                       wmGizmoGroup *gzgroup,
2318                                                       struct wmMsgBus *mbus)
2319 {
2320   Scene *scene = CTX_data_scene(C);
2321   bScreen *screen = CTX_wm_screen(C);
2322   ScrArea *sa = CTX_wm_area(C);
2323   ARegion *ar = CTX_wm_region(C);
2324   gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, VIEW3D_GGT_xform_shear);
2325 }
2326
2327 static void WIDGETGROUP_xform_shear_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
2328 {
2329   struct XFormShearWidgetGroup *xgzgroup = gzgroup->customdata;
2330   RegionView3D *rv3d = CTX_wm_region_view3d(C);
2331   {
2332     Scene *scene = CTX_data_scene(C);
2333     /* Shear is like rotate, use the rotate setting. */
2334     const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(
2335         scene, SCE_ORIENT_ROTATE);
2336     switch (orient_slot->type) {
2337       case V3D_ORIENT_VIEW: {
2338         float viewinv_m3[3][3];
2339         copy_m3_m4(viewinv_m3, rv3d->viewinv);
2340         if (!equals_m3m3(viewinv_m3, xgzgroup->prev.viewinv_m3)) {
2341           /* Take care calling refresh from draw_prepare,
2342            * this should be OK because it's only adjusting the cage orientation. */
2343           WIDGETGROUP_xform_shear_refresh(C, gzgroup);
2344         }
2345         break;
2346       }
2347     }
2348   }
2349
2350   /* Basic ordering for drawing only. */
2351   {
2352     LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) {
2353       /* Since we have two pairs of each axis,
2354        * bias the values so gizmos that are orthogonal to the view get priority.
2355        * This means we never default to shearing along
2356        * the view axis in the case of an overlap. */
2357       float axis_order[3], axis_bias[3];
2358       copy_v3_v3(axis_order, gz->matrix_basis[2]);
2359       copy_v3_v3(axis_bias, gz->matrix_basis[1]);
2360       if (dot_v3v3(axis_bias, rv3d->viewinv[2]) < 0.0f) {
2361         negate_v3(axis_bias);
2362       }
2363       madd_v3_v3fl(axis_order, axis_bias, 0.01f);
2364       gz->temp.f = dot_v3v3(rv3d->viewinv[2], axis_order);
2365     }
2366     BLI_listbase_sort(&gzgroup->gizmos, WM_gizmo_cmp_temp_fl_reverse);
2367   }
2368 }
2369
2370 void VIEW3D_GGT_xform_shear(wmGizmoGroupType *gzgt)
2371 {
2372   gzgt->name = "Transform Shear";
2373   gzgt->idname = "VIEW3D_GGT_xform_shear";
2374
2375   gzgt->flag |= WM_GIZMOGROUPTYPE_3D;
2376
2377   gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
2378   gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
2379
2380   gzgt->poll = WIDGETGROUP_xform_shear_poll;
2381   gzgt->setup = WIDGETGROUP_xform_shear_setup;
2382   gzgt->refresh = WIDGETGROUP_xform_shear_refresh;
2383   gzgt->message_subscribe = WIDGETGROUP_xform_shear_message_subscribe;
2384   gzgt->draw_prepare = WIDGETGROUP_xform_shear_draw_prepare;
2385 }
2386
2387 /** \} */