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 wm
22  */
23 
24 #include "MEM_guardedalloc.h"
25 
26 #include "BLI_listbase.h"
27 #include "BLI_math.h"
28 
29 #include "BKE_context.h"
30 
31 #include "GPU_batch.h"
32 
33 #include "RNA_access.h"
34 #include "RNA_define.h"
35 
36 #include "BKE_global.h"
37 #include "BKE_idprop.h"
38 #include "BKE_main.h"
39 
40 #include "WM_api.h"
41 #include "WM_toolsystem.h"
42 #include "WM_types.h"
43 
44 #include "ED_screen.h"
45 #include "ED_view3d.h"
46 
47 #include "UI_interface.h"
48 
49 #ifdef WITH_PYTHON
50 #  include "BPY_extern.h"
51 #endif
52 
53 /* only for own init/exit calls (wm_gizmotype_init/wm_gizmotype_free) */
54 #include "wm.h"
55 
56 /* own includes */
57 #include "wm_gizmo_intern.h"
58 #include "wm_gizmo_wmapi.h"
59 
60 static void wm_gizmo_register(wmGizmoGroup *gzgroup, wmGizmo *gz);
61 
62 /**
63  * \note Follow #wm_operator_create convention.
64  */
wm_gizmo_create(const wmGizmoType * gzt,PointerRNA * properties)65 static wmGizmo *wm_gizmo_create(const wmGizmoType *gzt, PointerRNA *properties)
66 {
67   BLI_assert(gzt != NULL);
68   BLI_assert(gzt->struct_size >= sizeof(wmGizmo));
69 
70   wmGizmo *gz = MEM_callocN(
71       gzt->struct_size + (sizeof(wmGizmoProperty) * gzt->target_property_defs_len), __func__);
72   gz->type = gzt;
73 
74   /* initialize properties, either copy or create */
75   gz->ptr = MEM_callocN(sizeof(PointerRNA), "wmGizmoPtrRNA");
76   if (properties && properties->data) {
77     gz->properties = IDP_CopyProperty(properties->data);
78   }
79   else {
80     IDPropertyTemplate val = {0};
81     gz->properties = IDP_New(IDP_GROUP, &val, "wmGizmoProperties");
82   }
83   RNA_pointer_create(G_MAIN->wm.first, gzt->srna, gz->properties, gz->ptr);
84 
85   WM_gizmo_properties_sanitize(gz->ptr, 0);
86 
87   unit_m4(gz->matrix_space);
88   unit_m4(gz->matrix_basis);
89   unit_m4(gz->matrix_offset);
90 
91   gz->drag_part = -1;
92 
93   return gz;
94 }
95 
WM_gizmo_new_ptr(const wmGizmoType * gzt,wmGizmoGroup * gzgroup,PointerRNA * properties)96 wmGizmo *WM_gizmo_new_ptr(const wmGizmoType *gzt, wmGizmoGroup *gzgroup, PointerRNA *properties)
97 {
98   wmGizmo *gz = wm_gizmo_create(gzt, properties);
99 
100   wm_gizmo_register(gzgroup, gz);
101 
102   if (gz->type->setup != NULL) {
103     gz->type->setup(gz);
104   }
105 
106   return gz;
107 }
108 
109 /**
110  * \param idname: Must be a valid gizmo type name,
111  * if you need to check it exists use #WM_gizmo_new_ptr
112  * because callers of this function don't NULL check the return value.
113  */
WM_gizmo_new(const char * idname,wmGizmoGroup * gzgroup,PointerRNA * properties)114 wmGizmo *WM_gizmo_new(const char *idname, wmGizmoGroup *gzgroup, PointerRNA *properties)
115 {
116   const wmGizmoType *gzt = WM_gizmotype_find(idname, false);
117   return WM_gizmo_new_ptr(gzt, gzgroup, properties);
118 }
119 
120 /**
121  * Initialize default values and allocate needed memory for members.
122  */
gizmo_init(wmGizmo * gz)123 static void gizmo_init(wmGizmo *gz)
124 {
125   const float color_default[4] = {1.0f, 1.0f, 1.0f, 1.0f};
126 
127   gz->scale_basis = 1.0f;
128   gz->line_width = 1.0f;
129 
130   /* defaults */
131   copy_v4_v4(gz->color, color_default);
132   copy_v4_v4(gz->color_hi, color_default);
133 }
134 
135 /**
136  * Register \a gizmo.
137  *
138  * \note Not to be confused with type registration from RNA.
139  */
wm_gizmo_register(wmGizmoGroup * gzgroup,wmGizmo * gz)140 static void wm_gizmo_register(wmGizmoGroup *gzgroup, wmGizmo *gz)
141 {
142   gizmo_init(gz);
143   wm_gizmogroup_gizmo_register(gzgroup, gz);
144 }
145 
146 /**
147  * \warning this doesn't check #wmGizmoMap (highlight, selection etc).
148  * Typical use is when freeing the windowing data,
149  * where caller can manage clearing selection, highlight... etc.
150  */
WM_gizmo_free(wmGizmo * gz)151 void WM_gizmo_free(wmGizmo *gz)
152 {
153   if (gz->type->free != NULL) {
154     gz->type->free(gz);
155   }
156 
157 #ifdef WITH_PYTHON
158   if (gz->py_instance) {
159     /* do this first in case there are any __del__ functions or
160      * similar that use properties */
161     BPY_DECREF_RNA_INVALIDATE(gz->py_instance);
162   }
163 #endif
164 
165   if (gz->op_data) {
166     for (int i = 0; i < gz->op_data_len; i++) {
167       WM_operator_properties_free(&gz->op_data[i].ptr);
168     }
169     MEM_freeN(gz->op_data);
170   }
171 
172   if (gz->ptr != NULL) {
173     WM_gizmo_properties_free(gz->ptr);
174     MEM_freeN(gz->ptr);
175   }
176 
177   if (gz->type->target_property_defs_len != 0) {
178     wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz);
179     for (int i = 0; i < gz->type->target_property_defs_len; i++) {
180       wmGizmoProperty *gz_prop = &gz_prop_array[i];
181       if (gz_prop->custom_func.free_fn) {
182         gz_prop->custom_func.free_fn(gz, gz_prop);
183       }
184     }
185   }
186 
187   MEM_freeN(gz);
188 }
189 
190 /**
191  * Free \a gizmo and unlink from \a gizmolist.
192  * \a gizmolist is allowed to be NULL.
193  */
WM_gizmo_unlink(ListBase * gizmolist,wmGizmoMap * gzmap,wmGizmo * gz,bContext * C)194 void WM_gizmo_unlink(ListBase *gizmolist, wmGizmoMap *gzmap, wmGizmo *gz, bContext *C)
195 {
196   if (gz->state & WM_GIZMO_STATE_HIGHLIGHT) {
197     wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
198   }
199   if (gz->state & WM_GIZMO_STATE_MODAL) {
200     wm_gizmomap_modal_set(gzmap, C, gz, NULL, false);
201   }
202   /* Unlink instead of setting so we don't run callbacks. */
203   if (gz->state & WM_GIZMO_STATE_SELECT) {
204     WM_gizmo_select_unlink(gzmap, gz);
205   }
206 
207   if (gizmolist) {
208     BLI_remlink(gizmolist, gz);
209   }
210 
211   BLI_assert(gzmap->gzmap_context.highlight != gz);
212   BLI_assert(gzmap->gzmap_context.modal != gz);
213 
214   WM_gizmo_free(gz);
215 }
216 
217 /* -------------------------------------------------------------------- */
218 /** \name Gizmo Creation API
219  *
220  * API for defining data on gizmo creation.
221  *
222  * \{ */
223 
WM_gizmo_operator_get(wmGizmo * gz,int part_index)224 struct wmGizmoOpElem *WM_gizmo_operator_get(wmGizmo *gz, int part_index)
225 {
226   if (gz->op_data && ((part_index >= 0) && (part_index < gz->op_data_len))) {
227     return &gz->op_data[part_index];
228   }
229   return NULL;
230 }
231 
WM_gizmo_operator_set(wmGizmo * gz,int part_index,wmOperatorType * ot,IDProperty * properties)232 PointerRNA *WM_gizmo_operator_set(wmGizmo *gz,
233                                   int part_index,
234                                   wmOperatorType *ot,
235                                   IDProperty *properties)
236 {
237   BLI_assert(part_index < 255);
238   /* We could pre-allocate these but using multiple is such a rare thing. */
239   if (part_index >= gz->op_data_len) {
240     gz->op_data_len = part_index + 1;
241     gz->op_data = MEM_recallocN(gz->op_data, sizeof(*gz->op_data) * gz->op_data_len);
242   }
243   wmGizmoOpElem *gzop = &gz->op_data[part_index];
244   gzop->type = ot;
245 
246   if (gzop->ptr.data) {
247     WM_operator_properties_free(&gzop->ptr);
248   }
249   WM_operator_properties_create_ptr(&gzop->ptr, ot);
250 
251   if (properties) {
252     gzop->ptr.data = properties;
253   }
254 
255   return &gzop->ptr;
256 }
257 
WM_gizmo_operator_invoke(bContext * C,wmGizmo * gz,wmGizmoOpElem * gzop)258 int WM_gizmo_operator_invoke(bContext *C, wmGizmo *gz, wmGizmoOpElem *gzop)
259 {
260   if (gz->flag & WM_GIZMO_OPERATOR_TOOL_INIT) {
261     /* Merge toolsettings into the gizmo properties. */
262     PointerRNA tref_ptr;
263     bToolRef *tref = WM_toolsystem_ref_from_context(C);
264     if (tref && WM_toolsystem_ref_properties_get_from_operator(tref, gzop->type, &tref_ptr)) {
265       if (gzop->ptr.data == NULL) {
266         IDPropertyTemplate val = {0};
267         gzop->ptr.data = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
268       }
269       IDP_MergeGroup(gzop->ptr.data, tref_ptr.data, false);
270     }
271   }
272   return WM_operator_name_call_ptr(C, gzop->type, WM_OP_INVOKE_DEFAULT, &gzop->ptr);
273 }
274 
wm_gizmo_set_matrix_rotation_from_z_axis__internal(float matrix[4][4],const float z_axis[3])275 static void wm_gizmo_set_matrix_rotation_from_z_axis__internal(float matrix[4][4],
276                                                                const float z_axis[3])
277 {
278   /* old code, seems we can use simpler method */
279 #if 0
280   const float z_global[3] = {0.0f, 0.0f, 1.0f};
281   float rot[3][3];
282 
283   rotation_between_vecs_to_mat3(rot, z_global, z_axis);
284   copy_v3_v3(matrix[0], rot[0]);
285   copy_v3_v3(matrix[1], rot[1]);
286   copy_v3_v3(matrix[2], rot[2]);
287 #else
288   normalize_v3_v3(matrix[2], z_axis);
289   ortho_basis_v3v3_v3(matrix[0], matrix[1], matrix[2]);
290 #endif
291 }
292 
wm_gizmo_set_matrix_rotation_from_yz_axis__internal(float matrix[4][4],const float y_axis[3],const float z_axis[3])293 static void wm_gizmo_set_matrix_rotation_from_yz_axis__internal(float matrix[4][4],
294                                                                 const float y_axis[3],
295                                                                 const float z_axis[3])
296 {
297   normalize_v3_v3(matrix[1], y_axis);
298   normalize_v3_v3(matrix[2], z_axis);
299   cross_v3_v3v3(matrix[0], matrix[1], matrix[2]);
300   normalize_v3(matrix[0]);
301 }
302 
303 /**
304  * wmGizmo.matrix utils.
305  */
WM_gizmo_set_matrix_rotation_from_z_axis(wmGizmo * gz,const float z_axis[3])306 void WM_gizmo_set_matrix_rotation_from_z_axis(wmGizmo *gz, const float z_axis[3])
307 {
308   wm_gizmo_set_matrix_rotation_from_z_axis__internal(gz->matrix_basis, z_axis);
309 }
WM_gizmo_set_matrix_rotation_from_yz_axis(wmGizmo * gz,const float y_axis[3],const float z_axis[3])310 void WM_gizmo_set_matrix_rotation_from_yz_axis(wmGizmo *gz,
311                                                const float y_axis[3],
312                                                const float z_axis[3])
313 {
314   wm_gizmo_set_matrix_rotation_from_yz_axis__internal(gz->matrix_basis, y_axis, z_axis);
315 }
WM_gizmo_set_matrix_location(wmGizmo * gz,const float origin[3])316 void WM_gizmo_set_matrix_location(wmGizmo *gz, const float origin[3])
317 {
318   copy_v3_v3(gz->matrix_basis[3], origin);
319 }
320 
321 /**
322  * wmGizmo.matrix_offset utils.
323  */
WM_gizmo_set_matrix_offset_rotation_from_z_axis(wmGizmo * gz,const float z_axis[3])324 void WM_gizmo_set_matrix_offset_rotation_from_z_axis(wmGizmo *gz, const float z_axis[3])
325 {
326   wm_gizmo_set_matrix_rotation_from_z_axis__internal(gz->matrix_offset, z_axis);
327 }
WM_gizmo_set_matrix_offset_rotation_from_yz_axis(wmGizmo * gz,const float y_axis[3],const float z_axis[3])328 void WM_gizmo_set_matrix_offset_rotation_from_yz_axis(wmGizmo *gz,
329                                                       const float y_axis[3],
330                                                       const float z_axis[3])
331 {
332   wm_gizmo_set_matrix_rotation_from_yz_axis__internal(gz->matrix_offset, y_axis, z_axis);
333 }
WM_gizmo_set_matrix_offset_location(wmGizmo * gz,const float offset[3])334 void WM_gizmo_set_matrix_offset_location(wmGizmo *gz, const float offset[3])
335 {
336   copy_v3_v3(gz->matrix_offset[3], offset);
337 }
338 
WM_gizmo_set_flag(wmGizmo * gz,const int flag,const bool enable)339 void WM_gizmo_set_flag(wmGizmo *gz, const int flag, const bool enable)
340 {
341   if (enable) {
342     gz->flag |= flag;
343   }
344   else {
345     gz->flag &= ~flag;
346   }
347 }
348 
WM_gizmo_set_scale(wmGizmo * gz,const float scale)349 void WM_gizmo_set_scale(wmGizmo *gz, const float scale)
350 {
351   gz->scale_basis = scale;
352 }
353 
WM_gizmo_set_line_width(wmGizmo * gz,const float line_width)354 void WM_gizmo_set_line_width(wmGizmo *gz, const float line_width)
355 {
356   gz->line_width = line_width;
357 }
358 
WM_gizmo_get_color(const wmGizmo * gz,float color[4])359 void WM_gizmo_get_color(const wmGizmo *gz, float color[4])
360 {
361   copy_v4_v4(color, gz->color);
362 }
WM_gizmo_set_color(wmGizmo * gz,const float color[4])363 void WM_gizmo_set_color(wmGizmo *gz, const float color[4])
364 {
365   copy_v4_v4(gz->color, color);
366 }
367 
WM_gizmo_get_color_highlight(const wmGizmo * gz,float color_hi[4])368 void WM_gizmo_get_color_highlight(const wmGizmo *gz, float color_hi[4])
369 {
370   copy_v4_v4(color_hi, gz->color_hi);
371 }
WM_gizmo_set_color_highlight(wmGizmo * gz,const float color_hi[4])372 void WM_gizmo_set_color_highlight(wmGizmo *gz, const float color_hi[4])
373 {
374   copy_v4_v4(gz->color_hi, color_hi);
375 }
376 
377 /** \} */ /* Gizmo Creation API. */
378 
379 /* -------------------------------------------------------------------- */
380 /** \name Gizmo Callback Assignment
381  *
382  * \{ */
383 
WM_gizmo_set_fn_custom_modal(struct wmGizmo * gz,wmGizmoFnModal fn)384 void WM_gizmo_set_fn_custom_modal(struct wmGizmo *gz, wmGizmoFnModal fn)
385 {
386   gz->custom_modal = fn;
387 }
388 
389 /** \} */
390 
391 /* -------------------------------------------------------------------- */
392 /**
393  * Add/Remove \a gizmo to selection.
394  * Reallocates memory for selected gizmos so better not call for selecting multiple ones.
395  *
396  * \return if the selection has changed.
397  */
wm_gizmo_select_set_ex(wmGizmoMap * gzmap,wmGizmo * gz,bool select,bool use_array,bool use_callback)398 bool wm_gizmo_select_set_ex(
399     wmGizmoMap *gzmap, wmGizmo *gz, bool select, bool use_array, bool use_callback)
400 {
401   bool changed = false;
402 
403   if (select) {
404     if ((gz->state & WM_GIZMO_STATE_SELECT) == 0) {
405       if (use_array) {
406         wm_gizmomap_select_array_push_back(gzmap, gz);
407       }
408       gz->state |= WM_GIZMO_STATE_SELECT;
409       changed = true;
410     }
411   }
412   else {
413     if (gz->state & WM_GIZMO_STATE_SELECT) {
414       if (use_array) {
415         wm_gizmomap_select_array_remove(gzmap, gz);
416       }
417       gz->state &= ~WM_GIZMO_STATE_SELECT;
418       changed = true;
419     }
420   }
421 
422   /* In the case of unlinking we only want to remove from the array
423    * and not write to the external state */
424   if (use_callback && changed) {
425     if (gz->type->select_refresh) {
426       gz->type->select_refresh(gz);
427     }
428   }
429 
430   return changed;
431 }
432 
433 /* Remove from selection array without running callbacks. */
WM_gizmo_select_unlink(wmGizmoMap * gzmap,wmGizmo * gz)434 bool WM_gizmo_select_unlink(wmGizmoMap *gzmap, wmGizmo *gz)
435 {
436   return wm_gizmo_select_set_ex(gzmap, gz, false, true, false);
437 }
438 
WM_gizmo_select_set(wmGizmoMap * gzmap,wmGizmo * gz,bool select)439 bool WM_gizmo_select_set(wmGizmoMap *gzmap, wmGizmo *gz, bool select)
440 {
441   return wm_gizmo_select_set_ex(gzmap, gz, select, true, true);
442 }
443 
WM_gizmo_highlight_set(wmGizmoMap * gzmap,wmGizmo * gz)444 bool WM_gizmo_highlight_set(wmGizmoMap *gzmap, wmGizmo *gz)
445 {
446   return wm_gizmomap_highlight_set(gzmap, NULL, gz, gz ? gz->highlight_part : 0);
447 }
448 
wm_gizmo_select_and_highlight(bContext * C,wmGizmoMap * gzmap,wmGizmo * gz)449 bool wm_gizmo_select_and_highlight(bContext *C, wmGizmoMap *gzmap, wmGizmo *gz)
450 {
451   if (WM_gizmo_select_set(gzmap, gz, true)) {
452     wm_gizmomap_highlight_set(gzmap, C, gz, gz->highlight_part);
453     return true;
454   }
455   return false;
456 }
457 
458 /**
459  * Special function to run from setup so gizmos start out interactive.
460  *
461  * We could do this when linking them,
462  * but this complicates things since the window update code needs to run first.
463  */
WM_gizmo_modal_set_from_setup(struct wmGizmoMap * gzmap,struct bContext * C,struct wmGizmo * gz,int part_index,const wmEvent * event)464 void WM_gizmo_modal_set_from_setup(struct wmGizmoMap *gzmap,
465                                    struct bContext *C,
466                                    struct wmGizmo *gz,
467                                    int part_index,
468                                    const wmEvent *event)
469 {
470   gz->highlight_part = part_index;
471   WM_gizmo_highlight_set(gzmap, gz);
472   if (false) {
473     wm_gizmomap_modal_set(gzmap, C, gz, event, true);
474   }
475   else {
476     /* WEAK: but it works. */
477     WM_operator_name_call(C, "GIZMOGROUP_OT_gizmo_tweak", WM_OP_INVOKE_DEFAULT, NULL);
478   }
479 }
480 
wm_gizmo_calculate_scale(wmGizmo * gz,const bContext * C)481 void wm_gizmo_calculate_scale(wmGizmo *gz, const bContext *C)
482 {
483   const RegionView3D *rv3d = CTX_wm_region_view3d(C);
484   float scale = UI_DPI_FAC;
485 
486   if ((gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_SCALE) == 0) {
487     scale *= U.gizmo_size;
488     if (rv3d) {
489       /* 'ED_view3d_pixel_size' includes 'U.pixelsize', remove it. */
490       float matrix_world[4][4];
491       if (gz->type->matrix_basis_get) {
492         float matrix_basis[4][4];
493         gz->type->matrix_basis_get(gz, matrix_basis);
494         mul_m4_m4m4(matrix_world, gz->matrix_space, matrix_basis);
495       }
496       else {
497         mul_m4_m4m4(matrix_world, gz->matrix_space, gz->matrix_basis);
498       }
499 
500       /* Exclude matrix_offset from scale. */
501       scale *= ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_world[3]);
502     }
503   }
504 
505   gz->scale_final = gz->scale_basis * scale;
506 }
507 
gizmo_update_prop_data(wmGizmo * gz)508 static void gizmo_update_prop_data(wmGizmo *gz)
509 {
510   /* gizmo property might have been changed, so update gizmo */
511   if (gz->type->property_update) {
512     wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz);
513     for (int i = 0; i < gz->type->target_property_defs_len; i++) {
514       wmGizmoProperty *gz_prop = &gz_prop_array[i];
515       if (WM_gizmo_target_property_is_valid(gz_prop)) {
516         gz->type->property_update(gz, gz_prop);
517       }
518     }
519   }
520 }
521 
wm_gizmo_update(wmGizmo * gz,const bContext * C,const bool refresh_map)522 void wm_gizmo_update(wmGizmo *gz, const bContext *C, const bool refresh_map)
523 {
524   if (refresh_map) {
525     gizmo_update_prop_data(gz);
526   }
527   wm_gizmo_calculate_scale(gz, C);
528 }
529 
wm_gizmo_is_visible(wmGizmo * gz)530 int wm_gizmo_is_visible(wmGizmo *gz)
531 {
532   if (gz->flag & WM_GIZMO_HIDDEN) {
533     return 0;
534   }
535   if ((gz->state & WM_GIZMO_STATE_MODAL) &&
536       !(gz->flag & (WM_GIZMO_DRAW_MODAL | WM_GIZMO_DRAW_VALUE))) {
537     /* don't draw while modal (dragging) */
538     return 0;
539   }
540   if ((gz->flag & WM_GIZMO_DRAW_HOVER) && !(gz->state & WM_GIZMO_STATE_HIGHLIGHT) &&
541       !(gz->state & WM_GIZMO_STATE_SELECT)) /* still draw selected gizmos */
542   {
543     /* update but don't draw */
544     return WM_GIZMO_IS_VISIBLE_UPDATE;
545   }
546 
547   return WM_GIZMO_IS_VISIBLE_UPDATE | WM_GIZMO_IS_VISIBLE_DRAW;
548 }
549 
WM_gizmo_calc_matrix_final_params(const wmGizmo * gz,const struct WM_GizmoMatrixParams * params,float r_mat[4][4])550 void WM_gizmo_calc_matrix_final_params(const wmGizmo *gz,
551                                        const struct WM_GizmoMatrixParams *params,
552                                        float r_mat[4][4])
553 {
554   const float(*const matrix_space)[4] = params->matrix_space ? params->matrix_space :
555                                                                gz->matrix_space;
556   const float(*const matrix_basis)[4] = params->matrix_basis ? params->matrix_basis :
557                                                                gz->matrix_basis;
558   const float(*const matrix_offset)[4] = params->matrix_offset ? params->matrix_offset :
559                                                                  gz->matrix_offset;
560   const float *scale_final = params->scale_final ? params->scale_final : &gz->scale_final;
561 
562   float final_matrix[4][4];
563   if (params->matrix_basis == NULL && gz->type->matrix_basis_get) {
564     gz->type->matrix_basis_get(gz, final_matrix);
565   }
566   else {
567     copy_m4_m4(final_matrix, matrix_basis);
568   }
569 
570   if (gz->flag & WM_GIZMO_DRAW_NO_SCALE) {
571     mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
572   }
573   else {
574     if (gz->flag & WM_GIZMO_DRAW_OFFSET_SCALE) {
575       mul_mat3_m4_fl(final_matrix, *scale_final);
576       mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
577     }
578     else {
579       mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
580       mul_mat3_m4_fl(final_matrix, *scale_final);
581     }
582   }
583 
584   mul_m4_m4m4(r_mat, matrix_space, final_matrix);
585 }
586 
WM_gizmo_calc_matrix_final_no_offset(const wmGizmo * gz,float r_mat[4][4])587 void WM_gizmo_calc_matrix_final_no_offset(const wmGizmo *gz, float r_mat[4][4])
588 {
589   float mat_identity[4][4];
590   unit_m4(mat_identity);
591 
592   WM_gizmo_calc_matrix_final_params(gz,
593                                     &((struct WM_GizmoMatrixParams){
594                                         .matrix_space = NULL,
595                                         .matrix_basis = NULL,
596                                         .matrix_offset = mat_identity,
597                                         .scale_final = NULL,
598                                     }),
599                                     r_mat);
600 }
601 
WM_gizmo_calc_matrix_final(const wmGizmo * gz,float r_mat[4][4])602 void WM_gizmo_calc_matrix_final(const wmGizmo *gz, float r_mat[4][4])
603 {
604   WM_gizmo_calc_matrix_final_params(gz,
605                                     &((struct WM_GizmoMatrixParams){
606                                         .matrix_space = NULL,
607                                         .matrix_basis = NULL,
608                                         .matrix_offset = NULL,
609                                         .scale_final = NULL,
610                                     }),
611                                     r_mat);
612 }
613 
614 /** \name Gizmo Property Access
615  *
616  * Matches `WM_operator_properties` conventions.
617  *
618  * \{ */
619 
WM_gizmo_properties_create_ptr(PointerRNA * ptr,wmGizmoType * gzt)620 void WM_gizmo_properties_create_ptr(PointerRNA *ptr, wmGizmoType *gzt)
621 {
622   RNA_pointer_create(NULL, gzt->srna, NULL, ptr);
623 }
624 
WM_gizmo_properties_create(PointerRNA * ptr,const char * gtstring)625 void WM_gizmo_properties_create(PointerRNA *ptr, const char *gtstring)
626 {
627   const wmGizmoType *gzt = WM_gizmotype_find(gtstring, false);
628 
629   if (gzt) {
630     WM_gizmo_properties_create_ptr(ptr, (wmGizmoType *)gzt);
631   }
632   else {
633     RNA_pointer_create(NULL, &RNA_GizmoProperties, NULL, ptr);
634   }
635 }
636 
637 /* similar to the function above except its uses ID properties
638  * used for keymaps and macros */
WM_gizmo_properties_alloc(PointerRNA ** ptr,IDProperty ** properties,const char * gtstring)639 void WM_gizmo_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *gtstring)
640 {
641   if (*properties == NULL) {
642     IDPropertyTemplate val = {0};
643     *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
644   }
645 
646   if (*ptr == NULL) {
647     *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
648     WM_gizmo_properties_create(*ptr, gtstring);
649   }
650 
651   (*ptr)->data = *properties;
652 }
653 
WM_gizmo_properties_sanitize(PointerRNA * ptr,const bool no_context)654 void WM_gizmo_properties_sanitize(PointerRNA *ptr, const bool no_context)
655 {
656   RNA_STRUCT_BEGIN (ptr, prop) {
657     switch (RNA_property_type(prop)) {
658       case PROP_ENUM:
659         if (no_context) {
660           RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
661         }
662         else {
663           RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
664         }
665         break;
666       case PROP_POINTER: {
667         StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
668 
669         /* recurse into gizmo properties */
670         if (RNA_struct_is_a(ptype, &RNA_GizmoProperties)) {
671           PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
672           WM_gizmo_properties_sanitize(&opptr, no_context);
673         }
674         break;
675       }
676       default:
677         break;
678     }
679   }
680   RNA_STRUCT_END;
681 }
682 
683 /**
684  * Set all props to their default.
685  *
686  * \param do_update: Only update un-initialized props.
687  *
688  * \note There's nothing specific to gizmos here.
689  * This could be made a general function.
690  */
WM_gizmo_properties_default(PointerRNA * ptr,const bool do_update)691 bool WM_gizmo_properties_default(PointerRNA *ptr, const bool do_update)
692 {
693   bool changed = false;
694   RNA_STRUCT_BEGIN (ptr, prop) {
695     switch (RNA_property_type(prop)) {
696       case PROP_POINTER: {
697         StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
698         if (ptype != &RNA_Struct) {
699           PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
700           changed |= WM_gizmo_properties_default(&opptr, do_update);
701         }
702         break;
703       }
704       default:
705         if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) {
706           if (RNA_property_reset(ptr, prop, -1)) {
707             changed = true;
708           }
709         }
710         break;
711     }
712   }
713   RNA_STRUCT_END;
714 
715   return changed;
716 }
717 
718 /* remove all props without PROP_SKIP_SAVE */
WM_gizmo_properties_reset(wmGizmo * gz)719 void WM_gizmo_properties_reset(wmGizmo *gz)
720 {
721   if (gz->ptr->data) {
722     PropertyRNA *iterprop;
723     iterprop = RNA_struct_iterator_property(gz->type->srna);
724 
725     RNA_PROP_BEGIN (gz->ptr, itemptr, iterprop) {
726       PropertyRNA *prop = itemptr.data;
727 
728       if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
729         const char *identifier = RNA_property_identifier(prop);
730         RNA_struct_idprops_unset(gz->ptr, identifier);
731       }
732     }
733     RNA_PROP_END;
734   }
735 }
736 
WM_gizmo_properties_clear(PointerRNA * ptr)737 void WM_gizmo_properties_clear(PointerRNA *ptr)
738 {
739   IDProperty *properties = ptr->data;
740 
741   if (properties) {
742     IDP_ClearProperty(properties);
743   }
744 }
745 
WM_gizmo_properties_free(PointerRNA * ptr)746 void WM_gizmo_properties_free(PointerRNA *ptr)
747 {
748   IDProperty *properties = ptr->data;
749 
750   if (properties) {
751     IDP_FreeProperty(properties);
752     ptr->data = NULL; /* just in case */
753   }
754 }
755 
756 /** \} */
757 
758 /** \name General Utilities
759  *
760  * \{ */
761 
WM_gizmo_context_check_drawstep(const struct bContext * C,eWM_GizmoFlagMapDrawStep step)762 bool WM_gizmo_context_check_drawstep(const struct bContext *C, eWM_GizmoFlagMapDrawStep step)
763 {
764   switch (step) {
765     case WM_GIZMOMAP_DRAWSTEP_2D: {
766       break;
767     }
768     case WM_GIZMOMAP_DRAWSTEP_3D: {
769       wmWindowManager *wm = CTX_wm_manager(C);
770       if (ED_screen_animation_playing(wm)) {
771         return false;
772       }
773       break;
774     }
775   }
776   return true;
777 }
778 
779 /** \} */
780