Tool System: per space/mode tool support
[blender.git] / source / blender / editors / transform / transform_manipulator_3d.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/transform/transform_manipulator_3d.c
22  *  \ingroup edtransform
23  *
24  * \name 3D Transform Manipulator
25  *
26  * Used for 3D View
27  */
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <math.h>
32 #include <float.h>
33
34 #include "DNA_armature_types.h"
35 #include "DNA_curve_types.h"
36 #include "DNA_gpencil_types.h"
37 #include "DNA_lattice_types.h"
38 #include "DNA_meta_types.h"
39 #include "DNA_screen_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_view3d_types.h"
42
43 #include "BLI_listbase.h"
44 #include "BLI_math.h"
45 #include "BLI_utildefines.h"
46
47 #include "RNA_access.h"
48
49 #include "BKE_action.h"
50 #include "BKE_context.h"
51 #include "BKE_curve.h"
52 #include "BKE_global.h"
53 #include "BKE_particle.h"
54 #include "BKE_pointcache.h"
55 #include "BKE_editmesh.h"
56 #include "BKE_lattice.h"
57 #include "BKE_gpencil.h"
58 #include "BKE_scene.h"
59 #include "BKE_workspace.h"
60
61 #include "BIF_gl.h"
62
63 #include "DEG_depsgraph.h"
64
65 #include "WM_api.h"
66 #include "WM_types.h"
67 #include "WM_message.h"
68
69 #include "ED_armature.h"
70 #include "ED_curve.h"
71 #include "ED_object.h"
72 #include "ED_particle.h"
73 #include "ED_view3d.h"
74 #include "ED_gpencil.h"
75 #include "ED_screen.h"
76 #include "ED_manipulator_library.h"
77
78 #include "UI_resources.h"
79
80 /* local module include */
81 #include "transform.h"
82
83 #include "MEM_guardedalloc.h"
84
85 #include "GPU_select.h"
86 #include "GPU_immediate.h"
87 #include "GPU_matrix.h"
88
89 #include "DEG_depsgraph_query.h"
90
91 /* return codes for select, and drawing flags */
92
93 #define MAN_TRANS_X             (1 << 0)
94 #define MAN_TRANS_Y             (1 << 1)
95 #define MAN_TRANS_Z             (1 << 2)
96 #define MAN_TRANS_C             (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z)
97
98 #define MAN_ROT_X               (1 << 3)
99 #define MAN_ROT_Y               (1 << 4)
100 #define MAN_ROT_Z               (1 << 5)
101 #define MAN_ROT_C               (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z)
102
103 #define MAN_SCALE_X             (1 << 8)
104 #define MAN_SCALE_Y             (1 << 9)
105 #define MAN_SCALE_Z             (1 << 10)
106 #define MAN_SCALE_C             (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z)
107
108 /* threshold for testing view aligned manipulator axis */
109 #define TW_AXIS_DOT_MIN 0.02f
110 #define TW_AXIS_DOT_MAX 0.1f
111
112 /* axes as index */
113 enum {
114         MAN_AXIS_TRANS_X = 0,
115         MAN_AXIS_TRANS_Y,
116         MAN_AXIS_TRANS_Z,
117         MAN_AXIS_TRANS_C,
118
119         MAN_AXIS_TRANS_XY,
120         MAN_AXIS_TRANS_YZ,
121         MAN_AXIS_TRANS_ZX,
122 #define MAN_AXIS_RANGE_TRANS_START MAN_AXIS_TRANS_X
123 #define MAN_AXIS_RANGE_TRANS_END (MAN_AXIS_TRANS_ZX + 1)
124
125         MAN_AXIS_ROT_X,
126         MAN_AXIS_ROT_Y,
127         MAN_AXIS_ROT_Z,
128         MAN_AXIS_ROT_C,
129         MAN_AXIS_ROT_T, /* trackball rotation */
130 #define MAN_AXIS_RANGE_ROT_START MAN_AXIS_ROT_X
131 #define MAN_AXIS_RANGE_ROT_END (MAN_AXIS_ROT_T + 1)
132
133         MAN_AXIS_SCALE_X,
134         MAN_AXIS_SCALE_Y,
135         MAN_AXIS_SCALE_Z,
136         MAN_AXIS_SCALE_C,
137         MAN_AXIS_SCALE_XY,
138         MAN_AXIS_SCALE_YZ,
139         MAN_AXIS_SCALE_ZX,
140 #define MAN_AXIS_RANGE_SCALE_START MAN_AXIS_SCALE_X
141 #define MAN_AXIS_RANGE_SCALE_END (MAN_AXIS_SCALE_ZX + 1)
142
143         MAN_AXIS_LAST = MAN_AXIS_RANGE_SCALE_END,
144 };
145
146 /* axis types */
147 enum {
148         MAN_AXES_ALL = 0,
149         MAN_AXES_TRANSLATE,
150         MAN_AXES_ROTATE,
151         MAN_AXES_SCALE,
152 };
153
154 /* naming from old blender we may combine. */
155 enum {
156         V3D_MANIP_TRANSLATE      = 1,
157         V3D_MANIP_ROTATE         = 2,
158         V3D_MANIP_SCALE          = 4,
159 };
160
161
162 typedef struct ManipulatorGroup {
163         bool all_hidden;
164         int twtype;
165
166         struct wmManipulator *manipulators[MAN_AXIS_LAST];
167 } ManipulatorGroup;
168
169 /* -------------------------------------------------------------------- */
170 /** \name Utilities
171  * \{ */
172
173 /* loop over axes */
174 #define MAN_ITER_AXES_BEGIN(axis, axis_idx) \
175         { \
176                 wmManipulator *axis; \
177                 int axis_idx; \
178                 for (axis_idx = 0; axis_idx < MAN_AXIS_LAST; axis_idx++) { \
179                         axis = manipulator_get_axis_from_index(man, axis_idx);
180
181 #define MAN_ITER_AXES_END \
182                 } \
183         } ((void)0)
184
185 static wmManipulator *manipulator_get_axis_from_index(const ManipulatorGroup *man, const short axis_idx)
186 {
187         BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN_AXIS_TRANS_X, (float)MAN_AXIS_LAST));
188         return man->manipulators[axis_idx];
189 }
190
191 static short manipulator_get_axis_type(const int axis_idx)
192 {
193         if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
194                 return MAN_AXES_TRANSLATE;
195         }
196         if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
197                 return MAN_AXES_ROTATE;
198         }
199         if (axis_idx >= MAN_AXIS_RANGE_SCALE_START && axis_idx < MAN_AXIS_RANGE_SCALE_END) {
200                 return MAN_AXES_SCALE;
201         }
202         BLI_assert(0);
203         return -1;
204 }
205
206 static uint manipulator_orientation_axis(const int axis_idx, bool *r_is_plane)
207 {
208         switch (axis_idx) {
209                 case MAN_AXIS_TRANS_YZ:
210                 case MAN_AXIS_SCALE_YZ:
211                         if (r_is_plane) {
212                                 *r_is_plane = true;
213                         }
214                         ATTR_FALLTHROUGH;
215                 case MAN_AXIS_TRANS_X:
216                 case MAN_AXIS_ROT_X:
217                 case MAN_AXIS_SCALE_X:
218                         return 0;
219
220                 case MAN_AXIS_TRANS_ZX:
221                 case MAN_AXIS_SCALE_ZX:
222                         if (r_is_plane) {
223                                 *r_is_plane = true;
224                         }
225                         ATTR_FALLTHROUGH;
226                 case MAN_AXIS_TRANS_Y:
227                 case MAN_AXIS_ROT_Y:
228                 case MAN_AXIS_SCALE_Y:
229                         return 1;
230
231                 case MAN_AXIS_TRANS_XY:
232                 case MAN_AXIS_SCALE_XY:
233                         if (r_is_plane) {
234                                 *r_is_plane = true;
235                         }
236                         ATTR_FALLTHROUGH;
237                 case MAN_AXIS_TRANS_Z:
238                 case MAN_AXIS_ROT_Z:
239                 case MAN_AXIS_SCALE_Z:
240                         return 2;
241         }
242         return 3;
243 }
244
245 static bool manipulator_is_axis_visible(
246         const RegionView3D *rv3d, const int twtype,
247         const float idot[3], const int axis_type, const int axis_idx)
248 {
249         bool is_plane = false;
250         const uint aidx_norm = manipulator_orientation_axis(axis_idx, &is_plane);
251         /* don't draw axis perpendicular to the view */
252         if (aidx_norm < 3) {
253                 float idot_axis = idot[aidx_norm];
254                 if (is_plane) {
255                         idot_axis = 1.0f - idot_axis;
256                 }
257                 if (idot_axis < TW_AXIS_DOT_MIN) {
258                         return false;
259                 }
260         }
261
262         if ((axis_type == MAN_AXES_TRANSLATE && !(twtype & V3D_MANIP_TRANSLATE)) ||
263             (axis_type == MAN_AXES_ROTATE && !(twtype & V3D_MANIP_ROTATE)) ||
264             (axis_type == MAN_AXES_SCALE && !(twtype & V3D_MANIP_SCALE)))
265         {
266                 return false;
267         }
268
269         switch (axis_idx) {
270                 case MAN_AXIS_TRANS_X:
271                         return (rv3d->twdrawflag & MAN_TRANS_X);
272                 case MAN_AXIS_TRANS_Y:
273                         return (rv3d->twdrawflag & MAN_TRANS_Y);
274                 case MAN_AXIS_TRANS_Z:
275                         return (rv3d->twdrawflag & MAN_TRANS_Z);
276                 case MAN_AXIS_TRANS_C:
277                         return (rv3d->twdrawflag & MAN_TRANS_C);
278                 case MAN_AXIS_ROT_X:
279                         return (rv3d->twdrawflag & MAN_ROT_X);
280                 case MAN_AXIS_ROT_Y:
281                         return (rv3d->twdrawflag & MAN_ROT_Y);
282                 case MAN_AXIS_ROT_Z:
283                         return (rv3d->twdrawflag & MAN_ROT_Z);
284                 case MAN_AXIS_ROT_C:
285                 case MAN_AXIS_ROT_T:
286                         return (rv3d->twdrawflag & MAN_ROT_C);
287                 case MAN_AXIS_SCALE_X:
288                         return (rv3d->twdrawflag & MAN_SCALE_X);
289                 case MAN_AXIS_SCALE_Y:
290                         return (rv3d->twdrawflag & MAN_SCALE_Y);
291                 case MAN_AXIS_SCALE_Z:
292                         return (rv3d->twdrawflag & MAN_SCALE_Z);
293                 case MAN_AXIS_SCALE_C:
294                         return (rv3d->twdrawflag & MAN_SCALE_C && (twtype & V3D_MANIP_TRANSLATE) == 0);
295                 case MAN_AXIS_TRANS_XY:
296                         return (rv3d->twdrawflag & MAN_TRANS_X &&
297                                 rv3d->twdrawflag & MAN_TRANS_Y &&
298                                 (twtype & V3D_MANIP_ROTATE) == 0);
299                 case MAN_AXIS_TRANS_YZ:
300                         return (rv3d->twdrawflag & MAN_TRANS_Y &&
301                                 rv3d->twdrawflag & MAN_TRANS_Z &&
302                                 (twtype & V3D_MANIP_ROTATE) == 0);
303                 case MAN_AXIS_TRANS_ZX:
304                         return (rv3d->twdrawflag & MAN_TRANS_Z &&
305                                 rv3d->twdrawflag & MAN_TRANS_X &&
306                                 (twtype & V3D_MANIP_ROTATE) == 0);
307                 case MAN_AXIS_SCALE_XY:
308                         return (rv3d->twdrawflag & MAN_SCALE_X &&
309                                 rv3d->twdrawflag & MAN_SCALE_Y &&
310                                 (twtype & V3D_MANIP_TRANSLATE) == 0 &&
311                                 (twtype & V3D_MANIP_ROTATE) == 0);
312                 case MAN_AXIS_SCALE_YZ:
313                         return (rv3d->twdrawflag & MAN_SCALE_Y &&
314                                 rv3d->twdrawflag & MAN_SCALE_Z &&
315                                 (twtype & V3D_MANIP_TRANSLATE) == 0 &&
316                                 (twtype & V3D_MANIP_ROTATE) == 0);
317                 case MAN_AXIS_SCALE_ZX:
318                         return (rv3d->twdrawflag & MAN_SCALE_Z &&
319                                 rv3d->twdrawflag & MAN_SCALE_X &&
320                                 (twtype & V3D_MANIP_TRANSLATE) == 0 &&
321                                 (twtype & V3D_MANIP_ROTATE) == 0);
322         }
323         return false;
324 }
325
326 static void manipulator_get_axis_color(
327         const int axis_idx, const float idot[3],
328         float r_col[4], float r_col_hi[4])
329 {
330         /* alpha values for normal/highlighted states */
331         const float alpha = 0.6f;
332         const float alpha_hi = 1.0f;
333         float alpha_fac;
334
335         bool is_plane = false;
336         const int axis_idx_norm = manipulator_orientation_axis(axis_idx, &is_plane);
337         /* get alpha fac based on axis angle, to fade axis out when hiding it because it points towards view */
338         if (axis_idx_norm < 3) {
339                 float idot_axis = idot[axis_idx_norm];
340                 if (is_plane) {
341                         idot_axis = 1.0f - idot_axis;
342                 }
343                 alpha_fac = (idot_axis > TW_AXIS_DOT_MAX) ?
344                         1.0f : (idot_axis < TW_AXIS_DOT_MIN) ?
345                         0.0f : ((idot_axis - TW_AXIS_DOT_MIN) / (TW_AXIS_DOT_MAX - TW_AXIS_DOT_MIN));
346         }
347         else {
348                 /* trackball rotation axis is a special case, we only draw a slight overlay */
349                 alpha_fac = (axis_idx == MAN_AXIS_ROT_T) ? 0.1f : 1.0f;
350         }
351
352         switch (axis_idx) {
353                 case MAN_AXIS_TRANS_X:
354                 case MAN_AXIS_ROT_X:
355                 case MAN_AXIS_SCALE_X:
356                 case MAN_AXIS_TRANS_YZ:
357                 case MAN_AXIS_SCALE_YZ:
358                         UI_GetThemeColor4fv(TH_AXIS_X, r_col);
359                         break;
360                 case MAN_AXIS_TRANS_Y:
361                 case MAN_AXIS_ROT_Y:
362                 case MAN_AXIS_SCALE_Y:
363                 case MAN_AXIS_TRANS_ZX:
364                 case MAN_AXIS_SCALE_ZX:
365                         UI_GetThemeColor4fv(TH_AXIS_Y, r_col);
366                         break;
367                 case MAN_AXIS_TRANS_Z:
368                 case MAN_AXIS_ROT_Z:
369                 case MAN_AXIS_SCALE_Z:
370                 case MAN_AXIS_TRANS_XY:
371                 case MAN_AXIS_SCALE_XY:
372                         UI_GetThemeColor4fv(TH_AXIS_Z, r_col);
373                         break;
374                 case MAN_AXIS_TRANS_C:
375                 case MAN_AXIS_ROT_C:
376                 case MAN_AXIS_SCALE_C:
377                 case MAN_AXIS_ROT_T:
378                         copy_v4_fl(r_col, 1.0f);
379                         break;
380         }
381
382         copy_v4_v4(r_col_hi, r_col);
383
384         r_col[3] = alpha * alpha_fac;
385         r_col_hi[3] = alpha_hi * alpha_fac;
386 }
387
388 static void manipulator_get_axis_constraint(const int axis_idx, int r_axis[3])
389 {
390         zero_v3_int(r_axis);
391
392         switch (axis_idx) {
393                 case MAN_AXIS_TRANS_X:
394                 case MAN_AXIS_ROT_X:
395                 case MAN_AXIS_SCALE_X:
396                         r_axis[0] = 1;
397                         break;
398                 case MAN_AXIS_TRANS_Y:
399                 case MAN_AXIS_ROT_Y:
400                 case MAN_AXIS_SCALE_Y:
401                         r_axis[1] = 1;
402                         break;
403                 case MAN_AXIS_TRANS_Z:
404                 case MAN_AXIS_ROT_Z:
405                 case MAN_AXIS_SCALE_Z:
406                         r_axis[2] = 1;
407                         break;
408                 case MAN_AXIS_TRANS_XY:
409                 case MAN_AXIS_SCALE_XY:
410                         r_axis[0] = r_axis[1] = 1;
411                         break;
412                 case MAN_AXIS_TRANS_YZ:
413                 case MAN_AXIS_SCALE_YZ:
414                         r_axis[1] = r_axis[2] = 1;
415                         break;
416                 case MAN_AXIS_TRANS_ZX:
417                 case MAN_AXIS_SCALE_ZX:
418                         r_axis[2] = r_axis[0] = 1;
419                         break;
420                 default:
421                         break;
422         }
423 }
424
425
426 /* **************** Preparation Stuff **************** */
427
428 /* transform widget center calc helper for below */
429 static void calc_tw_center(struct TransformBounds *tbounds, const float co[3])
430 {
431         minmax_v3v3_v3(tbounds->min, tbounds->max, co);
432         add_v3_v3(tbounds->center, co);
433
434         for (int i = 0; i < 3; i++) {
435                 const float d = dot_v3v3(tbounds->axis[i], co);
436                 tbounds->axis_min[i] = min_ff(d, tbounds->axis_min[i]);
437                 tbounds->axis_max[i] = max_ff(d, tbounds->axis_max[i]);
438         }
439 }
440
441 static void protectflag_to_drawflags(short protectflag, short *drawflags)
442 {
443         if (protectflag & OB_LOCK_LOCX)
444                 *drawflags &= ~MAN_TRANS_X;
445         if (protectflag & OB_LOCK_LOCY)
446                 *drawflags &= ~MAN_TRANS_Y;
447         if (protectflag & OB_LOCK_LOCZ)
448                 *drawflags &= ~MAN_TRANS_Z;
449
450         if (protectflag & OB_LOCK_ROTX)
451                 *drawflags &= ~MAN_ROT_X;
452         if (protectflag & OB_LOCK_ROTY)
453                 *drawflags &= ~MAN_ROT_Y;
454         if (protectflag & OB_LOCK_ROTZ)
455                 *drawflags &= ~MAN_ROT_Z;
456
457         if (protectflag & OB_LOCK_SCALEX)
458                 *drawflags &= ~MAN_SCALE_X;
459         if (protectflag & OB_LOCK_SCALEY)
460                 *drawflags &= ~MAN_SCALE_Y;
461         if (protectflag & OB_LOCK_SCALEZ)
462                 *drawflags &= ~MAN_SCALE_Z;
463 }
464
465 /* for pose mode */
466 static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan)
467 {
468         protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
469 }
470
471 /* for editmode*/
472 static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo)
473 {
474         if (ebo->flag & BONE_EDITMODE_LOCKED) {
475                 protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag);
476         }
477 }
478
479 /* could move into BLI_math however this is only useful for display/editing purposes */
480 static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
481 {
482         /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */
483
484         float cross_vec[3];
485         float quat[4];
486
487         /* this is an un-scientific method to get a vector to cross with
488          * XYZ intentionally YZX */
489         cross_vec[0] = axis[1];
490         cross_vec[1] = axis[2];
491         cross_vec[2] = axis[0];
492
493         /* X-axis */
494         cross_v3_v3v3(gmat[0], cross_vec, axis);
495         normalize_v3(gmat[0]);
496         axis_angle_to_quat(quat, axis, angle);
497         mul_qt_v3(quat, gmat[0]);
498
499         /* Y-axis */
500         axis_angle_to_quat(quat, axis, M_PI_2);
501         copy_v3_v3(gmat[1], gmat[0]);
502         mul_qt_v3(quat, gmat[1]);
503
504         /* Z-axis */
505         copy_v3_v3(gmat[2], axis);
506
507         normalize_m3(gmat);
508 }
509
510
511 static bool test_rotmode_euler(short rotmode)
512 {
513         return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1;
514 }
515
516 bool gimbal_axis(Object *ob, float gmat[3][3])
517 {
518         if (ob->mode & OB_MODE_POSE) {
519                 bPoseChannel *pchan = BKE_pose_channel_active(ob);
520
521                 if (pchan) {
522                         float mat[3][3], tmat[3][3], obmat[3][3];
523                         if (test_rotmode_euler(pchan->rotmode)) {
524                                 eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
525                         }
526                         else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
527                                 axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
528                         }
529                         else { /* quat */
530                                 return 0;
531                         }
532
533
534                         /* apply bone transformation */
535                         mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
536
537                         if (pchan->parent) {
538                                 float parent_mat[3][3];
539
540                                 copy_m3_m4(parent_mat, pchan->parent->pose_mat);
541                                 mul_m3_m3m3(mat, parent_mat, tmat);
542
543                                 /* needed if object transformation isn't identity */
544                                 copy_m3_m4(obmat, ob->obmat);
545                                 mul_m3_m3m3(gmat, obmat, mat);
546                         }
547                         else {
548                                 /* needed if object transformation isn't identity */
549                                 copy_m3_m4(obmat, ob->obmat);
550                                 mul_m3_m3m3(gmat, obmat, tmat);
551                         }
552
553                         normalize_m3(gmat);
554                         return 1;
555                 }
556         }
557         else {
558                 if (test_rotmode_euler(ob->rotmode)) {
559                         eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
560                 }
561                 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
562                         axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle);
563                 }
564                 else { /* quat */
565                         return 0;
566                 }
567
568                 if (ob->parent) {
569                         float parent_mat[3][3];
570                         copy_m3_m4(parent_mat, ob->parent->obmat);
571                         normalize_m3(parent_mat);
572                         mul_m3_m3m3(gmat, parent_mat, gmat);
573                 }
574                 return 1;
575         }
576
577         return 0;
578 }
579
580
581 /* centroid, boundbox, of selection */
582 /* returns total items selected */
583 int ED_transform_calc_manipulator_stats(
584         const bContext *C,
585         const struct TransformCalcParams *params,
586         struct TransformBounds *tbounds)
587 {
588         const Depsgraph *depsgraph = CTX_data_depsgraph(C);
589         ScrArea *sa = CTX_wm_area(C);
590         ARegion *ar = CTX_wm_region(C);
591         Scene *scene = CTX_data_scene(C);
592         ViewLayer *view_layer = CTX_data_view_layer(C);
593         Object *obedit = CTX_data_edit_object(C);
594         View3D *v3d = sa->spacedata.first;
595         RegionView3D *rv3d = ar->regiondata;
596         Base *base;
597         Object *ob = OBACT(view_layer);
598         const Object *ob_eval = NULL;
599         const Object *obedit_eval = NULL;
600         bGPdata *gpd = CTX_data_gpencil_data(C);
601         const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE));
602         int a, totsel = 0;
603         const int pivot_point = scene->toolsettings->transform_pivot_point;
604
605         /* transform widget matrix */
606         unit_m4(rv3d->twmat);
607
608         unit_m3(rv3d->tw_axis_matrix);
609         zero_v3(rv3d->tw_axis_min);
610         zero_v3(rv3d->tw_axis_max);
611
612         rv3d->twdrawflag = 0xFFFF;
613
614         ob_eval = DEG_get_evaluated_object(depsgraph, ob);
615         obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
616
617         /* global, local or normal orientation?
618          * if we could check 'totsel' now, this should be skipped with no selection. */
619         if (ob && !is_gp_edit) {
620                 const short orientation_type = params->orientation_type ? (params->orientation_type - 1) : scene->orientation_type;
621
622                 switch (orientation_type) {
623
624                         case V3D_MANIP_GLOBAL:
625                         {
626                                 break; /* nothing to do */
627                         }
628                         case V3D_MANIP_GIMBAL:
629                         {
630                                 float mat[3][3];
631                                 if (gimbal_axis(ob, mat)) {
632                                         copy_m4_m3(rv3d->twmat, mat);
633                                         break;
634                                 }
635                                 /* if not gimbal, fall through to normal */
636                                 ATTR_FALLTHROUGH;
637                         }
638                         case V3D_MANIP_NORMAL:
639                         {
640                                 if (obedit || ob->mode & OB_MODE_POSE) {
641                                         float mat[3][3];
642                                         ED_getTransformOrientationMatrix(C, mat, pivot_point);
643                                         copy_m4_m3(rv3d->twmat, mat);
644                                         break;
645                                 }
646                                 /* no break we define 'normal' as 'local' in Object mode */
647                                 ATTR_FALLTHROUGH;
648                         }
649                         case V3D_MANIP_LOCAL:
650                         {
651                                 if (ob->mode & OB_MODE_POSE) {
652                                         /* each bone moves on its own local axis, but  to avoid confusion,
653                                          * use the active pones axis for display [#33575], this works as expected on a single bone
654                                          * and users who select many bones will understand whats going on and what local means
655                                          * when they start transforming */
656                                         float mat[3][3];
657                                         ED_getTransformOrientationMatrix(C, mat, pivot_point);
658                                         copy_m4_m3(rv3d->twmat, mat);
659                                         break;
660                                 }
661                                 copy_m4_m4(rv3d->twmat, ob_eval->obmat);
662                                 normalize_m4(rv3d->twmat);
663                                 break;
664                         }
665                         case V3D_MANIP_VIEW:
666                         {
667                                 float mat[3][3];
668                                 copy_m3_m4(mat, rv3d->viewinv);
669                                 normalize_m3(mat);
670                                 copy_m4_m3(rv3d->twmat, mat);
671                                 break;
672                         }
673                         case V3D_MANIP_CURSOR:
674                         {
675                                 float mat[3][3];
676                                 ED_view3d_cursor3d_calc_mat3(scene, v3d, mat);
677                                 copy_m4_m3(rv3d->twmat, mat);
678                                 break;
679                         }
680                         case V3D_MANIP_CUSTOM:
681                         {
682                                 TransformOrientation *custom_orientation = BKE_scene_transform_orientation_find(
683                                         scene, scene->orientation_index_custom);
684                                 float mat[3][3];
685
686                                 if (applyTransformOrientation(custom_orientation, mat, NULL)) {
687                                         copy_m4_m3(rv3d->twmat, mat);
688                                 }
689                                 break;
690                         }
691                 }
692         }
693
694         /* transform widget centroid/center */
695         INIT_MINMAX(tbounds->min, tbounds->max);
696         zero_v3(tbounds->center);
697
698         copy_m3_m4(tbounds->axis, rv3d->twmat);
699         if (params->use_local_axis && (ob && ob->mode & OB_MODE_EDIT)) {
700                 float diff_mat[3][3];
701                 copy_m3_m4(diff_mat, ob_eval->obmat);
702                 normalize_m3(diff_mat);
703                 invert_m3(diff_mat);
704                 mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat);
705                 normalize_m3(tbounds->axis);
706         }
707
708         for (int i = 0; i < 3; i++) {
709                 tbounds->axis_min[i] = +FLT_MAX;
710                 tbounds->axis_max[i] = -FLT_MAX;
711         }
712
713         if (is_gp_edit) {
714                 float diff_mat[4][4];
715                 float fpt[3];
716
717                 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
718                         /* only editable and visible layers are considered */
719                         if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
720
721                                 /* calculate difference matrix if parent object */
722                                 if (gpl->parent != NULL) {
723                                         ED_gpencil_parent_location(gpl, diff_mat);
724                                 }
725
726                                 for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
727                                         /* skip strokes that are invalid for current view */
728                                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
729                                                 continue;
730                                         }
731
732                                         /* we're only interested in selected points here... */
733                                         if (gps->flag & GP_STROKE_SELECT) {
734                                                 bGPDspoint *pt;
735                                                 int i;
736
737                                                 /* Change selection status of all points, then make the stroke match */
738                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
739                                                         if (pt->flag & GP_SPOINT_SELECT) {
740                                                                 if (gpl->parent == NULL) {
741                                                                         calc_tw_center(tbounds, &pt->x);
742                                                                         totsel++;
743                                                                 }
744                                                                 else {
745                                                                         mul_v3_m4v3(fpt, diff_mat, &pt->x);
746                                                                         calc_tw_center(tbounds, fpt);
747                                                                         totsel++;
748                                                                 }
749                                                         }
750                                                 }
751                                         }
752                                 }
753                         }
754                 }
755
756
757                 /* selection center */
758                 if (totsel) {
759                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   /* centroid! */
760                 }
761         }
762         else if (obedit) {
763                 ob = obedit;
764                 ob_eval = obedit_eval;
765                 if (obedit->type == OB_MESH) {
766                         BMEditMesh *em = BKE_editmesh_from_object(obedit);
767                         BMEditSelection ese;
768                         float vec[3] = {0, 0, 0};
769
770                         /* USE LAST SELECTE WITH ACTIVE */
771                         if ((pivot_point == V3D_AROUND_ACTIVE) && BM_select_history_active_get(em->bm, &ese)) {
772                                 BM_editselection_center(&ese, vec);
773                                 calc_tw_center(tbounds, vec);
774                                 totsel = 1;
775                         }
776                         else {
777                                 BMesh *bm = em->bm;
778                                 BMVert *eve;
779
780                                 BMIter iter;
781
782                                 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
783                                         if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
784                                                 if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
785                                                         totsel++;
786                                                         calc_tw_center(tbounds, eve->co);
787                                                 }
788                                         }
789                                 }
790                         }
791                 } /* end editmesh */
792                 else if (obedit->type == OB_ARMATURE) {
793                         bArmature *arm = obedit->data;
794                         EditBone *ebo;
795
796                         if ((pivot_point == V3D_AROUND_ACTIVE) && (ebo = arm->act_edbone)) {
797                                 /* doesn't check selection or visibility intentionally */
798                                 if (ebo->flag & BONE_TIPSEL) {
799                                         calc_tw_center(tbounds, ebo->tail);
800                                         totsel++;
801                                 }
802                                 if ((ebo->flag & BONE_ROOTSEL) ||
803                                     ((ebo->flag & BONE_TIPSEL) == false))  /* ensure we get at least one point */
804                                 {
805                                         calc_tw_center(tbounds, ebo->head);
806                                         totsel++;
807                                 }
808                                 protectflag_to_drawflags_ebone(rv3d, ebo);
809                         }
810                         else {
811                                 for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
812                                         if (EBONE_VISIBLE(arm, ebo)) {
813                                                 if (ebo->flag & BONE_TIPSEL) {
814                                                         calc_tw_center(tbounds, ebo->tail);
815                                                         totsel++;
816                                                 }
817                                                 if ((ebo->flag & BONE_ROOTSEL) &&
818                                                     /* don't include same point multiple times */
819                                                     ((ebo->flag & BONE_CONNECTED) &&
820                                                      (ebo->parent != NULL) &&
821                                                      (ebo->parent->flag & BONE_TIPSEL) &&
822                                                      EBONE_VISIBLE(arm, ebo->parent)) == 0)
823                                                 {
824                                                         calc_tw_center(tbounds, ebo->head);
825                                                         totsel++;
826                                                 }
827                                                 if (ebo->flag & BONE_SELECTED) {
828                                                         protectflag_to_drawflags_ebone(rv3d, ebo);
829                                                 }
830                                         }
831                                 }
832                         }
833                 }
834                 else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
835                         Curve *cu = obedit->data;
836                         float center[3];
837
838                         if ((pivot_point == V3D_AROUND_ACTIVE) && ED_curve_active_center(cu, center)) {
839                                 calc_tw_center(tbounds, center);
840                                 totsel++;
841                         }
842                         else {
843                                 Nurb *nu;
844                                 BezTriple *bezt;
845                                 BPoint *bp;
846                                 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
847
848                                 nu = nurbs->first;
849                                 while (nu) {
850                                         if (nu->type == CU_BEZIER) {
851                                                 bezt = nu->bezt;
852                                                 a = nu->pntsu;
853                                                 while (a--) {
854                                                         /* exceptions
855                                                          * if handles are hidden then only check the center points.
856                                                          * If the center knot is selected then only use this as the center point.
857                                                          */
858                                                         if (cu->drawflag & CU_HIDE_HANDLES) {
859                                                                 if (bezt->f2 & SELECT) {
860                                                                         calc_tw_center(tbounds, bezt->vec[1]);
861                                                                         totsel++;
862                                                                 }
863                                                         }
864                                                         else if (bezt->f2 & SELECT) {
865                                                                 calc_tw_center(tbounds, bezt->vec[1]);
866                                                                 totsel++;
867                                                         }
868                                                         else {
869                                                                 if (bezt->f1 & SELECT) {
870                                                                         calc_tw_center(
871                                                                                 tbounds, bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0]);
872                                                                         totsel++;
873                                                                 }
874                                                                 if (bezt->f3 & SELECT) {
875                                                                         calc_tw_center(
876                                                                                 tbounds, bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2]);
877                                                                         totsel++;
878                                                                 }
879                                                         }
880                                                         bezt++;
881                                                 }
882                                         }
883                                         else {
884                                                 bp = nu->bp;
885                                                 a = nu->pntsu * nu->pntsv;
886                                                 while (a--) {
887                                                         if (bp->f1 & SELECT) {
888                                                                 calc_tw_center(tbounds, bp->vec);
889                                                                 totsel++;
890                                                         }
891                                                         bp++;
892                                                 }
893                                         }
894                                         nu = nu->next;
895                                 }
896                         }
897                 }
898                 else if (obedit->type == OB_MBALL) {
899                         MetaBall *mb = (MetaBall *)obedit->data;
900                         MetaElem *ml;
901
902                         if ((pivot_point == V3D_AROUND_ACTIVE) && (ml = mb->lastelem)) {
903                                 calc_tw_center(tbounds, &ml->x);
904                                 totsel++;
905                         }
906                         else {
907                                 for (ml = mb->editelems->first; ml; ml = ml->next) {
908                                         if (ml->flag & SELECT) {
909                                                 calc_tw_center(tbounds, &ml->x);
910                                                 totsel++;
911                                         }
912                                 }
913                         }
914                 }
915                 else if (obedit->type == OB_LATTICE) {
916                         Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
917                         BPoint *bp;
918
919                         if ((pivot_point == V3D_AROUND_ACTIVE) && (bp = BKE_lattice_active_point_get(lt))) {
920                                 calc_tw_center(tbounds, bp->vec);
921                                 totsel++;
922                         }
923                         else {
924                                 bp = lt->def;
925                                 a = lt->pntsu * lt->pntsv * lt->pntsw;
926                                 while (a--) {
927                                         if (bp->f1 & SELECT) {
928                                                 calc_tw_center(tbounds, bp->vec);
929                                                 totsel++;
930                                         }
931                                         bp++;
932                                 }
933                         }
934                 }
935
936                 /* selection center */
937                 if (totsel) {
938                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   // centroid!
939                         mul_m4_v3(obedit_eval->obmat, tbounds->center);
940                         mul_m4_v3(obedit_eval->obmat, tbounds->min);
941                         mul_m4_v3(obedit_eval->obmat, tbounds->max);
942                 }
943         }
944         else if (ob && (ob->mode & OB_MODE_POSE)) {
945                 bPoseChannel *pchan;
946                 int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the manipulator mode, could be mixed
947                 bool ok = false;
948
949                 if ((pivot_point == V3D_AROUND_ACTIVE) && (pchan = BKE_pose_channel_active(ob))) {
950                         /* doesn't check selection or visibility intentionally */
951                         Bone *bone = pchan->bone;
952                         if (bone) {
953                                 calc_tw_center(tbounds, pchan->pose_head);
954                                 protectflag_to_drawflags_pchan(rv3d, pchan);
955                                 totsel = 1;
956                                 ok = true;
957                         }
958                 }
959                 else {
960                         totsel = count_set_pose_transflags(&mode, 0, ob);
961
962                         if (totsel) {
963                                 /* use channels to get stats */
964                                 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
965                                         Bone *bone = pchan->bone;
966                                         if (bone && (bone->flag & BONE_TRANSFORM)) {
967                                                 calc_tw_center(tbounds, pchan->pose_head);
968                                                 protectflag_to_drawflags_pchan(rv3d, pchan);
969                                         }
970                                 }
971                                 ok = true;
972                         }
973                 }
974
975                 if (ok) {
976                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   // centroid!
977                         mul_m4_v3(ob_eval->obmat, tbounds->center);
978                         mul_m4_v3(ob_eval->obmat, tbounds->min);
979                         mul_m4_v3(ob_eval->obmat, tbounds->max);
980                 }
981         }
982         else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
983                 /* pass */
984         }
985         else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
986                 PTCacheEdit *edit = PE_get_current(scene, ob);
987                 PTCacheEditPoint *point;
988                 PTCacheEditKey *ek;
989                 int k;
990
991                 if (edit) {
992                         point = edit->points;
993                         for (a = 0; a < edit->totpoint; a++, point++) {
994                                 if (point->flag & PEP_HIDE) continue;
995
996                                 for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
997                                         if (ek->flag & PEK_SELECT) {
998                                                 calc_tw_center(tbounds, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
999                                                 totsel++;
1000                                         }
1001                                 }
1002                         }
1003
1004                         /* selection center */
1005                         if (totsel)
1006                                 mul_v3_fl(tbounds->center, 1.0f / (float)totsel);  // centroid!
1007                 }
1008         }
1009         else {
1010
1011                 /* we need the one selected object, if its not active */
1012                 base = BASACT(view_layer);
1013                 ob = OBACT(view_layer);
1014                 if (base && ((base->flag & BASE_SELECTED) == 0)) ob = NULL;
1015
1016                 for (base = view_layer->object_bases.first; base; base = base->next) {
1017                         if (!TESTBASELIB(base)) {
1018                                 continue;
1019                         }
1020                         const Object *base_object_eval = DEG_get_evaluated_object(depsgraph, base->object);
1021                         if (ob == NULL) {
1022                                 ob = base->object;
1023                                 ob_eval = base_object_eval;
1024                         }
1025                         if (params->use_only_center || base_object_eval->bb == NULL) {
1026                                 calc_tw_center(tbounds, base_object_eval->obmat[3]);
1027                         }
1028                         else {
1029                                 for (uint j = 0; j < 8; j++) {
1030                                         float co[3];
1031                                         mul_v3_m4v3(co, base_object_eval->obmat, base_object_eval->bb->vec[j]);
1032                                         calc_tw_center(tbounds, co);
1033                                 }
1034                         }
1035                         protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
1036                         totsel++;
1037                 }
1038
1039                 /* selection center */
1040                 if (totsel) {
1041                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   // centroid!
1042                 }
1043         }
1044
1045         if (totsel == 0) {
1046                 unit_m4(rv3d->twmat);
1047         }
1048         else {
1049                 copy_v3_v3(rv3d->tw_axis_min, tbounds->axis_min);
1050                 copy_v3_v3(rv3d->tw_axis_max, tbounds->axis_max);
1051                 copy_m3_m3(rv3d->tw_axis_matrix, tbounds->axis);
1052         }
1053
1054         return totsel;
1055 }
1056
1057 static void manipulator_get_idot(RegionView3D *rv3d, float r_idot[3])
1058 {
1059         float view_vec[3], axis_vec[3];
1060         ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
1061         for (int i = 0; i < 3; i++) {
1062                 normalize_v3_v3(axis_vec, rv3d->twmat[i]);
1063                 r_idot[i] = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec));
1064         }
1065 }
1066
1067 static void manipulator_prepare_mat(
1068         const bContext *C, View3D *v3d, RegionView3D *rv3d, const struct TransformBounds *tbounds)
1069 {
1070         Scene *scene = CTX_data_scene(C);
1071         ViewLayer *view_layer = CTX_data_view_layer(C);
1072
1073         switch (scene->toolsettings->transform_pivot_point) {
1074                 case V3D_AROUND_CENTER_BOUNDS:
1075                 case V3D_AROUND_ACTIVE:
1076                 {
1077                         bGPdata *gpd = CTX_data_gpencil_data(C);
1078                         Object *ob = OBACT(view_layer);
1079
1080                         if (((scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) &&
1081                              (OBEDIT_FROM_OBACT(ob) == NULL)) &&
1082                             ((gpd == NULL) || !(gpd->flag & GP_DATA_STROKE_EDITMODE)) &&
1083                             (!(ob->mode & OB_MODE_POSE)))
1084                         {
1085                                 copy_v3_v3(rv3d->twmat[3], ob->obmat[3]);
1086                         }
1087                         else {
1088                                 mid_v3_v3v3(rv3d->twmat[3], tbounds->min, tbounds->max);
1089                         }
1090                         break;
1091                 }
1092                 case V3D_AROUND_LOCAL_ORIGINS:
1093                 case V3D_AROUND_CENTER_MEAN:
1094                         copy_v3_v3(rv3d->twmat[3], tbounds->center);
1095                         break;
1096                 case V3D_AROUND_CURSOR:
1097                         copy_v3_v3(rv3d->twmat[3], ED_view3d_cursor3d_get(scene, v3d)->location);
1098                         break;
1099         }
1100 }
1101
1102 /**
1103  * Sets up \a r_start and \a r_len to define arrow line range.
1104  * Needed to adjust line drawing for combined manipulator axis types.
1105  */
1106 static void manipulator_line_range(const int twtype, const short axis_type, float *r_start, float *r_len)
1107 {
1108         const float ofs = 0.2f;
1109
1110         *r_start = 0.2f;
1111         *r_len = 1.0f;
1112
1113         switch (axis_type) {
1114                 case MAN_AXES_TRANSLATE:
1115                         if (twtype & V3D_MANIP_SCALE) {
1116                                 *r_start = *r_len - ofs + 0.075f;
1117                         }
1118                         if (twtype & V3D_MANIP_ROTATE) {
1119                                 *r_len += ofs;
1120                         }
1121                         break;
1122                 case MAN_AXES_SCALE:
1123                         if (twtype & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE)) {
1124                                 *r_len -= ofs + 0.025f;
1125                         }
1126                         break;
1127         }
1128
1129         *r_len -= *r_start;
1130 }
1131
1132 static void manipulator_xform_message_subscribe(
1133         wmManipulatorGroup *mgroup, struct wmMsgBus *mbus,
1134         Scene *scene, bScreen *screen, ScrArea *sa, ARegion *ar, const void *type_fn)
1135 {
1136         /* Subscribe to view properties */
1137         wmMsgSubscribeValue msg_sub_value_mpr_tag_refresh = {
1138                 .owner = ar,
1139                 .user_data = mgroup->parent_mmap,
1140                 .notify = WM_manipulator_do_msg_notify_tag_refresh,
1141         };
1142
1143         PointerRNA scene_ptr;
1144         RNA_id_pointer_create(&scene->id, &scene_ptr);
1145
1146         {
1147                 extern PropertyRNA rna_Scene_transform_orientation;
1148                 extern PropertyRNA rna_Scene_cursor_location;
1149                 const PropertyRNA *props[] = {
1150                         &rna_Scene_transform_orientation,
1151                         (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) ? &rna_Scene_cursor_location : NULL,
1152                 };
1153                 for (int i = 0; i < ARRAY_SIZE(props); i++) {
1154                         if (props[i]) {
1155                                 WM_msg_subscribe_rna(mbus, &scene_ptr, props[i], &msg_sub_value_mpr_tag_refresh, __func__);
1156                         }
1157                 }
1158         }
1159
1160         PointerRNA space_ptr;
1161         RNA_pointer_create(&screen->id, &RNA_SpaceView3D, sa->spacedata.first, &space_ptr);
1162
1163         if (type_fn == TRANSFORM_WGT_manipulator) {
1164                 extern PropertyRNA rna_ToolSettings_transform_pivot_point;
1165                 const PropertyRNA *props[] = {
1166                         &rna_ToolSettings_transform_pivot_point
1167                 };
1168                 for (int i = 0; i < ARRAY_SIZE(props); i++) {
1169                         WM_msg_subscribe_rna(mbus, &space_ptr, props[i], &msg_sub_value_mpr_tag_refresh, __func__);
1170                 }
1171         }
1172         else if (type_fn == VIEW3D_WGT_xform_cage) {
1173                 /* pass */
1174         }
1175         else {
1176                 BLI_assert(0);
1177         }
1178
1179         WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_mpr_tag_refresh);
1180 }
1181
1182 /** \} */
1183
1184
1185 /* -------------------------------------------------------------------- */
1186 /** \name Transform Manipulator
1187  * \{ */
1188
1189 static ManipulatorGroup *manipulatorgroup_init(wmManipulatorGroup *mgroup)
1190 {
1191         ManipulatorGroup *man;
1192
1193         man = MEM_callocN(sizeof(ManipulatorGroup), "manipulator_data");
1194
1195         const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true);
1196         const wmManipulatorType *wt_dial = WM_manipulatortype_find("MANIPULATOR_WT_dial_3d", true);
1197         const wmManipulatorType *wt_prim = WM_manipulatortype_find("MANIPULATOR_WT_primitive_3d", true);
1198
1199 #define MANIPULATOR_NEW_ARROW(v, draw_style) { \
1200         man->manipulators[v] = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL); \
1201         RNA_enum_set(man->manipulators[v]->ptr, "draw_style", draw_style); \
1202 } ((void)0)
1203 #define MANIPULATOR_NEW_DIAL(v, draw_options) { \
1204         man->manipulators[v] = WM_manipulator_new_ptr(wt_dial, mgroup, NULL); \
1205         RNA_enum_set(man->manipulators[v]->ptr, "draw_options", draw_options); \
1206 } ((void)0)
1207 #define MANIPULATOR_NEW_PRIM(v, draw_style) { \
1208         man->manipulators[v] = WM_manipulator_new_ptr(wt_prim, mgroup, NULL); \
1209         RNA_enum_set(man->manipulators[v]->ptr, "draw_style", draw_style); \
1210 } ((void)0)
1211
1212         /* add/init widgets - order matters! */
1213         MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_T, ED_MANIPULATOR_DIAL_DRAW_FLAG_FILL);
1214
1215         MANIPULATOR_NEW_DIAL(MAN_AXIS_SCALE_C, ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP);
1216
1217         MANIPULATOR_NEW_ARROW(MAN_AXIS_SCALE_X, ED_MANIPULATOR_ARROW_STYLE_BOX);
1218         MANIPULATOR_NEW_ARROW(MAN_AXIS_SCALE_Y, ED_MANIPULATOR_ARROW_STYLE_BOX);
1219         MANIPULATOR_NEW_ARROW(MAN_AXIS_SCALE_Z, ED_MANIPULATOR_ARROW_STYLE_BOX);
1220
1221         MANIPULATOR_NEW_PRIM(MAN_AXIS_SCALE_XY, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1222         MANIPULATOR_NEW_PRIM(MAN_AXIS_SCALE_YZ, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1223         MANIPULATOR_NEW_PRIM(MAN_AXIS_SCALE_ZX, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1224
1225         MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_X, ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP);
1226         MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_Y, ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP);
1227         MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_Z, ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP);
1228
1229         /* init screen aligned widget last here, looks better, behaves better */
1230         MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_C, ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP);
1231
1232         MANIPULATOR_NEW_DIAL(MAN_AXIS_TRANS_C, ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP);
1233
1234         MANIPULATOR_NEW_ARROW(MAN_AXIS_TRANS_X, ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1235         MANIPULATOR_NEW_ARROW(MAN_AXIS_TRANS_Y, ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1236         MANIPULATOR_NEW_ARROW(MAN_AXIS_TRANS_Z, ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1237
1238         MANIPULATOR_NEW_PRIM(MAN_AXIS_TRANS_XY, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1239         MANIPULATOR_NEW_PRIM(MAN_AXIS_TRANS_YZ, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1240         MANIPULATOR_NEW_PRIM(MAN_AXIS_TRANS_ZX, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1241
1242         return man;
1243 }
1244
1245 /**
1246  * Custom handler for manipulator widgets
1247  */
1248 static int manipulator_modal(
1249         bContext *C, wmManipulator *widget, const wmEvent *UNUSED(event),
1250         eWM_ManipulatorTweak UNUSED(tweak_flag))
1251 {
1252         const ScrArea *sa = CTX_wm_area(C);
1253         ARegion *ar = CTX_wm_region(C);
1254         View3D *v3d = sa->spacedata.first;
1255         RegionView3D *rv3d = ar->regiondata;
1256         struct TransformBounds tbounds;
1257
1258
1259         if (ED_transform_calc_manipulator_stats(
1260                     C, &(struct TransformCalcParams){
1261                         .use_only_center = true,
1262                     }, &tbounds))
1263         {
1264                 manipulator_prepare_mat(C, v3d, rv3d, &tbounds);
1265                 WM_manipulator_set_matrix_location(widget, rv3d->twmat[3]);
1266         }
1267
1268         ED_region_tag_redraw(ar);
1269
1270         return OPERATOR_RUNNING_MODAL;
1271 }
1272
1273 static void WIDGETGROUP_manipulator_setup(const bContext *C, wmManipulatorGroup *mgroup)
1274 {
1275         ManipulatorGroup *man = manipulatorgroup_init(mgroup);
1276         struct {
1277                 wmOperatorType *translate, *rotate, *trackball, *resize;
1278         } ot_store = {NULL};
1279
1280         mgroup->customdata = man;
1281
1282         {
1283                 /* TODO: support mixing modes again? - it's supported but tool system makes it unobvious. */
1284                 man->twtype = 0;
1285                 WorkSpace *workspace = CTX_wm_workspace(C);
1286                 Scene *scene = CTX_data_scene(C);
1287                 ScrArea *sa = CTX_wm_area(C);
1288                 const bToolKey tkey = {
1289                         .space_type = sa->spacetype,
1290                         .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, NULL, sa->spacetype),
1291                 };
1292                 bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_find(workspace, &tkey);
1293                 wmKeyMap *km = WM_keymap_find_all(C, tref_rt->keymap, sa->spacetype, RGN_TYPE_WINDOW);
1294                 /* Weak, check first event */
1295                 wmKeyMapItem *kmi = km ? km->items.first : NULL;
1296
1297                 if (kmi == NULL) {
1298                         man->twtype |= V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE | V3D_MANIP_SCALE;
1299                 }
1300                 else if (STREQ(kmi->idname, "TRANSFORM_OT_translate")) {
1301                         man->twtype |= V3D_MANIP_TRANSLATE;
1302                 }
1303                 else if (STREQ(kmi->idname, "TRANSFORM_OT_rotate")) {
1304                         man->twtype |= V3D_MANIP_ROTATE;
1305                 }
1306                 else if (STREQ(kmi->idname, "TRANSFORM_OT_resize")) {
1307                         man->twtype |= V3D_MANIP_SCALE;
1308                 }
1309                 BLI_assert(man->twtype != 0);
1310         }
1311
1312         /* *** set properties for axes *** */
1313
1314         MAN_ITER_AXES_BEGIN(axis, axis_idx)
1315         {
1316                 const short axis_type = manipulator_get_axis_type(axis_idx);
1317                 int constraint_axis[3] = {1, 0, 0};
1318                 PointerRNA *ptr;
1319
1320                 manipulator_get_axis_constraint(axis_idx, constraint_axis);
1321
1322                 /* custom handler! */
1323                 WM_manipulator_set_fn_custom_modal(axis, manipulator_modal);
1324
1325                 switch (axis_idx) {
1326                         case MAN_AXIS_TRANS_X:
1327                         case MAN_AXIS_TRANS_Y:
1328                         case MAN_AXIS_TRANS_Z:
1329                         case MAN_AXIS_SCALE_X:
1330                         case MAN_AXIS_SCALE_Y:
1331                         case MAN_AXIS_SCALE_Z:
1332                                 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH);
1333                                 break;
1334                         case MAN_AXIS_ROT_X:
1335                         case MAN_AXIS_ROT_Y:
1336                         case MAN_AXIS_ROT_Z:
1337                                 /* increased line width for better display */
1338                                 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH + 1.0f);
1339                                 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_VALUE, true);
1340                                 break;
1341                         case MAN_AXIS_TRANS_XY:
1342                         case MAN_AXIS_TRANS_YZ:
1343                         case MAN_AXIS_TRANS_ZX:
1344                         case MAN_AXIS_SCALE_XY:
1345                         case MAN_AXIS_SCALE_YZ:
1346                         case MAN_AXIS_SCALE_ZX:
1347                         {
1348                                 const float ofs_ax = 7.0f;
1349                                 const float ofs[3] = {ofs_ax, ofs_ax, 0.0f};
1350                                 WM_manipulator_set_scale(axis, 0.07f);
1351                                 WM_manipulator_set_matrix_offset_location(axis, ofs);
1352                                 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_OFFSET_SCALE, true);
1353                                 break;
1354                         }
1355                         case MAN_AXIS_TRANS_C:
1356                         case MAN_AXIS_ROT_C:
1357                         case MAN_AXIS_SCALE_C:
1358                         case MAN_AXIS_ROT_T:
1359                                 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH);
1360                                 if (axis_idx == MAN_AXIS_ROT_T) {
1361                                         WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_HOVER, true);
1362                                 }
1363                                 else if (axis_idx == MAN_AXIS_ROT_C) {
1364                                         WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_VALUE, true);
1365                                 }
1366                                 else {
1367                                         WM_manipulator_set_scale(axis, 0.2f);
1368                                 }
1369                                 break;
1370                 }
1371
1372                 switch (axis_type) {
1373                         case MAN_AXES_TRANSLATE:
1374                                 if (ot_store.translate == NULL) {
1375                                         ot_store.translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
1376                                 }
1377                                 ptr = WM_manipulator_operator_set(axis, 0, ot_store.translate, NULL);
1378                                 break;
1379                         case MAN_AXES_ROTATE:
1380                         {
1381                                 wmOperatorType *ot_rotate;
1382                                 if (axis_idx == MAN_AXIS_ROT_T) {
1383                                         if (ot_store.trackball == NULL) {
1384                                                 ot_store.trackball = WM_operatortype_find("TRANSFORM_OT_trackball", true);
1385                                         }
1386                                         ot_rotate = ot_store.trackball;
1387                                 }
1388                                 else {
1389                                         if (ot_store.rotate == NULL) {
1390                                                 ot_store.rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
1391                                         }
1392                                         ot_rotate = ot_store.rotate;
1393                                 }
1394                                 ptr = WM_manipulator_operator_set(axis, 0, ot_rotate, NULL);
1395                                 break;
1396                         }
1397                         case MAN_AXES_SCALE:
1398                         {
1399                                 if (ot_store.resize == NULL) {
1400                                         ot_store.resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1401                                 }
1402                                 ptr = WM_manipulator_operator_set(axis, 0, ot_store.resize, NULL);
1403                                 break;
1404                         }
1405                 }
1406
1407                 {
1408                         PropertyRNA *prop;
1409                         if ((prop = RNA_struct_find_property(ptr, "constraint_axis"))) {
1410                                 RNA_property_boolean_set_array(ptr, prop, constraint_axis);
1411                         }
1412                 }
1413
1414                 RNA_boolean_set(ptr, "release_confirm", 1);
1415         }
1416         MAN_ITER_AXES_END;
1417 }
1418
1419 static void WIDGETGROUP_manipulator_refresh(const bContext *C, wmManipulatorGroup *mgroup)
1420 {
1421         ManipulatorGroup *man = mgroup->customdata;
1422         ScrArea *sa = CTX_wm_area(C);
1423         ARegion *ar = CTX_wm_region(C);
1424         View3D *v3d = sa->spacedata.first;
1425         RegionView3D *rv3d = ar->regiondata;
1426         struct TransformBounds tbounds;
1427
1428         /* skip, we don't draw anything anyway */
1429         if ((man->all_hidden =
1430              (ED_transform_calc_manipulator_stats(
1431                      C, &(struct TransformCalcParams){
1432                          .use_only_center = true,
1433                      }, &tbounds) == 0)))
1434         {
1435                 return;
1436         }
1437
1438         manipulator_prepare_mat(C, v3d, rv3d, &tbounds);
1439
1440         /* *** set properties for axes *** */
1441
1442         MAN_ITER_AXES_BEGIN(axis, axis_idx)
1443         {
1444                 const short axis_type = manipulator_get_axis_type(axis_idx);
1445                 const int aidx_norm = manipulator_orientation_axis(axis_idx, NULL);
1446
1447                 WM_manipulator_set_matrix_location(axis, rv3d->twmat[3]);
1448
1449                 switch (axis_idx) {
1450                         case MAN_AXIS_TRANS_X:
1451                         case MAN_AXIS_TRANS_Y:
1452                         case MAN_AXIS_TRANS_Z:
1453                         case MAN_AXIS_SCALE_X:
1454                         case MAN_AXIS_SCALE_Y:
1455                         case MAN_AXIS_SCALE_Z:
1456                         {
1457                                 float start_co[3] = {0.0f, 0.0f, 0.0f};
1458                                 float len;
1459
1460                                 manipulator_line_range(man->twtype, axis_type, &start_co[2], &len);
1461
1462                                 WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1463                                 RNA_float_set(axis->ptr, "length", len);
1464                                 WM_manipulator_set_matrix_offset_location(axis, start_co);
1465                                 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_OFFSET_SCALE, true);
1466                                 break;
1467                         }
1468                         case MAN_AXIS_ROT_X:
1469                         case MAN_AXIS_ROT_Y:
1470                         case MAN_AXIS_ROT_Z:
1471                                 WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1472                                 break;
1473                         case MAN_AXIS_TRANS_XY:
1474                         case MAN_AXIS_TRANS_YZ:
1475                         case MAN_AXIS_TRANS_ZX:
1476                         case MAN_AXIS_SCALE_XY:
1477                         case MAN_AXIS_SCALE_YZ:
1478                         case MAN_AXIS_SCALE_ZX:
1479                         {
1480                                 const float *y_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1];
1481                                 const float *z_axis = rv3d->twmat[aidx_norm];
1482                                 WM_manipulator_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
1483                                 break;
1484                         }
1485                 }
1486         }
1487         MAN_ITER_AXES_END;
1488 }
1489
1490 static void WIDGETGROUP_manipulator_message_subscribe(
1491         const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus)
1492 {
1493         Scene *scene = CTX_data_scene(C);
1494         bScreen *screen = CTX_wm_screen(C);
1495         ScrArea *sa = CTX_wm_area(C);
1496         ARegion *ar = CTX_wm_region(C);
1497         manipulator_xform_message_subscribe(mgroup, mbus, scene, screen, sa, ar, TRANSFORM_WGT_manipulator);
1498 }
1499
1500 static void WIDGETGROUP_manipulator_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
1501 {
1502         ManipulatorGroup *man = mgroup->customdata;
1503         // ScrArea *sa = CTX_wm_area(C);
1504         ARegion *ar = CTX_wm_region(C);
1505         // View3D *v3d = sa->spacedata.first;
1506         RegionView3D *rv3d = ar->regiondata;
1507         float idot[3];
1508
1509         /* when looking through a selected camera, the manipulator can be at the
1510          * exact same position as the view, skip so we don't break selection */
1511         if (man->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 1e-6f) {
1512                 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1513                 {
1514                         WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, true);
1515                 }
1516                 MAN_ITER_AXES_END;
1517                 return;
1518         }
1519         manipulator_get_idot(rv3d, idot);
1520
1521         /* *** set properties for axes *** */
1522
1523         MAN_ITER_AXES_BEGIN(axis, axis_idx)
1524         {
1525                 const short axis_type = manipulator_get_axis_type(axis_idx);
1526                 /* XXX maybe unset _HIDDEN flag on redraw? */
1527                 if (manipulator_is_axis_visible(rv3d, man->twtype, idot, axis_type, axis_idx)) {
1528                         WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, false);
1529                 }
1530                 else {
1531                         WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, true);
1532                         continue;
1533                 }
1534
1535                 float color[4], color_hi[4];
1536                 manipulator_get_axis_color(axis_idx, idot, color, color_hi);
1537                 WM_manipulator_set_color(axis, color);
1538                 WM_manipulator_set_color_highlight(axis, color_hi);
1539
1540                 switch (axis_idx) {
1541                         case MAN_AXIS_TRANS_C:
1542                         case MAN_AXIS_ROT_C:
1543                         case MAN_AXIS_SCALE_C:
1544                         case MAN_AXIS_ROT_T:
1545                                 WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]);
1546                                 break;
1547                 }
1548         }
1549         MAN_ITER_AXES_END;
1550 }
1551
1552 static bool WIDGETGROUP_manipulator_poll(const struct bContext *C, struct wmManipulatorGroupType *wgt)
1553 {
1554         /* it's a given we only use this in 3D view */
1555         ScrArea *sa = CTX_wm_area(C);
1556         View3D *v3d = sa->spacedata.first;
1557         if (v3d && ((v3d->twflag & V3D_MANIPULATOR_DRAW)) == 0) {
1558                 return false;
1559         }
1560
1561         bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
1562         if ((tref_rt == NULL) ||
1563             !STREQ(wgt->idname, tref_rt->manipulator_group))
1564         {
1565                 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
1566                 return false;
1567         }
1568         return true;
1569 }
1570
1571 void TRANSFORM_WGT_manipulator(wmManipulatorGroupType *wgt)
1572 {
1573         wgt->name = "Transform Manipulator";
1574         wgt->idname = "TRANSFORM_WGT_manipulator";
1575
1576         wgt->flag |= WM_MANIPULATORGROUPTYPE_3D;
1577
1578         wgt->mmap_params.spaceid = SPACE_VIEW3D;
1579         wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
1580
1581         wgt->poll = WIDGETGROUP_manipulator_poll;
1582         wgt->setup = WIDGETGROUP_manipulator_setup;
1583         wgt->refresh = WIDGETGROUP_manipulator_refresh;
1584         wgt->message_subscribe = WIDGETGROUP_manipulator_message_subscribe;
1585         wgt->draw_prepare = WIDGETGROUP_manipulator_draw_prepare;
1586 }
1587
1588 /** \} */
1589
1590
1591 /* -------------------------------------------------------------------- */
1592 /** \name Scale Cage Manipulator
1593  * \{ */
1594
1595 struct XFormCageWidgetGroup {
1596         wmManipulator *manipulator;
1597 };
1598
1599 static bool WIDGETGROUP_xform_cage_poll(const bContext *C, wmManipulatorGroupType *wgt)
1600 {
1601         ScrArea *sa = CTX_wm_area(C);
1602         View3D *v3d = sa->spacedata.first;
1603         if (v3d && ((v3d->twflag & V3D_MANIPULATOR_DRAW)) == 0) {
1604                 return false;
1605         }
1606
1607         bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
1608         if (!STREQ(wgt->idname, tref_rt->manipulator_group)) {
1609                 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
1610                 return false;
1611         }
1612         return true;
1613 }
1614
1615 static void WIDGETGROUP_xform_cage_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
1616 {
1617         struct XFormCageWidgetGroup *xmgroup = MEM_mallocN(sizeof(struct XFormCageWidgetGroup), __func__);
1618         const wmManipulatorType *wt_cage = WM_manipulatortype_find("MANIPULATOR_WT_cage_3d", true);
1619         xmgroup->manipulator = WM_manipulator_new_ptr(wt_cage, mgroup, NULL);
1620         wmManipulator *mpr = xmgroup->manipulator;
1621
1622         RNA_enum_set(mpr->ptr, "transform",
1623                      ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE |
1624                      ED_MANIPULATOR_CAGE2D_XFORM_FLAG_TRANSLATE);
1625
1626         mpr->color[0] = 1;
1627         mpr->color_hi[0] = 1;
1628
1629         mgroup->customdata = xmgroup;
1630
1631         {
1632                 wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1633                 PointerRNA *ptr;
1634
1635                 /* assign operator */
1636                 PropertyRNA *prop_release_confirm = NULL;
1637                 PropertyRNA *prop_constraint_axis = NULL;
1638
1639                 int i = ED_MANIPULATOR_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
1640                 for (int x = 0; x < 3; x++) {
1641                         for (int y = 0; y < 3; y++) {
1642                                 for (int z = 0; z < 3; z++) {
1643                                         int constraint[3] = {x != 1, y != 1, z != 1};
1644                                         ptr = WM_manipulator_operator_set(mpr, i, ot_resize, NULL);
1645                                         if (prop_release_confirm == NULL) {
1646                                                 prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm");
1647                                                 prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
1648                                         }
1649                                         RNA_property_boolean_set(ptr, prop_release_confirm, true);
1650                                         RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint);
1651                                         i++;
1652                                 }
1653                         }
1654                 }
1655         }
1656 }
1657
1658 static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmManipulatorGroup *mgroup)
1659 {
1660         ScrArea *sa = CTX_wm_area(C);
1661         View3D *v3d = sa->spacedata.first;
1662         ARegion *ar = CTX_wm_region(C);
1663         RegionView3D *rv3d = ar->regiondata;
1664
1665         struct XFormCageWidgetGroup *xmgroup = mgroup->customdata;
1666         wmManipulator *mpr = xmgroup->manipulator;
1667
1668         struct TransformBounds tbounds;
1669
1670         if ((ED_transform_calc_manipulator_stats(
1671                      C, &(struct TransformCalcParams) {
1672                          .use_local_axis = true,
1673                      }, &tbounds) == 0) ||
1674             equals_v3v3(rv3d->tw_axis_min, rv3d->tw_axis_max))
1675         {
1676                 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, true);
1677         }
1678         else {
1679                 manipulator_prepare_mat(C, v3d, rv3d, &tbounds);
1680
1681                 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, false);
1682                 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_GRAB_CURSOR, true);
1683
1684                 float dims[3];
1685                 sub_v3_v3v3(dims, rv3d->tw_axis_max, rv3d->tw_axis_min);
1686                 RNA_float_set_array(mpr->ptr, "dimensions", dims);
1687                 mul_v3_fl(dims, 0.5f);
1688
1689                 copy_m4_m3(mpr->matrix_offset, rv3d->tw_axis_matrix);
1690                 mid_v3_v3v3(mpr->matrix_offset[3], rv3d->tw_axis_max, rv3d->tw_axis_min);
1691                 mul_m3_v3(rv3d->tw_axis_matrix, mpr->matrix_offset[3]);
1692
1693                 PropertyRNA *prop_center_override = NULL;
1694                 float center[3];
1695                 float center_global[3];
1696                 int i = ED_MANIPULATOR_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
1697                 for (int x = 0; x < 3; x++) {
1698                         center[0] = (float)(1 - x) * dims[0];
1699                         for (int y = 0; y < 3; y++) {
1700                                 center[1] = (float)(1 - y) * dims[1];
1701                                 for (int z = 0; z < 3; z++) {
1702                                         center[2] = (float)(1 - z) * dims[2];
1703                                         struct wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, i);
1704                                         if (prop_center_override == NULL) {
1705                                                 prop_center_override = RNA_struct_find_property(&mpop->ptr, "center_override");
1706                                         }
1707                                         mul_v3_m4v3(center_global, mpr->matrix_offset, center);
1708                                         RNA_property_float_set_array(&mpop->ptr, prop_center_override, center_global);
1709                                         i++;
1710                                 }
1711                         }
1712                 }
1713         }
1714 }
1715
1716 static void WIDGETGROUP_xform_cage_message_subscribe(
1717         const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus)
1718 {
1719         Scene *scene = CTX_data_scene(C);
1720         bScreen *screen = CTX_wm_screen(C);
1721         ScrArea *sa = CTX_wm_area(C);
1722         ARegion *ar = CTX_wm_region(C);
1723         manipulator_xform_message_subscribe(mgroup, mbus, scene, screen, sa, ar, VIEW3D_WGT_xform_cage);
1724 }
1725
1726 static void WIDGETGROUP_xform_cage_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
1727 {
1728         struct XFormCageWidgetGroup *xmgroup = mgroup->customdata;
1729         wmManipulator *mpr = xmgroup->manipulator;
1730
1731         ViewLayer *view_layer = CTX_data_view_layer(C);
1732         Object *ob = OBACT(view_layer);
1733         if (ob && ob->mode & OB_MODE_EDIT) {
1734                 copy_m4_m4(mpr->matrix_space, ob->obmat);
1735         }
1736         else {
1737                 unit_m4(mpr->matrix_space);
1738         }
1739 }
1740
1741 void VIEW3D_WGT_xform_cage(wmManipulatorGroupType *wgt)
1742 {
1743         wgt->name = "Transform Cage";
1744         wgt->idname = "VIEW3D_WGT_xform_cage";
1745
1746         wgt->flag |= WM_MANIPULATORGROUPTYPE_3D;
1747
1748         wgt->mmap_params.spaceid = SPACE_VIEW3D;
1749         wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
1750
1751         wgt->poll = WIDGETGROUP_xform_cage_poll;
1752         wgt->setup = WIDGETGROUP_xform_cage_setup;
1753         wgt->refresh = WIDGETGROUP_xform_cage_refresh;
1754         wgt->message_subscribe = WIDGETGROUP_xform_cage_message_subscribe;
1755         wgt->draw_prepare = WIDGETGROUP_xform_cage_draw_prepare;
1756 }
1757
1758 /** \} */