Blender V4.3
editmesh_extrude_spin_gizmo.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_listbase.h"
10#include "BLI_math_geom.h"
11#include "BLI_math_matrix.h"
12#include "BLI_math_rotation.h"
13#include "BLI_math_vector.h"
14
15#include "BKE_context.hh"
16#include "BKE_scene.hh"
17
18#include "RNA_access.hh"
19#include "RNA_define.hh"
20#include "RNA_enum_types.hh"
21
22#include "WM_api.hh"
23#include "WM_message.hh"
24#include "WM_toolsystem.hh"
25#include "WM_types.hh"
26
27#include "ED_gizmo_utils.hh"
28#include "ED_screen.hh"
29#include "ED_view3d.hh"
30
31#include "UI_resources.hh"
32
33#include "MEM_guardedalloc.h"
34
35#include "mesh_intern.hh" /* own include */
36
37#include "ED_transform.hh"
38
39#include "ED_gizmo_library.hh"
40#include "ED_undo.hh"
41
45/* Disable for now, issues w/ refresh and '+' icons overlap. */
46// #define USE_SELECT_CENTER
47
48#ifdef USE_SELECT_CENTER
49# include "BKE_editmesh.hh"
50#endif
51
52static const float dial_angle_partial = M_PI_2;
53static const float dial_angle_partial_margin = 0.92f;
54
55#define ORTHO_AXIS_OFFSET 2
56
57/* -------------------------------------------------------------------- */
60
62 struct {
66
67 /* Only for view orientation. */
68 struct {
69 float viewinv_m3[3][3];
71
72 /* We could store more vars here! */
73 struct {
76 float orient_mat[3][3];
77#ifdef USE_SELECT_CENTER
78 float select_center[3];
79 float select_center_ortho_axis[3][3];
80 bool use_select_center;
81#endif
83
84 /* Store data for invoke. */
85 struct {
88};
89
90/* Use dials only as a visualization when hovering over the icons. */
91#define USE_DIAL_HOVER
92
93#define INIT_SCALE_BASE 2.3f
94#define INIT_SCALE_BUTTON 0.15f
95
96static const uchar shape_plus[] = {
97 0x73, 0x73, 0x73, 0x36, 0x8c, 0x36, 0x8c, 0x73, 0xc9, 0x73, 0xc9, 0x8c, 0x8c,
98 0x8c, 0x8c, 0xc9, 0x73, 0xc9, 0x73, 0x8c, 0x36, 0x8c, 0x36, 0x73, 0x36, 0x73,
99};
100
101static void gizmo_mesh_spin_init_setup(const bContext * /*C*/, wmGizmoGroup *gzgroup)
102{
103 /* alpha values for normal/highlighted states */
104 const float alpha = 0.6f;
105 const float alpha_hi = 1.0f;
106 const float scale_base = INIT_SCALE_BASE;
107 const float scale_button = INIT_SCALE_BUTTON;
108
110 MEM_callocN(sizeof(*ggd), __func__));
111 gzgroup->customdata = ggd;
112 const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
113 const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true);
114
115 for (int i = 0; i < 3; i++) {
116 for (int j = 0; j < 2; j++) {
117 wmGizmo *gz = WM_gizmo_new_ptr(gzt_button, gzgroup, nullptr);
118 PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "shape");
120 gz->ptr, prop, (const char *)shape_plus, ARRAY_SIZE(shape_plus));
121
122 RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP);
123
124 float color[4];
126 color[3] = alpha;
128
129 WM_gizmo_set_scale(gz, scale_button);
130 gz->color[3] = 0.6f;
131
133
134 ggd->gizmos.icon_button[i][j] = gz;
135 }
136 }
137
138 for (int i = 0; i < ARRAY_SIZE(ggd->gizmos.xyz_view); i++) {
139 wmGizmo *gz = WM_gizmo_new_ptr(gzt_dial, gzgroup, nullptr);
142 ggd->gizmos.xyz_view[i] = gz;
143 }
144
145 for (int i = 0; i < 3; i++) {
146 wmGizmo *gz = ggd->gizmos.xyz_view[i];
147#ifndef USE_DIAL_HOVER
148 RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
149#endif
150 WM_gizmo_set_line_width(gz, 2.0f);
151 float color[4];
153 color[3] = alpha;
155 color[3] = alpha_hi;
158 RNA_float_set(gz->ptr,
159 "arc_partial_angle",
161 }
162
163 {
164 wmGizmo *gz = ggd->gizmos.xyz_view[3];
165 WM_gizmo_set_line_width(gz, 2.0f);
166 float color[4];
167 copy_v3_fl(color, 1.0f);
168 color[3] = alpha;
170 color[3] = alpha_hi;
172 WM_gizmo_set_scale(gz, scale_base);
173 }
174
175#ifdef USE_DIAL_HOVER
176 for (int i = 0; i < 4; i++) {
177 wmGizmo *gz = ggd->gizmos.xyz_view[i];
179 }
180#endif
181
182 ggd->data.ot_spin = WM_operatortype_find("MESH_OT_spin", true);
184}
185
186static void gizmo_mesh_spin_init_refresh(const bContext *C, wmGizmoGroup *gzgroup);
187
189 int axis_index,
190 const float axis_vec[3],
191 const float axis_tan[3])
192{
193 GizmoGroupData_SpinInit *ggd = static_cast<GizmoGroupData_SpinInit *>(gzgroup->customdata);
194 wmGizmo *gz = ggd->gizmos.xyz_view[axis_index];
195 if (axis_tan != nullptr) {
196 WM_gizmo_set_matrix_rotation_from_yz_axis(gz, axis_tan, axis_vec);
197 }
198 else {
200 }
201
202/* Only for display, use icons to access. */
203#ifndef USE_DIAL_HOVER
204 {
205 PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ggd->data.ot_spin, nullptr);
206 RNA_float_set_array(ptr, "axis", axis_vec);
207 }
208#endif
209 if (axis_index < 3) {
210 for (int j = 0; j < 2; j++) {
211 gz = ggd->gizmos.icon_button[axis_index][j];
212 PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ggd->data.ot_spin, nullptr);
213 float axis_vec_flip[3];
214 if (0 == j) {
215 negate_v3_v3(axis_vec_flip, axis_vec);
216 }
217 else {
218 copy_v3_v3(axis_vec_flip, axis_vec);
219 }
220 RNA_float_set_array(ptr, "axis", axis_vec_flip);
221 }
222 }
223}
224
226{
227 GizmoGroupData_SpinInit *ggd = static_cast<GizmoGroupData_SpinInit *>(gzgroup->customdata);
229 float viewinv_m3[3][3];
230 copy_m3_m4(viewinv_m3, rv3d->viewinv);
231
232 {
233 Scene *scene = CTX_data_scene(C);
235 scene, SCE_ORIENT_ROTATE);
236 switch (orient_slot->type) {
237 case V3D_ORIENT_VIEW: {
238 if (!equals_m3m3(viewinv_m3, ggd->prev.viewinv_m3)) {
239 /* Take care calling refresh from draw_prepare,
240 * this should be OK because it's only adjusting the cage orientation. */
242 }
243 break;
244 }
245 }
246 }
247
248 /* Refresh handled above when using view orientation. */
249 if (!equals_m3m3(viewinv_m3, ggd->prev.viewinv_m3)) {
250 gizmo_mesh_spin_init_refresh_axis_orientation(gzgroup, 3, rv3d->viewinv[2], nullptr);
251 copy_m3_m4(ggd->prev.viewinv_m3, rv3d->viewinv);
252 }
253
254/* Hack! highlight XYZ dials based on buttons */
255#ifdef USE_DIAL_HOVER
256 {
260 const int axis_flag = RNA_property_enum_get(&ptr, ggd->data.gzgt_axis_prop);
261 for (int i = 0; i < 4; i++) {
262 bool hide = (axis_flag & (1 << i)) == 0;
263 wmGizmo *gz = ggd->gizmos.xyz_view[i];
265 if (!hide) {
266 RNA_float_set(gz->ptr,
267 "arc_partial_angle",
269 }
270 }
271
272 for (int i = 0; i < 3; i++) {
273 for (int j = 0; j < 2; j++) {
274 wmGizmo *gz = ggd->gizmos.icon_button[i][j];
277 RNA_float_set(ggd->gizmos.xyz_view[i]->ptr, "arc_partial_angle", 0.0f);
278 i = 3;
279 break;
280 }
281 }
282 }
283 }
284#endif
285}
286
288 wmGizmoGroup *gzgroup,
289 wmGizmo *gz,
290 const wmEvent * /*event*/)
291{
292 /* Set the initial ortho axis. */
293 GizmoGroupData_SpinInit *ggd = static_cast<GizmoGroupData_SpinInit *>(gzgroup->customdata);
294 ggd->invoke.ortho_axis_active = -1;
295 for (int i = 0; i < 3; i++) {
296 if (ELEM(gz, UNPACK2(ggd->gizmos.icon_button[i]))) {
297 ggd->invoke.ortho_axis_active = i;
298 break;
299 }
300 }
301}
302
304{
305 GizmoGroupData_SpinInit *ggd = static_cast<GizmoGroupData_SpinInit *>(gzgroup->customdata);
307 const float *gizmo_center = nullptr;
308 {
309 Scene *scene = CTX_data_scene(C);
310 const View3DCursor *cursor = &scene->cursor;
311 gizmo_center = cursor->location;
312 }
313
314 for (int i = 0; i < ARRAY_SIZE(ggd->gizmos.xyz_view); i++) {
315 wmGizmo *gz = ggd->gizmos.xyz_view[i];
316 WM_gizmo_set_matrix_location(gz, gizmo_center);
317 }
318
319 for (int i = 0; i < ARRAY_SIZE(ggd->gizmos.icon_button); i++) {
320 for (int j = 0; j < 2; j++) {
321 wmGizmo *gz = ggd->gizmos.icon_button[i][j];
322 WM_gizmo_set_matrix_location(gz, gizmo_center);
323 }
324 }
325
327 for (int i = 0; i < 3; i++) {
328 const int axis_ortho = (i + ORTHO_AXIS_OFFSET) % 3;
329 const float *axis_ortho_vec = ggd->data.orient_mat[axis_ortho];
330#ifdef USE_SELECT_CENTER
331 if (ggd->data.use_select_center) {
332 float delta[3];
333 sub_v3_v3v3(delta, ggd->data.select_center, gizmo_center);
335 ggd->data.select_center_ortho_axis[i], delta, ggd->data.orient_mat[i]);
336 if (normalize_v3(ggd->data.select_center_ortho_axis[i]) != 0.0f) {
337 axis_ortho_vec = ggd->data.select_center_ortho_axis[i];
338 }
339 }
340#endif
342 gzgroup, i, ggd->data.orient_mat[i], axis_ortho_vec);
343 }
344
345 {
346 gizmo_mesh_spin_init_refresh_axis_orientation(gzgroup, 3, rv3d->viewinv[2], nullptr);
347 }
348
349#ifdef USE_SELECT_CENTER
350 {
351 Object *obedit = CTX_data_edit_object(C);
353 float select_center[3] = {0};
354 int totsel = 0;
355
356 BMesh *bm = em->bm;
357 BMVert *eve;
358 BMIter iter;
359
360 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
363 totsel++;
364 add_v3_v3(select_center, eve->co);
365 }
366 }
367 }
368 if (totsel) {
369 mul_v3_fl(select_center, 1.0f / totsel);
370 mul_m4_v3(obedit->object_to_world().ptr(), select_center);
371 copy_v3_v3(ggd->data.select_center, select_center);
372 ggd->data.use_select_center = true;
373 }
374 else {
375 ggd->data.use_select_center = false;
376 }
377 }
378#endif
379
380 for (int i = 0; i < ARRAY_SIZE(ggd->gizmos.icon_button); i++) {
381 const int axis_ortho = (i + ORTHO_AXIS_OFFSET) % 3;
382 const float *axis_ortho_vec = ggd->data.orient_mat[axis_ortho];
383 float offset = INIT_SCALE_BASE / INIT_SCALE_BUTTON;
384 float offset_vec[3];
385
386#ifdef USE_SELECT_CENTER
387 if (ggd->data.use_select_center && !is_zero_v3(ggd->data.select_center_ortho_axis[i])) {
388 axis_ortho_vec = ggd->data.select_center_ortho_axis[i];
389 }
390#endif
391
392 mul_v3_v3fl(offset_vec, axis_ortho_vec, offset);
393 for (int j = 0; j < 2; j++) {
394 wmGizmo *gz = ggd->gizmos.icon_button[i][j];
395 float mat3[3][3];
396 axis_angle_to_mat3(mat3, ggd->data.orient_mat[i], dial_angle_partial * (j ? -0.5f : 0.5f));
397 mul_v3_m3v3(gz->matrix_offset[3], mat3, offset_vec);
398 }
399 }
400
401 {
405 const int axis_flag = RNA_property_enum_get(&ptr, ggd->data.gzgt_axis_prop);
406 for (int i = 0; i < ARRAY_SIZE(ggd->gizmos.icon_button); i++) {
407 for (int j = 0; j < 2; j++) {
408 wmGizmo *gz = ggd->gizmos.icon_button[i][j];
409 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, (axis_flag & (1 << i)) == 0);
410 }
411 }
412 }
413
414 /* Needed to test view orientation changes. */
415 copy_m3_m4(ggd->prev.viewinv_m3, rv3d->viewinv);
416}
417
419 wmGizmoGroup *gzgroup,
420 wmMsgBus *mbus)
421{
422 GizmoGroupData_SpinInit *ggd = static_cast<GizmoGroupData_SpinInit *>(gzgroup->customdata);
423 Scene *scene = CTX_data_scene(C);
424 ARegion *region = CTX_wm_region(C);
425
426 /* Subscribe to view properties */
427 wmMsgSubscribeValue msg_sub_value_gz_tag_refresh{};
428 msg_sub_value_gz_tag_refresh.owner = region;
429 msg_sub_value_gz_tag_refresh.user_data = gzgroup->parent_gzmap;
430 msg_sub_value_gz_tag_refresh.notify = WM_gizmo_do_msg_notify_tag_refresh;
431
432 PointerRNA cursor_ptr = RNA_pointer_create(&scene->id, &RNA_View3DCursor, &scene->cursor);
433 /* All cursor properties. */
434 WM_msg_subscribe_rna(mbus, &cursor_ptr, nullptr, &msg_sub_value_gz_tag_refresh, __func__);
435
437 params.ptr.type = gzgroup->type->srna;
438 params.prop = ggd->data.gzgt_axis_prop,
439 WM_msg_subscribe_rna_params(mbus, &params, &msg_sub_value_gz_tag_refresh, __func__);
440}
441
443{
444 gzgt->name = "Mesh Spin Init";
445 gzgt->idname = "MESH_GGT_spin";
446
448
451
454 /* This works well with right click selection but overrides left-mouse selection
455 * when clicking which is needed to create a full 360 degree revolution, see: #89912. */
456 // gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
461
462 RNA_def_enum_flag(gzgt->srna, "axis", rna_enum_axis_flag_xyz_items, (1 << 2), "Axis", "");
463}
464
465#undef INIT_SCALE_BASE
466#undef INIT_SCALE_BUTTON
467
469
470/* -------------------------------------------------------------------- */
473
479#define USE_ANGLE_Z_ORIENT
480
482 /* Translate XYZ. */
484 /* Spin angle */
486
487 /* Translate XY constrained ('orient_mat'). */
489 /* Rotate XY constrained ('orient_mat'). */
491
492 /* Rotate on view axis. */
494
495 struct {
496 float plane_co[3];
497 float plane_no[3];
499
501
502 /* We could store more vars here! */
503 struct {
510
511#ifdef USE_ANGLE_Z_ORIENT
512 /* Apply 'orient_mat' for the final value. */
514#endif
515 /* The orientation, since the operator doesn't store this, we store our own.
516 * this is kept in sync with the operator,
517 * rotating the orientation when it doesn't match.
518 *
519 * Initialize to a sensible value where possible.
520 */
521 float orient_mat[3][3];
522
524};
525
532{
533 if (ggd->is_init) {
534 wmGizmo *gz = ggd->angle_z;
535 PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "click_value");
536 RNA_property_unset(gz->ptr, prop);
537 ggd->is_init = false;
538 }
539
540 wmOperator *op = ggd->data.op;
541 if (op == WM_operator_last_redo((bContext *)ggd->data.context)) {
543 }
544}
545
547 const float plane_no[3])
548{
549 float mat[3][3];
550 rotation_between_vecs_to_mat3(mat, ggd->data.orient_mat[2], plane_no);
551 mul_m3_m3m3(ggd->data.orient_mat, mat, ggd->data.orient_mat);
552 /* Not needed, just set for numeric stability. */
553 copy_v3_v3(ggd->data.orient_mat[2], plane_no);
554}
555
557{
558 wmOperator *op = ggd->data.op;
559 float plane_co[3], plane_no[3];
562 if (UNLIKELY(normalize_v3(plane_no) == 0.0f)) {
563 return;
564 }
565 const bool is_plane_co_eq = equals_v3v3(plane_co, ggd->prev.plane_co);
566 const bool is_plane_no_eq = equals_v3v3(plane_no, ggd->prev.plane_no);
567 if (is_plane_co_eq && is_plane_no_eq) {
568 return;
569 }
570 copy_v3_v3(ggd->prev.plane_co, plane_co);
571 copy_v3_v3(ggd->prev.plane_no, plane_no);
572
573 if (is_plane_no_eq == false) {
575 }
576
577 for (int i = 0; i < 2; i++) {
578 WM_gizmo_set_matrix_location(ggd->rotate_xy[i], plane_co);
580 }
583 /* translate_c location comes from the property. */
584
585 for (int i = 0; i < 2; i++) {
588 }
589#ifdef USE_ANGLE_Z_ORIENT
590 {
591 float plane_tan[3];
592 float orient_axis[3];
593 mul_v3_m3v3(orient_axis, ggd->data.orient_mat, ggd->data.orient_axis_relative);
594 project_plane_normalized_v3_v3v3(plane_tan, orient_axis, plane_no);
595 if (normalize_v3(plane_tan) != 0.0f) {
596 WM_gizmo_set_matrix_rotation_from_yz_axis(ggd->angle_z, plane_tan, plane_no);
597 }
598 else {
600 }
601 }
602#else
604#endif
605}
606
607/* depth callbacks */
608static void gizmo_spin_prop_depth_get(const wmGizmo *gz, wmGizmoProperty *gz_prop, void *value_p)
609{
612 wmOperator *op = ggd->data.op;
613 float *value = static_cast<float *>(value_p);
614
615 BLI_assert(gz_prop->type->array_length == 1);
616 UNUSED_VARS_NDEBUG(gz_prop);
617
618 const float *plane_no = gz->matrix_basis[2];
619 float plane_co[3];
621
622 value[0] = dot_v3v3(plane_no, plane_co) - dot_v3v3(plane_no, gz->matrix_basis[3]);
623}
624
625static void gizmo_spin_prop_depth_set(const wmGizmo *gz,
626 wmGizmoProperty *gz_prop,
627 const void *value_p)
628{
631 wmOperator *op = ggd->data.op;
632 const float *value = static_cast<const float *>(value_p);
633
634 BLI_assert(gz_prop->type->array_length == 1);
635 UNUSED_VARS_NDEBUG(gz_prop);
636
637 float plane_co[3], plane[4];
639 normalize_v3_v3(plane, gz->matrix_basis[2]);
640
641 plane[3] = -value[0] - dot_v3v3(plane, gz->matrix_basis[3]);
642
643 /* Keep our location, may be offset simply to be inside the viewport. */
644 closest_to_plane_normalized_v3(plane_co, plane, plane_co);
645
647
648 gizmo_spin_exec(ggd);
649}
650
651/* translate callbacks */
653 wmGizmoProperty *gz_prop,
654 void *value_p)
655{
658 wmOperator *op = ggd->data.op;
659 float *value = static_cast<float *>(value_p);
660
661 BLI_assert(gz_prop->type->array_length == 3);
662 UNUSED_VARS_NDEBUG(gz_prop);
663
665}
666
668 wmGizmoProperty *gz_prop,
669 const void *value)
670{
673 wmOperator *op = ggd->data.op;
674
675 BLI_assert(gz_prop->type->array_length == 3);
676 UNUSED_VARS_NDEBUG(gz_prop);
677
678 RNA_property_float_set_array(op->ptr, ggd->data.prop_axis_co, static_cast<const float *>(value));
679
680 gizmo_spin_exec(ggd);
681}
682
683/* angle callbacks */
685 wmGizmoProperty *gz_prop,
686 void *value_p)
687{
690 wmOperator *op = ggd->data.op;
691 float *value = static_cast<float *>(value_p);
692
693 BLI_assert(gz_prop->type->array_length == 1);
694 UNUSED_VARS_NDEBUG(gz_prop);
695
696 float plane_no[4];
698 normalize_v3(plane_no);
699
700 const float *rotate_axis = gz->matrix_basis[2];
701 float rotate_up[3];
702 ortho_v3_v3(rotate_up, rotate_axis);
703
704 float plane_no_proj[3];
705 project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, rotate_axis);
706
707 if (!is_zero_v3(plane_no_proj)) {
708 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, rotate_up, rotate_axis);
709 value[0] = angle;
710 }
711 else {
712 value[0] = 0.0f;
713 }
714}
715
717 wmGizmoProperty *gz_prop,
718 const void *value_p)
719{
722 wmOperator *op = ggd->data.op;
723 const float *value = static_cast<const float *>(value_p);
724
725 BLI_assert(gz_prop->type->array_length == 1);
726 UNUSED_VARS_NDEBUG(gz_prop);
727
728 float plane_no[4];
730 normalize_v3(plane_no);
731
732 const float *rotate_axis = gz->matrix_basis[2];
733 float rotate_up[3];
734 ortho_v3_v3(rotate_up, rotate_axis);
735
736 float plane_no_proj[3];
737 project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, rotate_axis);
738
739 if (!is_zero_v3(plane_no_proj)) {
740 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, rotate_up, rotate_axis);
741 const float angle_delta = angle - angle_compat_rad(value[0], angle);
742 if (angle_delta != 0.0f) {
743 float mat[3][3];
744 axis_angle_normalized_to_mat3(mat, rotate_axis, angle_delta);
745 mul_m3_v3(mat, plane_no);
746
747 /* re-normalize - seems acceptable */
749
750 gizmo_spin_exec(ggd);
751 }
752 }
753}
754
755/* angle callbacks */
756static void gizmo_spin_prop_angle_get(const wmGizmo *gz, wmGizmoProperty *gz_prop, void *value_p)
757{
760 wmOperator *op = ggd->data.op;
761 float *value = static_cast<float *>(value_p);
762
763 BLI_assert(gz_prop->type->array_length == 1);
764 UNUSED_VARS_NDEBUG(gz_prop);
765 value[0] = RNA_property_float_get(op->ptr, ggd->data.prop_angle);
766}
767
768static void gizmo_spin_prop_angle_set(const wmGizmo *gz,
769 wmGizmoProperty *gz_prop,
770 const void *value_p)
771{
774 wmOperator *op = ggd->data.op;
775 BLI_assert(gz_prop->type->array_length == 1);
776 UNUSED_VARS_NDEBUG(gz_prop);
777 const float *value = static_cast<const float *>(value_p);
778 RNA_property_float_set(op->ptr, ggd->data.prop_angle, value[0]);
779
780 gizmo_spin_exec(ggd);
781}
782
784{
785 if (ED_gizmo_poll_or_unlink_delayed_from_operator(C, gzgt, "MESH_OT_spin")) {
786 if (ED_gizmo_poll_or_unlink_delayed_from_tool_ex(C, gzgt, "MESH_GGT_spin")) {
787 return true;
788 }
789 }
790 return false;
791}
792
794{
795 /* Start off dragging. */
796 GizmoGroupData_SpinRedo *ggd = static_cast<GizmoGroupData_SpinRedo *>(gzgroup->customdata);
797 wmWindow *win = CTX_wm_window(C);
798 wmGizmo *gz = ggd->angle_z;
799 wmGizmoMap *gzmap = gzgroup->parent_gzmap;
800
801 ggd->is_init = true;
802
803 WM_gizmo_modal_set_from_setup(gzmap, (bContext *)C, gz, 0, win->eventstate);
804}
805
807{
808 wmOperatorType *ot = WM_operatortype_find("MESH_OT_spin", true);
810
811 if ((op == nullptr) || (op->type != ot)) {
812 return;
813 }
814
816 MEM_callocN(sizeof(*ggd), __func__));
817 gzgroup->customdata = ggd;
818
819 const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
820 const wmGizmoType *gzt_move = WM_gizmotype_find("GIZMO_GT_move_3d", true);
821 const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
822
823 /* Rotate View Axis (rotate_view) */
824 {
825 wmGizmo *gz = WM_gizmo_new_ptr(gzt_dial, gzgroup, nullptr);
827 zero_v4(gz->color);
828 copy_v3_fl(gz->color_hi, 1.0f);
829 gz->color_hi[3] = 0.1f;
831 RNA_enum_set(gz->ptr,
832 "draw_options",
835 ggd->rotate_view = gz;
836 }
837
838 /* Translate Center (translate_c) */
839 {
840 wmGizmo *gz = WM_gizmo_new_ptr(gzt_move, gzgroup, nullptr);
842 gz->color[3] = 0.6f;
843 RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_MOVE_STYLE_RING_2D);
845 WM_gizmo_set_scale(gz, 0.15);
846 WM_gizmo_set_line_width(gz, 2.0f);
847 ggd->translate_c = gz;
848 }
849
850 /* Spin Angle (angle_z) */
851 {
852 wmGizmo *gz = WM_gizmo_new_ptr(gzt_dial, gzgroup, nullptr);
853 copy_v3_v3(gz->color, gz->color_hi);
854 gz->color[3] = 0.5f;
855 RNA_boolean_set(gz->ptr, "wrap_angle", false);
857 RNA_float_set(gz->ptr, "arc_inner_factor", 0.9f);
858 RNA_float_set(gz->ptr, "click_value", M_PI * 2);
860 WM_gizmo_set_scale(gz, 2.0f);
861 WM_gizmo_set_line_width(gz, 1.0f);
862 ggd->angle_z = gz;
863 }
864
865 /* Translate X/Y Tangents (translate_xy) */
866 for (int i = 0; i < 2; i++) {
867 wmGizmo *gz = WM_gizmo_new_ptr(gzt_arrow, gzgroup, nullptr);
869 RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_NORMAL);
870 RNA_enum_set(gz->ptr, "draw_options", 0);
871 WM_gizmo_set_scale(gz, 1.2f);
872 ggd->translate_xy[i] = gz;
873 }
874
875 /* Rotate X/Y Tangents (rotate_xy) */
876 for (int i = 0; i < 2; i++) {
877 wmGizmo *gz = WM_gizmo_new_ptr(gzt_dial, gzgroup, nullptr);
879 gz->color[3] = 0.6f;
881 WM_gizmo_set_line_width(gz, 3.0f);
882 /* show the axis instead of mouse cursor */
883 RNA_enum_set(gz->ptr,
884 "draw_options",
887 ggd->rotate_xy[i] = gz;
888 }
889
890 {
891 ggd->data.context = (bContext *)C;
892 ggd->data.ot = ot;
893 ggd->data.op = op;
894 ggd->data.prop_axis_co = RNA_struct_type_find_property(ot->srna, "center");
896 ggd->data.prop_angle = RNA_struct_type_find_property(ot->srna, "angle");
897 }
898
899 /* The spin operator only knows about an axis,
900 * while the manipulator has X/Y orientation for the gizmos.
901 * Initialize the orientation from the spin gizmo if possible.
902 */
903 {
904 ARegion *region = CTX_wm_region(C);
905 wmGizmoMap *gzmap = region->gizmo_map;
906 wmGizmoGroup *gzgroup_init = WM_gizmomap_group_find(gzmap, "MESH_GGT_spin");
907 if (gzgroup_init) {
908 GizmoGroupData_SpinInit *ggd_init = static_cast<GizmoGroupData_SpinInit *>(
909 gzgroup_init->customdata);
910 copy_m3_m3(ggd->data.orient_mat, ggd_init->data.orient_mat);
911 if (ggd_init->invoke.ortho_axis_active != -1) {
913 ggd_init->gizmos.xyz_view[ggd_init->invoke.ortho_axis_active]->matrix_basis[1]);
914 ggd_init->invoke.ortho_axis_active = -1;
915 }
916 }
917 else {
918 unit_m3(ggd->data.orient_mat);
919 }
920 }
921
922#ifdef USE_ANGLE_Z_ORIENT
923 {
924 wmWindow *win = CTX_wm_window(C);
925 View3D *v3d = CTX_wm_view3d(C);
926 ARegion *region = CTX_wm_region(C);
927 const wmEvent *event = win->eventstate;
928 float plane_co[3], plane_no[3];
931
933
934 /* Use cursor as fallback if it's not set by the 'ortho_axis_active'. */
936 float cursor_co[3];
937 const int mval[2] = {event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin};
938 float plane[4];
939 plane_from_point_normal_v3(plane, plane_co, plane_no);
940 if (UNLIKELY(!ED_view3d_win_to_3d_on_plane_int(region, plane, mval, false, cursor_co))) {
941 ED_view3d_win_to_3d_int(v3d, region, plane, mval, cursor_co);
942 }
943 sub_v3_v3v3(ggd->data.orient_axis_relative, cursor_co, plane_co);
944 }
945
948 float imat3[3][3];
949 invert_m3_m3(imat3, ggd->data.orient_mat);
951 }
952 }
953#endif
954
956
957 /* Setup property callbacks */
958 {
959 {
963 params.range_get_fn = nullptr;
964 params.user_data = nullptr;
966 }
967
968 {
972 params.range_get_fn = nullptr;
973 params.user_data = nullptr;
975 }
976
977 for (int i = 0; i < 2; i++) {
978 {
981 params.value_set_fn = gizmo_spin_prop_axis_angle_set, params.range_get_fn = nullptr,
982 params.user_data = nullptr,
984 }
985 {
987 params.value_get_fn = gizmo_spin_prop_depth_get;
988 params.value_set_fn = gizmo_spin_prop_depth_set;
989 params.range_get_fn = nullptr;
990 params.user_data = nullptr;
992 }
993 }
994
996 params.value_get_fn = gizmo_spin_prop_angle_get;
997 params.value_set_fn = gizmo_spin_prop_angle_set;
998 params.range_get_fn = nullptr;
999 params.user_data = nullptr;
1001 }
1002
1003 wmWindow *win = CTX_wm_window(C);
1004 if (win && win->active) {
1005 bScreen *screen = WM_window_get_active_screen(win);
1006 if (screen->active_region) {
1007 ARegion *region = CTX_wm_region(C);
1008 if (screen->active_region == region) {
1009 /* Become modal as soon as it's started. */
1011 }
1012 }
1013 }
1014}
1015
1016static void gizmo_mesh_spin_redo_draw_prepare(const bContext * /*C*/, wmGizmoGroup *gzgroup)
1017{
1018 GizmoGroupData_SpinRedo *ggd = static_cast<GizmoGroupData_SpinRedo *>(gzgroup->customdata);
1019 if (ggd->data.op->next) {
1021 }
1022
1023 /* Not essential, just avoids feedback loop where matrices
1024 * could shift because of float precision.
1025 * Updates in this case are also redundant. */
1026 bool is_modal = false;
1027 LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) {
1028 if (gz->state & WM_GIZMO_STATE_MODAL) {
1029 is_modal = true;
1030 break;
1031 }
1032 }
1033 if (!is_modal) {
1035 }
1036
1039 {
1040 float view_up[3];
1041 project_plane_normalized_v3_v3v3(view_up, ggd->data.orient_mat[2], rv3d->viewinv[2]);
1042 if (normalize_v3(view_up) != 0.0f) {
1044 }
1045 else {
1047 }
1048 }
1049}
1050
1052{
1053 gzgt->name = "Mesh Spin Redo";
1054 gzgt->idname = "MESH_GGT_spin_redo";
1055
1057
1060
1064}
1065
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
TransformOrientationSlot * BKE_scene_orientation_slot_get(Scene *scene, int slot_index)
Definition scene.cc:2347
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
#define M_PI_2
#define M_PI
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:215
void closest_to_plane_normalized_v3(float r_close[3], const float plane[4], const float pt[3])
Definition math_geom.cc:440
void mul_m3_v3(const float M[3][3], float r[3])
void copy_m3_m3(float m1[3][3], const float m2[3][3])
void unit_m3(float m[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
bool equals_m3m3(const float mat1[3][3], const float mat2[3][3])
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
float angle_compat_rad(float angle, float angle_compat)
void axis_angle_normalized_to_mat3(float R[3][3], const float axis[3], float angle)
void rotation_between_vecs_to_mat3(float m[3][3], const float v1[3], const float v2[3])
void axis_angle_to_mat3(float R[3][3], const float axis[3], float angle)
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const float v_plane[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
float angle_signed_on_axis_v3v3_v3(const float v1[3], const float v2[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
void ortho_v3_v3(float out[3], const float v[3])
MINLINE void zero_v4(float r[4])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
unsigned char uchar
#define UNPACK2(a)
#define ARRAY_SIZE(arr)
#define UNUSED_VARS_NDEBUG(...)
#define UNLIKELY(x)
#define ELEM(...)
@ SCE_ORIENT_ROTATE
@ RGN_TYPE_WINDOW
@ SPACE_VIEW3D
@ V3D_ORIENT_VIEW
@ ED_GIZMO_MOVE_STYLE_RING_2D
@ ED_GIZMO_BUTTON_SHOW_BACKDROP
@ ED_GIZMO_ARROW_STYLE_NORMAL
@ ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_MIRROR
@ ED_GIZMO_DIAL_DRAW_FLAG_FILL
@ ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_START_Y
@ ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE
@ ED_GIZMO_DIAL_DRAW_FLAG_CLIP
bool ED_gizmo_poll_or_unlink_delayed_from_tool(const bContext *C, wmGizmoGroupType *gzgt)
bool ED_gizmo_poll_or_unlink_delayed_from_operator(const bContext *C, wmGizmoGroupType *gzgt, const char *idname)
bool ED_gizmo_poll_or_unlink_delayed_from_tool_ex(const bContext *C, wmGizmoGroupType *gzgt, const char *gzgt_idname)
void ED_transform_calc_orientation_from_type(const bContext *C, float r_mat[3][3])
int ED_undo_operator_repeat(bContext *C, wmOperator *op)
Definition ed_undo.cc:644
RegionView3D * ED_view3d_context_rv3d(bContext *C)
void ED_view3d_win_to_3d_int(const View3D *v3d, const ARegion *region, const float depth_pt[3], const int mval[2], float r_out[3])
bool ED_view3d_win_to_3d_on_plane_int(const ARegion *region, const float plane[4], const int mval[2], bool do_clip, float r_out[3])
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
#define C
Definition RandGen.cpp:29
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_AXIS_X
@ TH_GIZMO_PRIMARY
@ WM_GIZMO_HIDDEN
@ WM_GIZMO_OPERATOR_TOOL_INIT
@ WM_GIZMO_DRAW_VALUE
@ WM_GIZMO_DRAW_OFFSET_SCALE
@ WM_GIZMO_HIDDEN_SELECT
@ WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP
@ WM_GIZMOGROUPTYPE_3D
@ WM_GIZMO_STATE_HIGHLIGHT
@ WM_GIZMO_STATE_MODAL
#define WM_toolsystem_ref_properties_ensure_from_gizmo_group(tref, gzgroup, r_ptr)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_VERTS_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
static void gizmo_spin_prop_depth_get(const wmGizmo *gz, wmGizmoProperty *gz_prop, void *value_p)
static void gizmo_mesh_spin_redo_setup(const bContext *C, wmGizmoGroup *gzgroup)
static bool gizmo_mesh_spin_redo_poll(const bContext *C, wmGizmoGroupType *gzgt)
static void gizmo_mesh_spin_redo_update_orient_axis(GizmoGroupData_SpinRedo *ggd, const float plane_no[3])
static void gizmo_spin_prop_axis_angle_get(const wmGizmo *gz, wmGizmoProperty *gz_prop, void *value_p)
static void gizmo_spin_exec(GizmoGroupData_SpinRedo *ggd)
static void gizmo_mesh_spin_init_invoke_prepare(const bContext *, wmGizmoGroup *gzgroup, wmGizmo *gz, const wmEvent *)
#define INIT_SCALE_BASE
static void gizmo_spin_prop_angle_set(const wmGizmo *gz, wmGizmoProperty *gz_prop, const void *value_p)
static void gizmo_spin_prop_depth_set(const wmGizmo *gz, wmGizmoProperty *gz_prop, const void *value_p)
void MESH_GGT_spin_redo(wmGizmoGroupType *gzgt)
static void gizmo_spin_prop_axis_angle_set(const wmGizmo *gz, wmGizmoProperty *gz_prop, const void *value_p)
static void gizmo_mesh_spin_init_setup(const bContext *, wmGizmoGroup *gzgroup)
static void gizmo_spin_prop_angle_get(const wmGizmo *gz, wmGizmoProperty *gz_prop, void *value_p)
static const uchar shape_plus[]
#define ORTHO_AXIS_OFFSET
static void gizmo_mesh_spin_init_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
#define INIT_SCALE_BUTTON
static void gizmo_spin_prop_translate_get(const wmGizmo *gz, wmGizmoProperty *gz_prop, void *value_p)
static void gizmo_mesh_spin_init_refresh_axis_orientation(wmGizmoGroup *gzgroup, int axis_index, const float axis_vec[3], const float axis_tan[3])
static void gizmo_mesh_spin_redo_update_from_op(GizmoGroupData_SpinRedo *ggd)
static const float dial_angle_partial_margin
static void gizmo_mesh_spin_init_message_subscribe(const bContext *C, wmGizmoGroup *gzgroup, wmMsgBus *mbus)
static void gizmo_mesh_spin_redo_draw_prepare(const bContext *, wmGizmoGroup *gzgroup)
static const float dial_angle_partial
static void gizmo_spin_prop_translate_set(const wmGizmo *gz, wmGizmoProperty *gz_prop, const void *value)
void MESH_GGT_spin(wmGizmoGroupType *gzgt)
static void gizmo_mesh_spin_redo_modal_from_setup(const bContext *C, wmGizmoGroup *gzgroup)
static void gizmo_mesh_spin_init_refresh(const bContext *C, wmGizmoGroup *gzgroup)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
void RNA_property_float_set_array(PointerRNA *ptr, PropertyRNA *prop, const float *values)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
void RNA_property_unset(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_def_enum_flag(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
const EnumPropertyItem rna_enum_axis_flag_xyz_items[]
struct wmGizmoMap * gizmo_map
float co[3]
struct GizmoGroupData_SpinInit::@117220237124142011313363136145335226167176205055 gizmos
struct GizmoGroupData_SpinInit::@076275372334070172326311247116332227106050157276 prev
struct GizmoGroupData_SpinInit::@231171075003344327243105331021104072165246141060 invoke
struct GizmoGroupData_SpinInit::@163372130305262265165262003176216371026040225130 data
struct GizmoGroupData_SpinRedo::@070216203257373217157336310263215071361154315354 data
struct GizmoGroupData_SpinRedo::@030171037105034227262072241033370357300353040272 prev
float viewinv[4][4]
View3DCursor cursor
struct ARegion * active_region
int ymin
int xmin
wmGizmoGroupFnMsgBusSubscribe message_subscribe
wmGizmoGroupFnRefresh refresh
wmGizmoGroupFnInit setup
const char * idname
wmGizmoGroupFnInvokePrepare invoke_prepare
wmGizmoMapType_Params gzmap_params
eWM_GizmoFlagGroupTypeFlag flag
wmGizmoGroupFnPoll poll
wmGizmoGroupFnDrawPrepare draw_prepare
wmGizmoMap * parent_gzmap
wmGizmoGroupType * type
const wmGizmoPropertyType * type
wmGizmoGroup * parent_gzgroup
eWM_GizmoFlagState state
float matrix_basis[4][4]
float matrix_offset[4][4]
float color_hi[4]
float color[4]
PointerRNA * ptr
eWM_GizmoFlag flag
struct wmOperator * next
struct wmOperatorType * type
struct PointerRNA * ptr
struct wmEvent * eventstate
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_gizmo_modal_set_from_setup(wmGizmoMap *gzmap, bContext *C, wmGizmo *gz, int part_index, const wmEvent *event)
Definition wm_gizmo.cc:415
void WM_gizmo_set_color_highlight(wmGizmo *gz, const float color_hi[4])
Definition wm_gizmo.cc:336
void WM_gizmo_set_line_width(wmGizmo *gz, const float line_width)
Definition wm_gizmo.cc:318
void WM_gizmo_set_matrix_rotation_from_yz_axis(wmGizmo *gz, const float y_axis[3], const float z_axis[3])
Definition wm_gizmo.cc:277
wmGizmo * WM_gizmo_new_ptr(const wmGizmoType *gzt, wmGizmoGroup *gzgroup, PointerRNA *properties)
Definition wm_gizmo.cc:81
void WM_gizmo_set_scale(wmGizmo *gz, const float scale)
Definition wm_gizmo.cc:313
void WM_gizmo_set_matrix_location(wmGizmo *gz, const float origin[3])
Definition wm_gizmo.cc:283
void WM_gizmo_set_flag(wmGizmo *gz, const int flag, const bool enable)
Definition wm_gizmo.cc:303
void WM_gizmo_set_matrix_rotation_from_z_axis(wmGizmo *gz, const float z_axis[3])
Definition wm_gizmo.cc:273
PointerRNA * WM_gizmo_operator_set(wmGizmo *gz, int part_index, wmOperatorType *ot, IDProperty *properties)
Definition wm_gizmo.cc:203
void WM_gizmo_set_color(wmGizmo *gz, const float color[4])
Definition wm_gizmo.cc:327
wmGizmoGroup * WM_gizmomap_group_find(wmGizmoMap *gzmap, const char *idname)
void WM_gizmo_do_msg_notify_tag_refresh(bContext *, wmMsgSubscribeKey *, wmMsgSubscribeValue *msg_val)
void WM_gizmo_target_property_def_func(wmGizmo *gz, const char *idname, const wmGizmoPropertyFnParams *params)
const wmGizmoType * WM_gizmotype_find(const char *idname, bool quiet)
void WM_msg_subscribe_rna_params(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params, const wmMsgSubscribeValue *msg_val_params, const char *id_repr)
void WM_msg_subscribe_rna(wmMsgBus *mbus, PointerRNA *ptr, const PropertyRNA *prop, const wmMsgSubscribeValue *msg_val_params, const char *id_repr)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
wmOperator * WM_operator_last_redo(const bContext *C)
bToolRef * WM_toolsystem_ref_from_context(const bContext *C)
bScreen * WM_window_get_active_screen(const wmWindow *win)