Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / gizmo_library / gizmo_types / cage3d_gizmo.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2014 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edgizmolib
22  *
23  * \name Cage Gizmo
24  *
25  * 2D Gizmo
26  *
27  * \brief Rectangular gizmo acting as a 'cage' around its content.
28  * Interacting scales or translates the gizmo.
29  */
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_math.h"
34
35 #include "BKE_context.h"
36
37 #include "GPU_matrix.h"
38 #include "GPU_shader.h"
39 #include "GPU_immediate.h"
40 #include "GPU_immediate_util.h"
41 #include "GPU_select.h"
42 #include "GPU_state.h"
43
44 #include "RNA_access.h"
45 #include "RNA_define.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "ED_screen.h"
51 #include "ED_view3d.h"
52 #include "ED_gizmo_library.h"
53
54 /* own includes */
55 #include "../gizmo_library_intern.h"
56
57 #define GIZMO_RESIZER_SIZE 10.0f
58 #define GIZMO_MARGIN_OFFSET_SCALE 1.5f
59
60 static void gizmo_calc_matrix_final_no_offset(const wmGizmo *gz,
61                                               float orig_matrix_final_no_offset[4][4],
62                                               bool use_space)
63 {
64   float mat_identity[4][4];
65   struct WM_GizmoMatrixParams params = {NULL};
66   unit_m4(mat_identity);
67   if (use_space == false) {
68     params.matrix_basis = mat_identity;
69   }
70   params.matrix_offset = mat_identity;
71   WM_gizmo_calc_matrix_final_params(gz, &params, orig_matrix_final_no_offset);
72 }
73
74 static void gizmo_calc_rect_view_scale(const wmGizmo *gz, const float dims[3], float scale[3])
75 {
76   UNUSED_VARS(dims);
77
78   /* Unlike cage2d, no need to correct for aspect. */
79   float matrix_final_no_offset[4][4];
80
81   float x_axis[3], y_axis[3], z_axis[3];
82   gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset, false);
83   mul_v3_mat3_m4v3(x_axis, matrix_final_no_offset, gz->matrix_offset[0]);
84   mul_v3_mat3_m4v3(y_axis, matrix_final_no_offset, gz->matrix_offset[1]);
85   mul_v3_mat3_m4v3(z_axis, matrix_final_no_offset, gz->matrix_offset[2]);
86
87   scale[0] = 1.0f / len_v3(x_axis);
88   scale[1] = 1.0f / len_v3(y_axis);
89   scale[2] = 1.0f / len_v3(z_axis);
90 }
91
92 static void gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[3], float margin[3])
93 {
94   float handle_size;
95   if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
96     handle_size = 0.15f;
97   }
98   else {
99     handle_size = GIZMO_RESIZER_SIZE;
100   }
101   // XXX, the scale isn't taking offset into account, we need to calculate scale per handle!
102   // handle_size *= gz->scale_final;
103
104   float scale_xyz[3];
105   gizmo_calc_rect_view_scale(gz, dims, scale_xyz);
106   margin[0] = ((handle_size * scale_xyz[0]));
107   margin[1] = ((handle_size * scale_xyz[1]));
108   margin[2] = ((handle_size * scale_xyz[2]));
109 }
110
111 /* -------------------------------------------------------------------- */
112
113 static void gizmo_rect_pivot_from_scale_part(int part, float r_pt[3], bool r_constrain_axis[3])
114 {
115   if (part >= ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z &&
116       part <= ED_GIZMO_CAGE3D_PART_SCALE_MAX_X_MAX_Y_MAX_Z) {
117     int index = (part - ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z);
118     int range[3];
119     range[2] = index % 3;
120     index = index / 3;
121     range[1] = index % 3;
122     index = index / 3;
123     range[0] = index % 3;
124
125     const float sign[3] = {0.5f, 0.0f, -0.5f};
126     for (int i = 0; i < 3; i++) {
127       r_pt[i] = sign[range[i]];
128       r_constrain_axis[i] = (range[i] == 1);
129     }
130   }
131 }
132
133 /* -------------------------------------------------------------------- */
134 /** \name Box Draw Style
135  *
136  * Useful for 3D views, see: #ED_GIZMO_CAGE2D_STYLE_BOX
137  * \{ */
138
139 static void cage3d_draw_box_corners(const float r[3], const float margin[3], const float color[3])
140 {
141   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
142   UNUSED_VARS(margin);
143
144   immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
145   immUniformColor3fv(color);
146
147   imm_draw_cube_wire_3d(pos, (float[3]){0}, r);
148
149   immUnbindProgram();
150 }
151
152 static void cage3d_draw_box_interaction(const float color[4],
153                                         const int highlighted,
154                                         const float size[3],
155                                         const float margin[3])
156 {
157   if (highlighted >= ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z &&
158       highlighted <= ED_GIZMO_CAGE3D_PART_SCALE_MAX_X_MAX_Y_MAX_Z) {
159     int index = (highlighted - ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z);
160     int range[3];
161     range[2] = index % 3;
162     index = index / 3;
163     range[1] = index % 3;
164     index = index / 3;
165     range[0] = index % 3;
166
167     const float sign[3] = {-1.0f, 0.0f, 1.0f};
168     float co[3];
169
170     for (int i = 0; i < 3; i++) {
171       co[i] = size[i] * sign[range[i]];
172     }
173     const float rad[3] = {margin[0] / 3, margin[1] / 3, margin[2] / 3};
174
175     {
176       uint pos = GPU_vertformat_attr_add(
177           immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
178       immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
179       immUniformColor3fv(color);
180       imm_draw_cube_fill_3d(pos, co, rad);
181       immUnbindProgram();
182     }
183   }
184 }
185
186 /** \} */
187
188 /* -------------------------------------------------------------------- */
189 /** \name Circle Draw Style
190  *
191  * Useful for 2D views, see: #ED_GIZMO_CAGE2D_STYLE_CIRCLE
192  * \{ */
193
194 static void imm_draw_point_aspect_3d(uint pos, const float co[3], const float rad[3], bool solid)
195 {
196   if (solid) {
197     imm_draw_cube_fill_3d(pos, co, rad);
198   }
199   else {
200     imm_draw_cube_wire_3d(pos, co, rad);
201   }
202 }
203
204 static void cage3d_draw_circle_wire(const float r[3],
205                                     const float margin[3],
206                                     const float color[3],
207                                     const int transform_flag,
208                                     const int draw_options)
209 {
210   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
211
212   immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
213   immUniformColor3fv(color);
214
215   imm_draw_cube_wire_3d(pos, (float[3]){0}, r);
216
217 #if 0
218   if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) {
219     if (draw_options & ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE) {
220       const float rad[2] = {margin[0] / 2, margin[1] / 2};
221       const float center[2] = {0.0f, 0.0f};
222
223       immBegin(GPU_PRIM_LINES, 4);
224       immVertex2f(pos, center[0] - rad[0], center[1] - rad[1]);
225       immVertex2f(pos, center[0] + rad[0], center[1] + rad[1]);
226       immVertex2f(pos, center[0] + rad[0], center[1] - rad[1]);
227       immVertex2f(pos, center[0] - rad[0], center[1] + rad[1]);
228       immEnd();
229     }
230   }
231 #else
232   UNUSED_VARS(margin, transform_flag, draw_options);
233 #endif
234
235   immUnbindProgram();
236 }
237
238 static void cage3d_draw_circle_handles(const RegionView3D *rv3d,
239                                        const float matrix_final[4][4],
240                                        const float r[3],
241                                        const float margin[3],
242                                        const float color[3],
243                                        bool solid,
244                                        float scale)
245 {
246   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
247   const float rad[3] = {margin[0] / 3, margin[1] / 3, margin[2] / 3};
248
249   immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
250   immUniformColor3fv(color);
251
252   float sign[3] = {-1.0f, 0.0f, 1.0f};
253   for (int x = 0; x < 3; x++) {
254     for (int y = 0; y < 3; y++) {
255       for (int z = 0; z < 3; z++) {
256         if (x == 1 && y == 1 && z == 1) {
257           continue;
258         }
259         const float co[3] = {r[0] * sign[x], r[1] * sign[y], r[2] * sign[z]};
260         float co_test[3];
261         mul_v3_m4v3(co_test, matrix_final, co);
262         float rad_scale[3];
263         mul_v3_v3fl(rad_scale, rad, ED_view3d_pixel_size(rv3d, co_test) * scale);
264         imm_draw_point_aspect_3d(pos, co, rad_scale, solid);
265       }
266     }
267   }
268
269   immUnbindProgram();
270 }
271
272 /** \} */
273
274 static void gizmo_cage3d_draw_intern(
275     RegionView3D *rv3d, wmGizmo *gz, const bool select, const bool highlight, const int select_id)
276 {
277   // const bool use_clamp = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) == 0;
278   float dims[3];
279   RNA_float_get_array(gz->ptr, "dimensions", dims);
280   float matrix_final[4][4];
281
282   const int transform_flag = RNA_enum_get(gz->ptr, "transform");
283   const int draw_style = RNA_enum_get(gz->ptr, "draw_style");
284   const int draw_options = RNA_enum_get(gz->ptr, "draw_options");
285
286   const float size_real[3] = {dims[0] / 2.0f, dims[1] / 2.0f, dims[2] / 2.0f};
287
288   WM_gizmo_calc_matrix_final(gz, matrix_final);
289
290   GPU_matrix_push();
291   GPU_matrix_mul(matrix_final);
292
293   float margin[3];
294   gizmo_calc_rect_view_margin(gz, dims, margin);
295
296   /* Handy for quick testing draw (if it's outside bounds). */
297   if (false) {
298     GPU_blend(true);
299     uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
300     immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
301     immUniformColor4fv((const float[4]){1, 1, 1, 0.5f});
302     float s = 0.5f;
303     immRectf(pos, -s, -s, s, s);
304     immUnbindProgram();
305     GPU_blend(false);
306   }
307
308   if (select) {
309     /* expand for hotspot */
310 #if 0
311     const float size[3] = {
312         size_real[0] + margin[0] / 2,
313         size_real[1] + margin[1] / 2,
314         size_real[2] + margin[2] / 2,
315     };
316 #else
317     /* just use same value for now. */
318     const float size[3] = {UNPACK3(size_real)};
319 #endif
320
321     if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE) {
322       for (int i = ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
323            i <= ED_GIZMO_CAGE3D_PART_SCALE_MAX_X_MAX_Y_MAX_Z;
324            i++) {
325         if (i == ED_GIZMO_CAGE3D_PART_SCALE_MID_X_MID_Y_MID_Z) {
326           continue;
327         }
328         GPU_select_load_id(select_id | i);
329         cage3d_draw_box_interaction(gz->color, i, size, margin);
330       }
331     }
332     if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) {
333       const int transform_part = ED_GIZMO_CAGE3D_PART_TRANSLATE;
334       GPU_select_load_id(select_id | transform_part);
335       cage3d_draw_box_interaction(gz->color, transform_part, size, margin);
336     }
337   }
338   else {
339 #if 0
340     const rctf _r = {
341         .xmin = -size_real[0],
342         .ymin = -size_real[1],
343         .xmax = size_real[0],
344         .ymax = size_real[1],
345     };
346 #endif
347     if (draw_style == ED_GIZMO_CAGE2D_STYLE_BOX) {
348       /* corner gizmos */
349       GPU_line_width(gz->line_width + 3.0f);
350       cage3d_draw_box_corners(size_real, margin, (const float[3]){0, 0, 0});
351
352       /* corner gizmos */
353       float color[4];
354       gizmo_color_get(gz, highlight, color);
355       GPU_line_width(gz->line_width);
356       cage3d_draw_box_corners(size_real, margin, color);
357
358       bool show = false;
359       if (gz->highlight_part == ED_GIZMO_CAGE3D_PART_TRANSLATE) {
360         /* Only show if we're drawing the center handle
361          * otherwise the entire rectangle is the hotspot. */
362         if (draw_options & ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE) {
363           show = true;
364         }
365       }
366       else {
367         show = true;
368       }
369
370       if (show) {
371         cage3d_draw_box_interaction(gz->color, gz->highlight_part, size_real, margin);
372       }
373     }
374     else if (draw_style == ED_GIZMO_CAGE2D_STYLE_CIRCLE) {
375       float color[4];
376       gizmo_color_get(gz, highlight, color);
377
378       GPU_line_smooth(true);
379       GPU_polygon_smooth(true);
380       GPU_blend(true);
381
382       GPU_line_width(gz->line_width + 3.0f);
383       cage3d_draw_circle_wire(
384           size_real, margin, (const float[3]){0, 0, 0}, transform_flag, draw_options);
385       GPU_line_width(gz->line_width);
386       cage3d_draw_circle_wire(size_real, margin, color, transform_flag, draw_options);
387
388       /* corner gizmos */
389       cage3d_draw_circle_handles(
390           rv3d, matrix_final, size_real, margin, (const float[3]){0, 0, 0}, true, 60);
391       cage3d_draw_circle_handles(rv3d, matrix_final, size_real, margin, color, true, 40);
392
393       GPU_blend(false);
394       GPU_polygon_smooth(false);
395       GPU_line_smooth(false);
396     }
397     else {
398       BLI_assert(0);
399     }
400   }
401
402   GPU_line_width(1.0);
403   GPU_matrix_pop();
404 }
405
406 /**
407  * For when we want to draw 3d cage in 3d views.
408  */
409 static void gizmo_cage3d_draw_select(const bContext *C, wmGizmo *gz, int select_id)
410 {
411   ARegion *ar = CTX_wm_region(C);
412   RegionView3D *rv3d = ar->regiondata;
413   gizmo_cage3d_draw_intern(rv3d, gz, true, false, select_id);
414 }
415
416 static void gizmo_cage3d_draw(const bContext *C, wmGizmo *gz)
417 {
418   ARegion *ar = CTX_wm_region(C);
419   RegionView3D *rv3d = ar->regiondata;
420   const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0;
421   gizmo_cage3d_draw_intern(rv3d, gz, false, is_highlight, -1);
422 }
423
424 static int gizmo_cage3d_get_cursor(wmGizmo *gz)
425 {
426   if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
427     return BC_NSEW_SCROLLCURSOR;
428   }
429
430   return CURSOR_STD;
431 }
432
433 typedef struct RectTransformInteraction {
434   float orig_mouse[3];
435   float orig_matrix_offset[4][4];
436   float orig_matrix_final_no_offset[4][4];
437 } RectTransformInteraction;
438
439 static void gizmo_cage3d_setup(wmGizmo *gz)
440 {
441   gz->flag |= /* WM_GIZMO_DRAW_MODAL | */ /* TODO */
442       WM_GIZMO_DRAW_NO_SCALE;
443 }
444
445 static int gizmo_cage3d_invoke(bContext *C, wmGizmo *gz, const wmEvent *event)
446 {
447   RectTransformInteraction *data = MEM_callocN(sizeof(RectTransformInteraction),
448                                                "cage_interaction");
449
450   copy_m4_m4(data->orig_matrix_offset, gz->matrix_offset);
451   gizmo_calc_matrix_final_no_offset(gz, data->orig_matrix_final_no_offset, true);
452
453   if (gizmo_window_project_3d(
454           C, gz, (const float[2]){UNPACK2(event->mval)}, false, data->orig_mouse) == 0) {
455     zero_v3(data->orig_mouse);
456   }
457
458   gz->interaction_data = data;
459
460   return OPERATOR_RUNNING_MODAL;
461 }
462
463 static int gizmo_cage3d_modal(bContext *C,
464                               wmGizmo *gz,
465                               const wmEvent *event,
466                               eWM_GizmoFlagTweak UNUSED(tweak_flag))
467 {
468   if (event->type != MOUSEMOVE) {
469     return OPERATOR_RUNNING_MODAL;
470   }
471   /* For transform logic to be manageable we operate in -0.5..0.5 2D space,
472    * no matter the size of the rectangle, mouse coords are scaled to unit space.
473    * The mouse coords have been projected into the matrix
474    * so we don't need to worry about axis alignment.
475    *
476    * - The cursor offset are multiplied by 'dims'.
477    * - Matrix translation is also multiplied by 'dims'.
478    */
479   RectTransformInteraction *data = gz->interaction_data;
480   float point_local[3];
481
482   float dims[3];
483   RNA_float_get_array(gz->ptr, "dimensions", dims);
484
485   {
486     float matrix_back[4][4];
487     copy_m4_m4(matrix_back, gz->matrix_offset);
488     copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset);
489
490     bool ok = gizmo_window_project_3d(
491         C, gz, (const float[2]){UNPACK2(event->mval)}, false, point_local);
492     copy_m4_m4(gz->matrix_offset, matrix_back);
493     if (!ok) {
494       return OPERATOR_RUNNING_MODAL;
495     }
496   }
497
498   const int transform_flag = RNA_enum_get(gz->ptr, "transform");
499   wmGizmoProperty *gz_prop;
500
501   gz_prop = WM_gizmo_target_property_find(gz, "matrix");
502   if (gz_prop->type != NULL) {
503     WM_gizmo_target_property_float_get_array(gz, gz_prop, &gz->matrix_offset[0][0]);
504   }
505
506   if (gz->highlight_part == ED_GIZMO_CAGE3D_PART_TRANSLATE) {
507     /* do this to prevent clamping from changing size */
508     copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset);
509     gz->matrix_offset[3][0] = data->orig_matrix_offset[3][0] +
510                               (point_local[0] - data->orig_mouse[0]);
511     gz->matrix_offset[3][1] = data->orig_matrix_offset[3][1] +
512                               (point_local[1] - data->orig_mouse[1]);
513     gz->matrix_offset[3][2] = data->orig_matrix_offset[3][2] +
514                               (point_local[2] - data->orig_mouse[2]);
515   }
516   else if (gz->highlight_part == ED_GIZMO_CAGE3D_PART_ROTATE) {
517     /* TODO (if needed) */
518   }
519   else {
520     /* scale */
521     copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset);
522     float pivot[3];
523     bool constrain_axis[3] = {false};
524
525     if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) {
526       gizmo_rect_pivot_from_scale_part(gz->highlight_part, pivot, constrain_axis);
527     }
528     else {
529       zero_v3(pivot);
530     }
531
532     /* Cursor deltas scaled to (-0.5..0.5). */
533     float delta_orig[3], delta_curr[3];
534
535     for (int i = 0; i < 3; i++) {
536       delta_orig[i] = ((data->orig_mouse[i] - data->orig_matrix_offset[3][i]) / dims[i]) -
537                       pivot[i];
538       delta_curr[i] = ((point_local[i] - data->orig_matrix_offset[3][i]) / dims[i]) - pivot[i];
539     }
540
541     float scale[3] = {1.0f, 1.0f, 1.0f};
542     for (int i = 0; i < 3; i++) {
543       if (constrain_axis[i] == false) {
544         if (delta_orig[i] < 0.0f) {
545           delta_orig[i] *= -1.0f;
546           delta_curr[i] *= -1.0f;
547         }
548         const int sign = signum_i(scale[i]);
549
550         scale[i] = 1.0f + ((delta_curr[i] - delta_orig[i]) / len_v3(data->orig_matrix_offset[i]));
551
552         if ((transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_SIGNED) == 0) {
553           if (sign != signum_i(scale[i])) {
554             scale[i] = 0.0f;
555           }
556         }
557       }
558     }
559
560     if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_UNIFORM) {
561       if (constrain_axis[0] == false && constrain_axis[1] == false) {
562         scale[1] = scale[0] = (scale[1] + scale[0]) / 2.0f;
563       }
564       else if (constrain_axis[0] == false) {
565         scale[1] = scale[0];
566       }
567       else if (constrain_axis[1] == false) {
568         scale[0] = scale[1];
569       }
570       else {
571         BLI_assert(0);
572       }
573     }
574
575     /* scale around pivot */
576     float matrix_scale[4][4];
577     unit_m4(matrix_scale);
578
579     mul_v3_fl(matrix_scale[0], scale[0]);
580     mul_v3_fl(matrix_scale[1], scale[1]);
581     mul_v3_fl(matrix_scale[2], scale[2]);
582
583     transform_pivot_set_m4(
584         matrix_scale,
585         (const float[3]){pivot[0] * dims[0], pivot[1] * dims[1], pivot[2] * dims[2]});
586     mul_m4_m4m4(gz->matrix_offset, data->orig_matrix_offset, matrix_scale);
587   }
588
589   if (gz_prop->type != NULL) {
590     WM_gizmo_target_property_float_set_array(C, gz, gz_prop, &gz->matrix_offset[0][0]);
591   }
592
593   /* tag the region for redraw */
594   ED_region_tag_redraw(CTX_wm_region(C));
595   WM_event_add_mousemove(C);
596
597   return OPERATOR_RUNNING_MODAL;
598 }
599
600 static void gizmo_cage3d_property_update(wmGizmo *gz, wmGizmoProperty *gz_prop)
601 {
602   if (STREQ(gz_prop->type->idname, "matrix")) {
603     if (WM_gizmo_target_property_array_length(gz, gz_prop) == 16) {
604       WM_gizmo_target_property_float_get_array(gz, gz_prop, &gz->matrix_offset[0][0]);
605     }
606     else {
607       BLI_assert(0);
608     }
609   }
610   else {
611     BLI_assert(0);
612   }
613 }
614
615 static void gizmo_cage3d_exit(bContext *C, wmGizmo *gz, const bool cancel)
616 {
617   RectTransformInteraction *data = gz->interaction_data;
618
619   if (!cancel) {
620     return;
621   }
622
623   wmGizmoProperty *gz_prop;
624
625   /* reset properties */
626   gz_prop = WM_gizmo_target_property_find(gz, "matrix");
627   if (gz_prop->type != NULL) {
628     WM_gizmo_target_property_float_set_array(C, gz, gz_prop, &data->orig_matrix_offset[0][0]);
629   }
630
631   copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset);
632 }
633
634 /* -------------------------------------------------------------------- */
635 /** \name Cage Gizmo API
636  *
637  * \{ */
638
639 static void GIZMO_GT_cage_3d(wmGizmoType *gzt)
640 {
641   /* identifiers */
642   gzt->idname = "GIZMO_GT_cage_3d";
643
644   /* api callbacks */
645   gzt->draw = gizmo_cage3d_draw;
646   gzt->draw_select = gizmo_cage3d_draw_select;
647   gzt->setup = gizmo_cage3d_setup;
648   gzt->invoke = gizmo_cage3d_invoke;
649   gzt->property_update = gizmo_cage3d_property_update;
650   gzt->modal = gizmo_cage3d_modal;
651   gzt->exit = gizmo_cage3d_exit;
652   gzt->cursor_get = gizmo_cage3d_get_cursor;
653
654   gzt->struct_size = sizeof(wmGizmo);
655
656   /* rna */
657   static EnumPropertyItem rna_enum_draw_style[] = {
658       {ED_GIZMO_CAGE2D_STYLE_BOX, "BOX", 0, "Box", ""},
659       {ED_GIZMO_CAGE2D_STYLE_CIRCLE, "CIRCLE", 0, "Circle", ""},
660       {0, NULL, 0, NULL, NULL},
661   };
662   static EnumPropertyItem rna_enum_transform[] = {
663       {ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE, "TRANSLATE", 0, "Move", ""},
664       {ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE, "SCALE", 0, "Scale", ""},
665       {ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_UNIFORM, "SCALE_UNIFORM", 0, "Scale Uniform", ""},
666       {0, NULL, 0, NULL, NULL},
667   };
668   static EnumPropertyItem rna_enum_draw_options[] = {
669       {ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE,
670        "XFORM_CENTER_HANDLE",
671        0,
672        "Center Handle",
673        ""},
674       {0, NULL, 0, NULL, NULL},
675   };
676   static float unit_v3[3] = {1.0f, 1.0f, 1.0f};
677   RNA_def_float_vector(
678       gzt->srna, "dimensions", 3, unit_v3, 0, FLT_MAX, "Dimensions", "", 0.0f, FLT_MAX);
679   RNA_def_enum_flag(gzt->srna, "transform", rna_enum_transform, 0, "Transform Options", "");
680   RNA_def_enum(gzt->srna,
681                "draw_style",
682                rna_enum_draw_style,
683                ED_GIZMO_CAGE2D_STYLE_CIRCLE,
684                "Draw Style",
685                "");
686   RNA_def_enum_flag(gzt->srna,
687                     "draw_options",
688                     rna_enum_draw_options,
689                     ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE,
690                     "Draw Options",
691                     "");
692
693   WM_gizmotype_target_property_def(gzt, "matrix", PROP_FLOAT, 16);
694 }
695
696 void ED_gizmotypes_cage_3d(void)
697 {
698   WM_gizmotype_append(GIZMO_GT_cage_3d);
699 }
700
701 /** \} */