tornavis/source/blender/editors/transform/transform_gizmo_2d.c

1044 lines
33 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edtransform
*
* \name 2D Transform Gizmo
*
* Used for UV/Image Editor
*/
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_layer.h"
#include "RNA_access.h"
#include "UI_resources.h"
#include "UI_view2d.h"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_types.h"
#include "ED_gizmo_library.h"
#include "ED_gizmo_utils.h"
#include "ED_image.h"
#include "ED_screen.h"
#include "ED_uvedit.h"
#include "SEQ_channels.h"
#include "SEQ_iterator.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "SEQ_transform.h"
#include "transform.h"
#include "transform_gizmo.h"
/* -------------------------------------------------------------------- */
/** \name Shared Callback's
* \{ */
static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt)
{
if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
return false;
}
if ((U.gizmo_flag & USER_GIZMO_DRAW) == 0) {
return false;
}
if (G.moving) {
return false;
}
ScrArea *area = CTX_wm_area(C);
if (area == NULL) {
return false;
}
/* NOTE: below this is assumed to be a tool gizmo.
* If there are cases that need to check other flags - this function could be split. */
switch (area->spacetype) {
case SPACE_IMAGE: {
const SpaceImage *sima = area->spacedata.first;
Object *obedit = CTX_data_edit_object(C);
if (!ED_space_image_show_uvedit(sima, obedit)) {
return false;
}
break;
}
case SPACE_SEQ: {
const SpaceSeq *sseq = area->spacedata.first;
if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_TOOL)) {
return false;
}
if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) {
return false;
}
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
if (ed == NULL) {
return false;
}
break;
}
}
return true;
}
static void gizmo2d_pivot_point_message_subscribe(struct wmGizmoGroup *gzgroup,
struct wmMsgBus *mbus,
/* Additional args. */
bScreen *screen,
ScrArea *area,
ARegion *region)
{
wmMsgSubscribeValue msg_sub_value_gz_tag_refresh = {
.owner = region,
.user_data = gzgroup->parent_gzmap,
.notify = WM_gizmo_do_msg_notify_tag_refresh,
};
switch (area->spacetype) {
case SPACE_IMAGE: {
SpaceImage *sima = area->spacedata.first;
PointerRNA ptr;
RNA_pointer_create(&screen->id, &RNA_SpaceImageEditor, sima, &ptr);
{
const PropertyRNA *props[] = {
&rna_SpaceImageEditor_pivot_point,
(sima->around == V3D_AROUND_CURSOR) ? &rna_SpaceImageEditor_cursor_location : NULL,
};
for (int i = 0; i < ARRAY_SIZE(props); i++) {
if (props[i] == NULL) {
continue;
}
WM_msg_subscribe_rna(mbus, &ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
}
}
break;
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Arrow / Cage Gizmo Group
*
* Defines public functions, not the gizmo itself:
*
* - #ED_widgetgroup_gizmo2d_xform_callbacks_set
* - #ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set
*
* \{ */
/* axes as index */
enum {
MAN2D_AXIS_TRANS_X = 0,
MAN2D_AXIS_TRANS_Y,
MAN2D_AXIS_LAST,
};
typedef struct GizmoGroup2D {
wmGizmo *translate_xy[3];
wmGizmo *cage;
/* Current origin in view space, used to update widget origin for possible view changes */
float origin[2];
float min[2];
float max[2];
float rotation;
bool no_cage;
} GizmoGroup2D;
/* **************** Utilities **************** */
static void gizmo2d_get_axis_color(const int axis_idx, float *r_col, float *r_col_hi)
{
const float alpha = 0.6f;
const float alpha_hi = 1.0f;
int col_id;
switch (axis_idx) {
case MAN2D_AXIS_TRANS_X:
col_id = TH_AXIS_X;
break;
case MAN2D_AXIS_TRANS_Y:
col_id = TH_AXIS_Y;
break;
default:
BLI_assert(0);
col_id = TH_AXIS_Y;
break;
}
UI_GetThemeColor4fv(col_id, r_col);
copy_v4_v4(r_col_hi, r_col);
r_col[3] *= alpha;
r_col_hi[3] *= alpha_hi;
}
static GizmoGroup2D *gizmogroup2d_init(wmGizmoGroup *gzgroup)
{
const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
const wmGizmoType *gzt_cage = WM_gizmotype_find("GIZMO_GT_cage_2d", true);
const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true);
GizmoGroup2D *ggd = MEM_callocN(sizeof(GizmoGroup2D), __func__);
ggd->translate_xy[0] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
ggd->translate_xy[1] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
ggd->translate_xy[2] = WM_gizmo_new_ptr(gzt_button, gzgroup, NULL);
ggd->cage = WM_gizmo_new_ptr(gzt_cage, gzgroup, NULL);
RNA_enum_set(ggd->cage->ptr,
"transform",
ED_GIZMO_CAGE_XFORM_FLAG_TRANSLATE | ED_GIZMO_CAGE_XFORM_FLAG_SCALE |
ED_GIZMO_CAGE_XFORM_FLAG_ROTATE);
return ggd;
}
/**
* Calculates origin in view space, use with #gizmo2d_origin_to_region.
*/
static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min, float *r_max)
{
float min_buf[2], max_buf[2];
if (r_min == NULL) {
r_min = min_buf;
}
if (r_max == NULL) {
r_max = max_buf;
}
ScrArea *area = CTX_wm_area(C);
bool has_select = false;
if (area->spacetype == SPACE_IMAGE) {
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
scene, view_layer, NULL, &objects_len);
if (ED_uvedit_minmax_multi(scene, objects, objects_len, r_min, r_max)) {
has_select = true;
}
MEM_freeN(objects);
}
else if (area->spacetype == SPACE_SEQ) {
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
ListBase *channels = SEQ_channels_displayed_get(ed);
SeqCollection *strips = SEQ_query_rendered_strips(scene, channels, seqbase, scene->r.cfra, 0);
SEQ_filter_selected_strips(strips);
int selected_strips = SEQ_collection_len(strips);
if (selected_strips > 0) {
has_select = true;
SEQ_image_transform_bounding_box_from_collection(
scene, strips, selected_strips != 1, r_min, r_max);
}
SEQ_collection_free(strips);
if (selected_strips > 1) {
/* Don't draw the cage as transforming multiple strips isn't currently very useful as it
* doesn't behave as one would expect.
*
* This is because our current transform system doesn't support shearing which would make the
* scaling transforms of the bounding box behave weirdly.
* In addition to this, the rotation of the bounding box can not currently be hooked up
* properly to read the result from the transform system (when transforming multiple strips).
*/
const int pivot_point = scene->toolsettings->sequencer_tool_settings->pivot_point;
if (pivot_point == V3D_AROUND_CURSOR) {
SpaceSeq *sseq = area->spacedata.first;
SEQ_image_preview_unit_to_px(scene, sseq->cursor, r_center);
}
else {
mid_v2_v2v2(r_center, r_min, r_max);
}
zero_v2(r_min);
zero_v2(r_max);
return has_select;
}
}
if (has_select == false) {
zero_v2(r_min);
zero_v2(r_max);
}
mid_v2_v2v2(r_center, r_min, r_max);
return has_select;
}
static int gizmo2d_calc_transform_orientation(const bContext *C)
{
ScrArea *area = CTX_wm_area(C);
if (area->spacetype != SPACE_SEQ) {
return V3D_ORIENT_GLOBAL;
}
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
ListBase *channels = SEQ_channels_displayed_get(ed);
SeqCollection *strips = SEQ_query_rendered_strips(scene, channels, seqbase, scene->r.cfra, 0);
SEQ_filter_selected_strips(strips);
bool use_local_orient = SEQ_collection_len(strips) == 1;
SEQ_collection_free(strips);
if (use_local_orient) {
return V3D_ORIENT_LOCAL;
}
return V3D_ORIENT_GLOBAL;
}
static float gizmo2d_calc_rotation(const bContext *C)
{
ScrArea *area = CTX_wm_area(C);
if (area->spacetype != SPACE_SEQ) {
return 0.0f;
}
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
ListBase *channels = SEQ_channels_displayed_get(ed);
SeqCollection *strips = SEQ_query_rendered_strips(scene, channels, seqbase, scene->r.cfra, 0);
SEQ_filter_selected_strips(strips);
if (SEQ_collection_len(strips) == 1) {
/* Only return the strip rotation if only one is selected. */
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
StripTransform *transform = seq->strip->transform;
float mirror[2];
SEQ_image_transform_mirror_factor_get(seq, mirror);
SEQ_collection_free(strips);
return transform->rotation * mirror[0] * mirror[1];
}
}
SEQ_collection_free(strips);
return 0.0f;
}
static bool seq_get_strip_pivot_median(const Scene *scene, float r_pivot[2])
{
zero_v2(r_pivot);
Editing *ed = SEQ_editing_get(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
ListBase *channels = SEQ_channels_displayed_get(ed);
SeqCollection *strips = SEQ_query_rendered_strips(scene, channels, seqbase, scene->r.cfra, 0);
SEQ_filter_selected_strips(strips);
bool has_select = SEQ_collection_len(strips) != 0;
if (has_select) {
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
float origin[2];
SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin);
add_v2_v2(r_pivot, origin);
}
mul_v2_fl(r_pivot, 1.0f / SEQ_collection_len(strips));
}
SEQ_collection_free(strips);
return has_select;
}
static bool gizmo2d_calc_transform_pivot(const bContext *C, float r_pivot[2])
{
ScrArea *area = CTX_wm_area(C);
Scene *scene = CTX_data_scene(C);
bool has_select = false;
if (area->spacetype == SPACE_IMAGE) {
SpaceImage *sima = area->spacedata.first;
ViewLayer *view_layer = CTX_data_view_layer(C);
ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_pivot, sima->around, &has_select);
}
else if (area->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = area->spacedata.first;
const int pivot_point = scene->toolsettings->sequencer_tool_settings->pivot_point;
if (pivot_point == V3D_AROUND_CURSOR) {
SEQ_image_preview_unit_to_px(scene, sseq->cursor, r_pivot);
Editing *ed = SEQ_editing_get(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
ListBase *channels = SEQ_channels_displayed_get(ed);
SeqCollection *strips = SEQ_query_rendered_strips(
scene, channels, seqbase, scene->r.cfra, 0);
SEQ_filter_selected_strips(strips);
has_select = SEQ_collection_len(strips) != 0;
SEQ_collection_free(strips);
}
else {
has_select = seq_get_strip_pivot_median(scene, r_pivot);
}
}
else {
BLI_assert_msg(0, "Unhandled space type!");
}
return has_select;
}
/**
* Convert origin (or any other point) from view to region space.
*/
BLI_INLINE void gizmo2d_origin_to_region(ARegion *region, float *r_origin)
{
UI_view2d_view_to_region_fl(&region->v2d, r_origin[0], r_origin[1], &r_origin[0], &r_origin[1]);
}
/**
* Custom handler for gizmo widgets
*/
static int gizmo2d_modal(bContext *C,
wmGizmo *widget,
const wmEvent *UNUSED(event),
eWM_GizmoFlagTweak UNUSED(tweak_flag))
{
ARegion *region = CTX_wm_region(C);
float origin[3];
gizmo2d_calc_transform_pivot(C, origin);
gizmo2d_origin_to_region(region, origin);
WM_gizmo_set_matrix_location(widget, origin);
ED_region_tag_redraw_editor_overlays(region);
return OPERATOR_RUNNING_MODAL;
}
static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
{
wmOperatorType *ot_translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
GizmoGroup2D *ggd = gizmogroup2d_init(gzgroup);
gzgroup->customdata = ggd;
for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
wmGizmo *gz = ggd->translate_xy[i];
/* custom handler! */
WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal);
if (i < 2) {
float color[4], color_hi[4];
gizmo2d_get_axis_color(i, color, color_hi);
/* set up widget data */
RNA_float_set(gz->ptr, "length", 0.8f);
float axis[3] = {0.0f};
axis[i] = 1.0f;
WM_gizmo_set_matrix_rotation_from_z_axis(gz, axis);
float offset[3] = {0, 0, 0};
offset[2] = 0.18f;
WM_gizmo_set_matrix_offset_location(gz, offset);
gz->flag |= WM_GIZMO_DRAW_OFFSET_SCALE;
WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH);
WM_gizmo_set_color(gz, color);
WM_gizmo_set_color_highlight(gz, color_hi);
WM_gizmo_set_scale(gz, 1.0f);
}
else {
float color[4], color_hi[4];
UI_GetThemeColor4fv(TH_GIZMO_VIEW_ALIGN, color);
copy_v4_v4(color_hi, color);
color[3] *= 0.6f;
PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
RNA_property_enum_set(gz->ptr, prop, ICON_NONE);
RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP);
/* Make the center low alpha. */
WM_gizmo_set_line_width(gz, 2.0f);
RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0);
WM_gizmo_set_color(gz, color);
WM_gizmo_set_color_highlight(gz, color_hi);
WM_gizmo_set_scale(gz, 0.2f);
}
/* Assign operator. */
PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_translate, NULL);
if (i < 2) {
bool constraint[3] = {false};
constraint[i] = true;
if (RNA_struct_find_property(ptr, "constraint_axis")) {
RNA_boolean_set_array(ptr, "constraint_axis", constraint);
}
}
RNA_boolean_set(ptr, "release_confirm", true);
}
{
wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
wmOperatorType *ot_rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
PointerRNA *ptr;
/* assign operator */
ptr = WM_gizmo_operator_set(ggd->cage, 0, ot_translate, NULL);
RNA_boolean_set(ptr, "release_confirm", 1);
const bool constraint_x[3] = {1, 0, 0};
const bool constraint_y[3] = {0, 1, 0};
ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X, ot_resize, NULL);
PropertyRNA *prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm");
PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
RNA_property_boolean_set(ptr, prop_release_confirm, true);
RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x);
ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X, ot_resize, NULL);
RNA_property_boolean_set(ptr, prop_release_confirm, true);
RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x);
ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y, ot_resize, NULL);
RNA_property_boolean_set(ptr, prop_release_confirm, true);
RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y);
ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y, ot_resize, NULL);
RNA_property_boolean_set(ptr, prop_release_confirm, true);
RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y);
ptr = WM_gizmo_operator_set(
ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y, ot_resize, NULL);
RNA_property_boolean_set(ptr, prop_release_confirm, true);
ptr = WM_gizmo_operator_set(
ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y, ot_resize, NULL);
RNA_property_boolean_set(ptr, prop_release_confirm, true);
ptr = WM_gizmo_operator_set(
ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y, ot_resize, NULL);
RNA_property_boolean_set(ptr, prop_release_confirm, true);
ptr = WM_gizmo_operator_set(
ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y, ot_resize, NULL);
RNA_property_boolean_set(ptr, prop_release_confirm, true);
ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE, ot_rotate, NULL);
RNA_property_boolean_set(ptr, prop_release_confirm, true);
}
}
static void rotate_around_center_v2(float point[2], const float center[2], const float angle)
{
float tmp[2];
sub_v2_v2v2(tmp, point, center);
rotate_v2_v2fl(point, tmp, angle);
add_v2_v2(point, center);
}
static void gizmo2d_xform_refresh(const bContext *C, wmGizmoGroup *gzgroup)
{
GizmoGroup2D *ggd = gzgroup->customdata;
bool has_select;
if (ggd->no_cage) {
has_select = gizmo2d_calc_transform_pivot(C, ggd->origin);
}
else {
has_select = gizmo2d_calc_bounds(C, ggd->origin, ggd->min, ggd->max);
ggd->rotation = gizmo2d_calc_rotation(C);
}
bool show_cage = !ggd->no_cage && !equals_v2v2(ggd->min, ggd->max);
if (has_select == false) {
/* Nothing selected. Disable gizmo drawing and return. */
ggd->cage->flag |= WM_GIZMO_HIDDEN;
for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
ggd->translate_xy[i]->flag |= WM_GIZMO_HIDDEN;
}
return;
}
if (!show_cage) {
/* Disable cage gizmo drawing and return. */
ggd->cage->flag |= WM_GIZMO_HIDDEN;
for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
ggd->translate_xy[i]->flag &= ~WM_GIZMO_HIDDEN;
}
return;
}
/* We will show the cage gizmo! Setup all necessary data. */
ggd->cage->flag &= ~WM_GIZMO_HIDDEN;
for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
ggd->translate_xy[i]->flag |= WM_GIZMO_HIDDEN;
}
}
static void gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
{
ARegion *region = CTX_wm_region(C);
GizmoGroup2D *ggd = gzgroup->customdata;
float origin[3] = {UNPACK2(ggd->origin), 0.0f};
gizmo2d_origin_to_region(region, origin);
for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
wmGizmo *gz = ggd->translate_xy[i];
WM_gizmo_set_matrix_location(gz, origin);
}
UI_view2d_view_to_region_m4(&region->v2d, ggd->cage->matrix_space);
/* Define the bounding box of the gizmo in the offset transform matrix. */
unit_m4(ggd->cage->matrix_offset);
const float min_gizmo_pixel_size = 0.001f; /* Draw Gizmo larger than this many pixels. */
const float min_scale_axis_x = min_gizmo_pixel_size / ggd->cage->matrix_space[0][0];
const float min_scale_axis_y = min_gizmo_pixel_size / ggd->cage->matrix_space[1][1];
ggd->cage->matrix_offset[0][0] = max_ff(min_scale_axis_x, ggd->max[0] - ggd->min[0]);
ggd->cage->matrix_offset[1][1] = max_ff(min_scale_axis_y, ggd->max[1] - ggd->min[1]);
ScrArea *area = CTX_wm_area(C);
if (area->spacetype == SPACE_SEQ) {
Scene *scene = CTX_data_scene(C);
seq_get_strip_pivot_median(scene, origin);
float matrix_rotate[4][4];
unit_m4(matrix_rotate);
copy_v3_v3(matrix_rotate[3], origin);
rotate_m4(matrix_rotate, 'Z', ggd->rotation);
unit_m4(ggd->cage->matrix_basis);
mul_m4_m4m4(ggd->cage->matrix_basis, matrix_rotate, ggd->cage->matrix_basis);
float mid[2];
sub_v2_v2v2(mid, origin, ggd->origin);
mul_v2_fl(mid, -1.0f);
copy_v2_v2(ggd->cage->matrix_offset[3], mid);
}
else {
const float origin_aa[3] = {UNPACK2(ggd->origin), 0.0f};
WM_gizmo_set_matrix_offset_location(ggd->cage, origin_aa);
}
}
static void gizmo2d_xform_invoke_prepare(const bContext *C,
wmGizmoGroup *gzgroup,
wmGizmo *UNUSED(gz),
const wmEvent *UNUSED(event))
{
GizmoGroup2D *ggd = gzgroup->customdata;
wmGizmoOpElem *gzop;
const float *mid = ggd->origin;
const float *min = ggd->min;
const float *max = ggd->max;
/* Define the different transform center points that will be used when grabbing the corners or
* rotating with the gizmo.
*
* The coordinates are referred to as their cardinal directions:
* N
* o
*NW | NE
* x-----------x
* | |
*W| C |E
* | |
* x-----------x
*SW S SE
*/
float n[3] = {mid[0], max[1], 0.0f};
float w[3] = {min[0], mid[1], 0.0f};
float e[3] = {max[0], mid[1], 0.0f};
float s[3] = {mid[0], min[1], 0.0f};
float nw[3] = {min[0], max[1], 0.0f};
float ne[3] = {max[0], max[1], 0.0f};
float sw[3] = {min[0], min[1], 0.0f};
float se[3] = {max[0], min[1], 0.0f};
float c[3] = {mid[0], mid[1], 0.0f};
float orient_matrix[3][3];
unit_m3(orient_matrix);
ScrArea *area = CTX_wm_area(C);
if (ggd->rotation != 0.0f && area->spacetype == SPACE_SEQ) {
float origin[3];
Scene *scene = CTX_data_scene(C);
seq_get_strip_pivot_median(scene, origin);
/* We need to rotate the cardinal points so they align with the rotated bounding box. */
rotate_around_center_v2(n, origin, ggd->rotation);
rotate_around_center_v2(w, origin, ggd->rotation);
rotate_around_center_v2(e, origin, ggd->rotation);
rotate_around_center_v2(s, origin, ggd->rotation);
rotate_around_center_v2(nw, origin, ggd->rotation);
rotate_around_center_v2(ne, origin, ggd->rotation);
rotate_around_center_v2(sw, origin, ggd->rotation);
rotate_around_center_v2(se, origin, ggd->rotation);
rotate_around_center_v2(c, origin, ggd->rotation);
axis_angle_to_mat3_single(orient_matrix, 'Z', ggd->rotation);
}
int orient_type = gizmo2d_calc_transform_orientation(C);
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X);
PropertyRNA *prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override");
PropertyRNA *prop_mouse_dir = RNA_struct_find_property(&gzop->ptr, "mouse_dir_constraint");
RNA_property_float_set_array(&gzop->ptr, prop_center_override, e);
RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[0]);
RNA_enum_set(&gzop->ptr, "orient_type", orient_type);
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X);
RNA_property_float_set_array(&gzop->ptr, prop_center_override, w);
RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[0]);
RNA_enum_set(&gzop->ptr, "orient_type", orient_type);
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y);
RNA_property_float_set_array(&gzop->ptr, prop_center_override, n);
RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[1]);
RNA_enum_set(&gzop->ptr, "orient_type", orient_type);
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y);
RNA_property_float_set_array(&gzop->ptr, prop_center_override, s);
RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[1]);
RNA_enum_set(&gzop->ptr, "orient_type", orient_type);
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y);
RNA_property_float_set_array(&gzop->ptr, prop_center_override, ne);
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y);
RNA_property_float_set_array(&gzop->ptr, prop_center_override, se);
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y);
RNA_property_float_set_array(&gzop->ptr, prop_center_override, nw);
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y);
RNA_property_float_set_array(&gzop->ptr, prop_center_override, sw);
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE);
RNA_property_float_set_array(&gzop->ptr, prop_center_override, c);
}
void ED_widgetgroup_gizmo2d_xform_callbacks_set(wmGizmoGroupType *gzgt)
{
gzgt->poll = gizmo2d_generic_poll;
gzgt->setup = gizmo2d_xform_setup;
gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
gzgt->refresh = gizmo2d_xform_refresh;
gzgt->draw_prepare = gizmo2d_xform_draw_prepare;
gzgt->invoke_prepare = gizmo2d_xform_invoke_prepare;
}
static void gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup)
{
gizmo2d_xform_setup(C, gzgroup);
GizmoGroup2D *ggd = gzgroup->customdata;
ggd->no_cage = true;
}
static void gizmo2d_xform_no_cage_message_subscribe(const struct bContext *C,
struct wmGizmoGroup *gzgroup,
struct wmMsgBus *mbus)
{
bScreen *screen = CTX_wm_screen(C);
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, area, region);
}
void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType *gzgt)
{
ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt);
gzgt->setup = gizmo2d_xform_setup_no_cage;
gzgt->message_subscribe = gizmo2d_xform_no_cage_message_subscribe;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Scale Handles
*
* Defines public functions, not the gizmo itself:
*
* - #ED_widgetgroup_gizmo2d_resize_callbacks_set
*
* \{ */
typedef struct GizmoGroup_Resize2D {
wmGizmo *gizmo_xy[3];
float origin[2];
float rotation;
} GizmoGroup_Resize2D;
static GizmoGroup_Resize2D *gizmogroup2d_resize_init(wmGizmoGroup *gzgroup)
{
const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true);
GizmoGroup_Resize2D *ggd = MEM_callocN(sizeof(GizmoGroup_Resize2D), __func__);
ggd->gizmo_xy[0] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
ggd->gizmo_xy[1] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
ggd->gizmo_xy[2] = WM_gizmo_new_ptr(gzt_button, gzgroup, NULL);
return ggd;
}
static void gizmo2d_resize_refresh(const bContext *C, wmGizmoGroup *gzgroup)
{
GizmoGroup_Resize2D *ggd = gzgroup->customdata;
float origin[3];
const bool has_select = gizmo2d_calc_transform_pivot(C, origin);
if (has_select == false) {
for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) {
ggd->gizmo_xy[i]->flag |= WM_GIZMO_HIDDEN;
}
}
else {
for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) {
ggd->gizmo_xy[i]->flag &= ~WM_GIZMO_HIDDEN;
}
copy_v2_v2(ggd->origin, origin);
ggd->rotation = gizmo2d_calc_rotation(C);
}
}
static void gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
{
ARegion *region = CTX_wm_region(C);
GizmoGroup_Resize2D *ggd = gzgroup->customdata;
float origin[3] = {UNPACK2(ggd->origin), 0.0f};
gizmo2d_origin_to_region(region, origin);
for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) {
wmGizmo *gz = ggd->gizmo_xy[i];
WM_gizmo_set_matrix_location(gz, origin);
if (i < 2) {
float axis[3] = {0.0f}, rotated_axis[3];
axis[i] = 1.0f;
rotate_v3_v3v3fl(rotated_axis, axis, (float[3]){0, 0, 1}, ggd->rotation);
WM_gizmo_set_matrix_rotation_from_z_axis(gz, rotated_axis);
}
}
}
static void gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
{
wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
GizmoGroup_Resize2D *ggd = gizmogroup2d_resize_init(gzgroup);
gzgroup->customdata = ggd;
for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) {
wmGizmo *gz = ggd->gizmo_xy[i];
/* custom handler! */
WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal);
if (i < 2) {
float color[4], color_hi[4];
gizmo2d_get_axis_color(i, color, color_hi);
/* set up widget data */
RNA_float_set(gz->ptr, "length", 1.0f);
RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX);
WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH);
WM_gizmo_set_color(gz, color);
WM_gizmo_set_color_highlight(gz, color_hi);
WM_gizmo_set_scale(gz, 1.0f);
}
else {
float color[4], color_hi[4];
UI_GetThemeColor4fv(TH_GIZMO_VIEW_ALIGN, color);
copy_v4_v4(color_hi, color);
color[3] *= 0.6f;
PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
RNA_property_enum_set(gz->ptr, prop, ICON_NONE);
RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP);
/* Make the center low alpha. */
WM_gizmo_set_line_width(gz, 2.0f);
RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0);
WM_gizmo_set_color(gz, color);
WM_gizmo_set_color_highlight(gz, color_hi);
WM_gizmo_set_scale(gz, 1.2f);
}
/* Assign operator. */
PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_resize, NULL);
if (i < 2) {
bool constraint[3] = {false};
constraint[i] = true;
if (RNA_struct_find_property(ptr, "constraint_axis")) {
RNA_boolean_set_array(ptr, "constraint_axis", constraint);
}
}
RNA_boolean_set(ptr, "release_confirm", true);
}
}
static void gizmo2d_resize_invoke_prepare(const bContext *C,
wmGizmoGroup *UNUSED(gzgroup),
wmGizmo *gz,
const wmEvent *UNUSED(event))
{
wmGizmoOpElem *gzop;
int orient_type = gizmo2d_calc_transform_orientation(C);
gzop = WM_gizmo_operator_get(gz, 0);
RNA_enum_set(&gzop->ptr, "orient_type", orient_type);
}
static void gizmo2d_resize_message_subscribe(const struct bContext *C,
struct wmGizmoGroup *gzgroup,
struct wmMsgBus *mbus)
{
bScreen *screen = CTX_wm_screen(C);
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, area, region);
}
void ED_widgetgroup_gizmo2d_resize_callbacks_set(wmGizmoGroupType *gzgt)
{
gzgt->poll = gizmo2d_generic_poll;
gzgt->setup = gizmo2d_resize_setup;
gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
gzgt->refresh = gizmo2d_resize_refresh;
gzgt->draw_prepare = gizmo2d_resize_draw_prepare;
gzgt->invoke_prepare = gizmo2d_resize_invoke_prepare;
gzgt->message_subscribe = gizmo2d_resize_message_subscribe;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Rotate Handles
*
* Defines public functions, not the gizmo itself:
*
* - #ED_widgetgroup_gizmo2d_rotate_setup
*
* \{ */
typedef struct GizmoGroup_Rotate2D {
wmGizmo *gizmo;
float origin[2];
} GizmoGroup_Rotate2D;
static GizmoGroup_Rotate2D *gizmogroup2d_rotate_init(wmGizmoGroup *gzgroup)
{
const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true);
GizmoGroup_Rotate2D *ggd = MEM_callocN(sizeof(GizmoGroup_Rotate2D), __func__);
ggd->gizmo = WM_gizmo_new_ptr(gzt_button, gzgroup, NULL);
return ggd;
}
static void gizmo2d_rotate_refresh(const bContext *C, wmGizmoGroup *gzgroup)
{
GizmoGroup_Rotate2D *ggd = gzgroup->customdata;
float origin[3];
const bool has_select = gizmo2d_calc_transform_pivot(C, origin);
if (has_select == false) {
ggd->gizmo->flag |= WM_GIZMO_HIDDEN;
}
else {
ggd->gizmo->flag &= ~WM_GIZMO_HIDDEN;
copy_v2_v2(ggd->origin, origin);
}
}
static void gizmo2d_rotate_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
{
ARegion *region = CTX_wm_region(C);
GizmoGroup_Rotate2D *ggd = gzgroup->customdata;
float origin[3] = {UNPACK2(ggd->origin), 0.0f};
gizmo2d_origin_to_region(region, origin);
wmGizmo *gz = ggd->gizmo;
WM_gizmo_set_matrix_location(gz, origin);
}
static void gizmo2d_rotate_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
{
wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_rotate", true);
GizmoGroup_Rotate2D *ggd = gizmogroup2d_rotate_init(gzgroup);
gzgroup->customdata = ggd;
/* Other setup functions iterate over axis. */
{
wmGizmo *gz = ggd->gizmo;
/* custom handler! */
WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal);
WM_gizmo_set_scale(gz, 1.2f);
{
float color[4];
UI_GetThemeColor4fv(TH_GIZMO_VIEW_ALIGN, color);
PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
RNA_property_enum_set(gz->ptr, prop, ICON_NONE);
RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP);
/* Make the center low alpha. */
WM_gizmo_set_line_width(gz, 2.0f);
RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0);
WM_gizmo_set_color(gz, color);
WM_gizmo_set_color_highlight(gz, color);
}
/* Assign operator. */
PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_resize, NULL);
RNA_boolean_set(ptr, "release_confirm", true);
}
}
static void gizmo2d_rotate_message_subscribe(const struct bContext *C,
struct wmGizmoGroup *gzgroup,
struct wmMsgBus *mbus)
{
bScreen *screen = CTX_wm_screen(C);
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, area, region);
}
void ED_widgetgroup_gizmo2d_rotate_callbacks_set(wmGizmoGroupType *gzgt)
{
gzgt->poll = gizmo2d_generic_poll;
gzgt->setup = gizmo2d_rotate_setup;
gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
gzgt->refresh = gizmo2d_rotate_refresh;
gzgt->draw_prepare = gizmo2d_rotate_draw_prepare;
gzgt->message_subscribe = gizmo2d_rotate_message_subscribe;
}
/** \} */