VSE: Implement the bounding box (xform) tool in the seq preview window

Make the "xform" tool/gizmo available for strip transformations in the
sequencer preview window.

Because of the amount of hacks needed to make the gizmo work nicely with
multiple strips at the same time, it was decided to only show the
translate gizmo when multiple strips are selected.

This is because the transforms with multiple strips would appear buggy
because of our lack of shearing support in the transform system.
There is also currently no way to properly sync the gizmo drawing with
the transform when using multiple strips.

Reviewed By: Richard Antalik, Campbell Barton

Differential Revision: http://developer.blender.org/D12729
This commit is contained in:
Sebastian Parborg 2021-10-08 12:09:27 +02:00
parent a3e2cc0bb7
commit 482806c816
Notes: blender-bot 2023-02-14 10:35:28 +01:00
Referenced by commit 9a3c7da934, Fix T92184: Cage gizmo doesn't with area light
Referenced by issue #95290, Regression: 2D transform gizmo hover cursor does not indicate axis anymore
Referenced by issue #94784, Crop node gizmo doesn't work
Referenced by issue #92184, Manipulating area light size with widget is not working
16 changed files with 475 additions and 365 deletions

View File

@ -2515,6 +2515,18 @@ class _defs_sequencer_generic:
keymap="Sequencer Tool: Scale",
)
@ToolDef.from_fn
def transform():
return dict(
idname="builtin.transform",
label="Transform",
description=(
"Supports any combination of grab, rotate, and scale at once"
),
icon="ops.transform.transform",
widget="SEQUENCER_GGT_gizmo2d",
# No keymap default action, only for gizmo!
)
class _defs_sequencer_select:
@ToolDef.from_fn
@ -3112,6 +3124,8 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_sequencer_generic.translate,
_defs_sequencer_generic.rotate,
_defs_sequencer_generic.scale,
_defs_sequencer_generic.transform,
None,
_defs_sequencer_generic.sample,
*_tools_annotate,
],
@ -3126,9 +3140,12 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_sequencer_generic.translate,
_defs_sequencer_generic.rotate,
_defs_sequencer_generic.scale,
_defs_sequencer_generic.blade,
_defs_sequencer_generic.transform,
None,
_defs_sequencer_generic.sample,
*_tools_annotate,
None,
_defs_sequencer_generic.blade,
],
}

View File

@ -98,64 +98,12 @@ static bool gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[2],
zero_v2(margin);
return false;
}
margin[0] = ((handle_size * scale_xy[0]));
margin[1] = ((handle_size * scale_xy[1]));
return true;
}
/* -------------------------------------------------------------------- */
static void gizmo_rect_pivot_from_scale_part(int part, float r_pt[2], bool r_constrain_axis[2])
{
bool x = true, y = true;
switch (part) {
case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X: {
ARRAY_SET_ITEMS(r_pt, 0.5, 0.0);
x = false;
break;
}
case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X: {
ARRAY_SET_ITEMS(r_pt, -0.5, 0.0);
x = false;
break;
}
case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y: {
ARRAY_SET_ITEMS(r_pt, 0.0, 0.5);
y = false;
break;
}
case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y: {
ARRAY_SET_ITEMS(r_pt, 0.0, -0.5);
y = false;
break;
}
case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y: {
ARRAY_SET_ITEMS(r_pt, 0.5, 0.5);
x = y = false;
break;
}
case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y: {
ARRAY_SET_ITEMS(r_pt, 0.5, -0.5);
x = y = false;
break;
}
case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y: {
ARRAY_SET_ITEMS(r_pt, -0.5, 0.5);
x = y = false;
break;
}
case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y: {
ARRAY_SET_ITEMS(r_pt, -0.5, -0.5);
x = y = false;
break;
}
default:
BLI_assert(0);
}
r_constrain_axis[0] = x;
r_constrain_axis[1] = y;
}
/* -------------------------------------------------------------------- */
/** \name Box Draw Style
*
@ -400,6 +348,7 @@ static void cage2d_draw_box_interaction(const float color[4],
ARRAY_SET_ITEMS(verts[1], r_rotate.xmin, r_rotate.ymax);
ARRAY_SET_ITEMS(verts[2], r_rotate.xmax, r_rotate.ymax);
ARRAY_SET_ITEMS(verts[3], r_rotate.xmax, r_rotate.ymin);
verts_len = 4;
if (is_solid) {
prim_type = GPU_PRIM_TRI_FAN;
@ -769,10 +718,10 @@ static int gizmo_cage2d_get_cursor(wmGizmo *gz)
return WM_CURSOR_NSEW_SCROLL;
case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X:
case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X:
return WM_CURSOR_X_MOVE;
return WM_CURSOR_NSEW_SCROLL;
case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y:
case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y:
return WM_CURSOR_Y_MOVE;
return WM_CURSOR_NSEW_SCROLL;
/* TODO: diagonal cursor. */
case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y:
@ -937,173 +886,11 @@ static int gizmo_cage2d_invoke(bContext *C, wmGizmo *gz, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
static int gizmo_cage2d_modal(bContext *C,
wmGizmo *gz,
const wmEvent *event,
static int gizmo_cage2d_modal(bContext *UNUSED(C),
wmGizmo *UNUSED(gz),
const wmEvent *UNUSED(event),
eWM_GizmoFlagTweak UNUSED(tweak_flag))
{
if (event->type != MOUSEMOVE) {
return OPERATOR_RUNNING_MODAL;
}
/* For transform logic to be manageable we operate in -0.5..0.5 2D space,
* no matter the size of the rectangle, mouse coords are scaled to unit space.
* The mouse coords have been projected into the matrix
* so we don't need to worry about axis alignment.
*
* - The cursor offset are multiplied by 'dims'.
* - Matrix translation is also multiplied by 'dims'.
*/
RectTransformInteraction *data = gz->interaction_data;
float point_local[2];
float dims[2];
RNA_float_get_array(gz->ptr, "dimensions", dims);
{
float matrix_back[4][4];
copy_m4_m4(matrix_back, gz->matrix_offset);
copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset);
bool ok = gizmo_window_project_2d(
C, gz, (const float[2]){UNPACK2(event->mval)}, 2, false, point_local);
copy_m4_m4(gz->matrix_offset, matrix_back);
if (!ok) {
return OPERATOR_RUNNING_MODAL;
}
}
const int transform_flag = RNA_enum_get(gz->ptr, "transform");
wmGizmoProperty *gz_prop;
gz_prop = WM_gizmo_target_property_find(gz, "matrix");
if (gz_prop->type != NULL) {
WM_gizmo_target_property_float_get_array(gz, gz_prop, &gz->matrix_offset[0][0]);
}
if (gz->highlight_part == ED_GIZMO_CAGE2D_PART_TRANSLATE) {
/* do this to prevent clamping from changing size */
copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset);
gz->matrix_offset[3][0] = data->orig_matrix_offset[3][0] +
(point_local[0] - data->orig_mouse[0]);
gz->matrix_offset[3][1] = data->orig_matrix_offset[3][1] +
(point_local[1] - data->orig_mouse[1]);
}
else if (gz->highlight_part == ED_GIZMO_CAGE2D_PART_ROTATE) {
#define MUL_V2_V3_M4_FINAL(test_co, mouse_co) \
mul_v3_m4v3( \
test_co, data->orig_matrix_final_no_offset, ((const float[3]){UNPACK2(mouse_co), 0.0}))
float test_co[3];
if (data->dial == NULL) {
MUL_V2_V3_M4_FINAL(test_co, data->orig_matrix_offset[3]);
data->dial = BLI_dial_init(test_co, FLT_EPSILON);
MUL_V2_V3_M4_FINAL(test_co, data->orig_mouse);
BLI_dial_angle(data->dial, test_co);
}
/* rotate */
MUL_V2_V3_M4_FINAL(test_co, point_local);
const float angle = BLI_dial_angle(data->dial, test_co);
float matrix_space_inv[4][4];
float matrix_rotate[4][4];
float pivot[3];
copy_v3_v3(pivot, data->orig_matrix_offset[3]);
invert_m4_m4(matrix_space_inv, gz->matrix_space);
unit_m4(matrix_rotate);
mul_m4_m4m4(matrix_rotate, matrix_rotate, matrix_space_inv);
rotate_m4(matrix_rotate, 'Z', -angle);
mul_m4_m4m4(matrix_rotate, matrix_rotate, gz->matrix_space);
zero_v3(matrix_rotate[3]);
transform_pivot_set_m4(matrix_rotate, pivot);
mul_m4_m4m4(gz->matrix_offset, matrix_rotate, data->orig_matrix_offset);
#undef MUL_V2_V3_M4_FINAL
}
else {
/* scale */
copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset);
float pivot[2];
bool constrain_axis[2] = {false};
if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) {
gizmo_rect_pivot_from_scale_part(gz->highlight_part, pivot, constrain_axis);
}
else {
zero_v2(pivot);
}
/* Cursor deltas scaled to (-0.5..0.5). */
float delta_orig[2], delta_curr[2];
for (int i = 0; i < 2; i++) {
delta_orig[i] = ((data->orig_mouse[i] - data->orig_matrix_offset[3][i]) / dims[i]) -
pivot[i];
delta_curr[i] = ((point_local[i] - data->orig_matrix_offset[3][i]) / dims[i]) - pivot[i];
}
float scale[2] = {1.0f, 1.0f};
for (int i = 0; i < 2; i++) {
if (constrain_axis[i] == false) {
if (delta_orig[i] < 0.0f) {
delta_orig[i] *= -1.0f;
delta_curr[i] *= -1.0f;
}
const int sign = signum_i(scale[i]);
scale[i] = 1.0f + ((delta_curr[i] - delta_orig[i]) / len_v3(data->orig_matrix_offset[i]));
if ((transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_SIGNED) == 0) {
if (sign != signum_i(scale[i])) {
scale[i] = 0.0f;
}
}
}
}
if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_UNIFORM) {
if (constrain_axis[0] == false && constrain_axis[1] == false) {
scale[1] = scale[0] = (scale[1] + scale[0]) / 2.0f;
}
else if (constrain_axis[0] == false) {
scale[1] = scale[0];
}
else if (constrain_axis[1] == false) {
scale[0] = scale[1];
}
else {
BLI_assert(0);
}
}
/* scale around pivot */
float matrix_scale[4][4];
unit_m4(matrix_scale);
mul_v3_fl(matrix_scale[0], scale[0]);
mul_v3_fl(matrix_scale[1], scale[1]);
transform_pivot_set_m4(matrix_scale,
(const float[3]){pivot[0] * dims[0], pivot[1] * dims[1], 0.0f});
mul_m4_m4m4(gz->matrix_offset, data->orig_matrix_offset, matrix_scale);
}
if (gz_prop->type != NULL) {
WM_gizmo_target_property_float_set_array(C, gz, gz_prop, &gz->matrix_offset[0][0]);
}
/* tag the region for redraw */
ED_region_tag_redraw_editor_overlays(CTX_wm_region(C));
WM_event_add_mousemove(CTX_wm_window(C));
return OPERATOR_RUNNING_MODAL;
}

View File

@ -2250,6 +2250,11 @@ void sequencer_draw_preview(const bContext *C,
sequencer_draw_maskedit(C, scene, region, sseq);
#endif
/* Draw registered callbacks. */
GPU_framebuffer_bind(framebuffer_overlay);
ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW);
GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
/* Scope is freed in sequencer_check_scopes when `ibuf` changes and redraw is needed. */
if (ibuf) {
IMB_freeImBuf(ibuf);

View File

@ -797,6 +797,25 @@ static bool transform_event_modal_constraint(TransInfo *t, short modal_type)
if (constraint_new == CON_AXIS2) {
return false;
}
if (t->data_type == TC_SEQ_IMAGE_DATA) {
/* Setup the 2d msg string so it writes out the transform space. */
msg_2d = msg_3d;
short orient_index = 1;
if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) {
/* Successive presses on existing axis, cycle orientation modes. */
orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient));
}
transform_orientations_current_set(t, orient_index);
if (orient_index != 0) {
/* Make sure that we don't stop the constraint unless we are looped back around to
* "no constraint". */
constraint_curr = -1;
}
}
if (constraint_curr == constraint_new) {
stopConstraint(t);
}

View File

@ -755,7 +755,7 @@ void drawConstraint(TransInfo *t)
{
TransCon *tc = &(t->con);
if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) {
if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE, SPACE_SEQ)) {
return;
}
if (!(tc->mode & CON_APPLY)) {
@ -921,6 +921,16 @@ static void drawObjectConstraint(TransInfo *t)
}
}
if (t->options & CTX_SEQUENCER_IMAGE) {
/* Because we construct an "L" shape to deform the sequence, we should skip
* all points except the first vertex. Otherwise we will draw the same axis constraint line
* 3 times for each strip.
*/
if (i % 3 != 0) {
continue;
}
}
if (t->flag & T_EDIT) {
mul_v3_m4v3(co, tc->mat, td->center);

View File

@ -64,12 +64,16 @@ static TransData *SeqToTransData(const Scene *scene,
SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin);
float vertex[2] = {origin[0], origin[1]};
/* Add control vertex, so rotation and scale can be calculated. */
/* Add control vertex, so rotation and scale can be calculated.
* All three vertices will form a "L" shape that is aligned to the local strip axis.
*/
if (vert_index == 1) {
vertex[0] += 1.0f;
vertex[0] += cosf(transform->rotation);
vertex[1] += sinf(transform->rotation);
}
else if (vert_index == 2) {
vertex[1] += 1.0f;
vertex[0] -= sinf(transform->rotation);
vertex[1] += cosf(transform->rotation);
}
td2d->loc[0] = vertex[0];
@ -81,10 +85,12 @@ static TransData *SeqToTransData(const Scene *scene,
td->center[0] = origin[0];
td->center[1] = origin[1];
memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
unit_m3(td->mtx);
unit_m3(td->smtx);
unit_m3(td->axismtx);
rotate_m3(td->axismtx, transform->rotation);
normalize_m3(td->axismtx);
tdseq->seq = seq;
copy_v2_v2(tdseq->orig_origin_position, origin);
@ -159,18 +165,18 @@ void recalcData_sequencer_image(TransInfo *t)
for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) {
/* Origin. */
float loc[2];
copy_v2_v2(loc, td2d->loc);
float origin[2];
copy_v2_v2(origin, td2d->loc);
i++, td++, td2d++;
/* X and Y control points used to read scale and rotation. */
float handle_x[2];
copy_v2_v2(handle_x, td2d->loc);
sub_v2_v2(handle_x, loc);
sub_v2_v2(handle_x, origin);
i++, td++, td2d++;
float handle_y[2];
copy_v2_v2(handle_y, td2d->loc);
sub_v2_v2(handle_y, loc);
sub_v2_v2(handle_y, origin);
TransDataSeq *tdseq = td->extra;
Sequence *seq = tdseq->seq;
@ -181,8 +187,9 @@ void recalcData_sequencer_image(TransInfo *t)
/* Calculate translation. */
float translation[2];
copy_v2_v2(translation, tdseq->orig_origin_position);
sub_v2_v2(translation, loc);
sub_v2_v2(translation, origin);
mul_v2_v2(translation, mirror);
transform->xofs = tdseq->orig_translation[0] - translation[0];
transform->yofs = tdseq->orig_translation[1] - translation[1];
@ -192,7 +199,8 @@ void recalcData_sequencer_image(TransInfo *t)
/* Rotation. Scaling can cause negative rotation. */
if (t->mode == TFM_ROTATION) {
float rotation = angle_signed_v2v2(handle_x, (float[]){1, 0}) * mirror[0] * mirror[1];
const float orig_dir[2] = {cosf(tdseq->orig_rotation), sinf(tdseq->orig_rotation)};
float rotation = angle_signed_v2v2(handle_x, orig_dir) * mirror[0] * mirror[1];
transform->rotation = tdseq->orig_rotation + rotation;
transform->rotation += DEG2RAD(360.0);
transform->rotation = fmod(transform->rotation, DEG2RAD(360.0));

View File

@ -73,42 +73,56 @@
void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis, short options)
{
if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_SEQ)) {
return;
}
float v1[3], v2[3], v3[3];
uchar col[3], col2[3];
if (t->spacetype == SPACE_VIEW3D) {
View3D *v3d = t->view;
GPU_matrix_push();
copy_v3_v3(v3, dir);
mul_v3_fl(v3, v3d->clip_end);
sub_v3_v3v3(v2, center, v3);
add_v3_v3v3(v1, center, v3);
if (options & DRAWLIGHT) {
col[0] = col[1] = col[2] = 220;
}
else {
UI_GetThemeColor3ubv(TH_GRID, col);
}
UI_make_axis_color(col, col2, axis);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ubv(col2);
immBegin(GPU_PRIM_LINES, 2);
immVertex3fv(pos, v1);
immVertex3fv(pos, v2);
immEnd();
immUnbindProgram();
GPU_matrix_pop();
}
else if (t->spacetype == SPACE_SEQ) {
View2D *v2d = t->view;
copy_v3_v3(v3, dir);
float max_dist = max_ff(BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur));
mul_v3_fl(v3, max_dist);
sub_v3_v3v3(v2, center, v3);
add_v3_v3v3(v1, center, v3);
}
GPU_matrix_push();
if (options & DRAWLIGHT) {
col[0] = col[1] = col[2] = 220;
}
else {
UI_GetThemeColor3ubv(TH_GRID, col);
}
UI_make_axis_color(col, col2, axis);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ubv(col2);
immBegin(GPU_PRIM_LINES, 2);
immVertex3fv(pos, v1);
immVertex3fv(pos, v2);
immEnd();
immUnbindProgram();
GPU_matrix_pop();
}
/**

View File

@ -173,6 +173,7 @@ typedef struct GizmoGroup2D {
float origin[2];
float min[2];
float max[2];
float rotation;
bool no_cage;
@ -241,7 +242,7 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min
}
ScrArea *area = CTX_wm_area(C);
bool changed = false;
bool has_select = false;
if (area->spacetype == SPACE_IMAGE) {
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@ -249,18 +250,75 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
view_layer, NULL, &objects_len);
if (ED_uvedit_minmax_multi(scene, objects, objects_len, r_min, r_max)) {
changed = true;
has_select = true;
}
MEM_freeN(objects);
}
else if (area->spacetype == SPACE_SEQ) {
Scene *scene = CTX_data_scene(C);
ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0);
SEQ_filter_selected_strips(strips);
int selected_strips = SEQ_collection_len(strips);
if (selected_strips > 0) {
INIT_MINMAX2(r_min, r_max);
has_select = true;
if (changed == false) {
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
float quad[4][2];
SEQ_image_transform_quad_get(scene, seq, selected_strips != 1, quad);
for (int i = 0; i < 4; i++) {
minmax_v2v2_v2(r_min, r_max, quad[i]);
}
}
}
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).
*/
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 changed;
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);
SeqCollection *strips = SEQ_query_rendered_strips(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)
@ -276,9 +334,10 @@ static float gizmo2d_calc_rotation(const bContext *C)
SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0);
SEQ_filter_selected_strips(strips);
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
if (seq == ed->act_seq) {
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);
@ -436,17 +495,17 @@ static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup
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_array(ptr, prop_constraint_axis, constraint_x);
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);
RNA_property_boolean_set(ptr, prop_release_confirm, true);
ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y, ot_resize, NULL);
RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y);
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_array(ptr, prop_constraint_axis, constraint_y);
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);
@ -465,87 +524,51 @@ static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup
}
}
static void gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup)
static void rotate_around_center_v2(float point[2], const float center[2], const float angle)
{
gizmo2d_xform_setup(C, gzgroup);
GizmoGroup2D *ggd = gzgroup->customdata;
ggd->no_cage = true;
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;
float origin[3];
bool has_select;
if (ggd->no_cage) {
has_select = gizmo2d_calc_center(C, origin);
has_select = gizmo2d_calc_center(C, ggd->origin);
}
else {
has_select = gizmo2d_calc_bounds(C, origin, ggd->min, ggd->max);
has_select = gizmo2d_calc_bounds(C, ggd->origin, ggd->min, ggd->max);
ggd->rotation = gizmo2d_calc_rotation(C);
}
copy_v2_v2(ggd->origin, origin);
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;
}
ggd->cage->flag |= WM_GIZMO_HIDDEN;
return;
}
else {
if (show_cage) {
ggd->cage->flag &= ~WM_GIZMO_HIDDEN;
for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
wmGizmo *gz = ggd->translate_xy[i];
gz->flag |= WM_GIZMO_HIDDEN;
}
}
else {
ggd->cage->flag |= WM_GIZMO_HIDDEN;
for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
wmGizmo *gz = ggd->translate_xy[i];
gz->flag &= ~WM_GIZMO_HIDDEN;
}
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;
}
if (show_cage) {
wmGizmoOpElem *gzop;
float mid[2];
const float *min = ggd->min;
const float *max = ggd->max;
mid_v2_v2v2(mid, min, max);
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");
RNA_property_float_set_array(
&gzop->ptr, prop_center_override, (float[3]){max[0], mid[1], 0.0f});
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X);
RNA_property_float_set_array(
&gzop->ptr, prop_center_override, (float[3]){min[0], mid[1], 0.0f});
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y);
RNA_property_float_set_array(
&gzop->ptr, prop_center_override, (float[3]){mid[0], max[1], 0.0f});
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y);
RNA_property_float_set_array(
&gzop->ptr, prop_center_override, (float[3]){mid[0], min[1], 0.0f});
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, (float[3]){max[0], max[1], 0.0f});
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, (float[3]){max[0], min[1], 0.0f});
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, (float[3]){min[0], max[1], 0.0f});
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, (float[3]){min[0], min[1], 0.0f});
gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE);
RNA_property_float_set_array(
&gzop->ptr, prop_center_override, (float[3]){mid[0], mid[1], 0.0f});
}
/* 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;
}
}
@ -554,7 +577,6 @@ 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};
const float origin_aa[3] = {UNPACK2(ggd->origin), 0.0f};
gizmo2d_origin_to_region(region, origin);
@ -564,9 +586,145 @@ static void gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
}
UI_view2d_view_to_region_m4(&region->v2d, ggd->cage->matrix_space);
WM_gizmo_set_matrix_offset_location(ggd->cage, origin_aa);
/* Define the bounding box of the gizmo in the offset transform matrix. */
unit_m4(ggd->cage->matrix_offset);
ggd->cage->matrix_offset[0][0] = (ggd->max[0] - ggd->min[0]);
ggd->cage->matrix_offset[1][1] = (ggd->max[1] - ggd->min[1]);
ScrArea *area = CTX_wm_area(C);
if (area->spacetype == SPACE_SEQ) {
gizmo2d_calc_center(C, 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];
gizmo2d_calc_center(C, 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);
rotate_m3(orient_matrix, 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,
@ -579,15 +737,6 @@ static void gizmo2d_xform_no_cage_message_subscribe(const struct bContext *C,
gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, area, region);
}
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;
}
void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType *gzgt)
{
ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt);
@ -726,6 +875,18 @@ static void gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgrou
}
}
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)
@ -743,6 +904,7 @@ void ED_widgetgroup_gizmo2d_resize_callbacks_set(wmGizmoGroupType *gzgt)
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;
}

View File

@ -1082,9 +1082,17 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
case TFM_ROTATION:
initRotation(t);
break;
case TFM_RESIZE:
initResize(t);
case TFM_RESIZE: {
float mouse_dir_constraint[3];
if (op) {
RNA_float_get_array(op->ptr, "mouse_dir_constraint", mouse_dir_constraint);
}
else {
zero_v3(mouse_dir_constraint);
}
initResize(t, mouse_dir_constraint);
break;
}
case TFM_SKIN_RESIZE:
initSkinResize(t);
break;

View File

@ -121,7 +121,7 @@ void initMirror(TransInfo *t);
void initPushPull(TransInfo *t);
/* transform_mode_resize.c */
void initResize(TransInfo *t);
void initResize(TransInfo *t, float mouse_dir_constraint[3]);
/* transform_mode_rotate.c */
void initRotation(TransInfo *t);

View File

@ -201,14 +201,45 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
ED_area_status_text(t->area, str);
}
void initResize(TransInfo *t)
void initResize(TransInfo *t, float mouse_dir_constraint[3])
{
t->mode = TFM_RESIZE;
t->transform = applyResize;
t->tsnap.applySnap = ApplySnapResize;
t->tsnap.distance = ResizeBetween;
initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
if (is_zero_v3(mouse_dir_constraint)) {
initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
}
else {
int mval_start[2], mval_end[2];
float mval_dir[3], t_mval[2];
float viewmat[3][3];
copy_m3_m4(viewmat, t->viewmat);
mul_v3_m3v3(mval_dir, viewmat, mouse_dir_constraint);
normalize_v2(mval_dir);
if (is_zero_v2(mval_dir)) {
/* The screen space direction is orthogonal to the view.
* Fall back to constraining on the Y axis. */
mval_dir[0] = 0;
mval_dir[1] = 1;
}
mval_start[0] = t->center2d[0];
mval_start[1] = t->center2d[1];
t_mval[0] = t->mval[0] - mval_start[0];
t_mval[1] = t->mval[1] - mval_start[1];
project_v2_v2v2(mval_dir, t_mval, mval_dir);
mval_end[0] = t->center2d[0] + mval_dir[0];
mval_end[1] = t->center2d[1] + mval_dir[1];
setCustomPoints(t, &t->mouse, mval_end, mval_start);
initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO);
}
t->flag |= T_NULL_ONE;
t->num.val_flag[0] |= NUM_NULL_ONE;

View File

@ -188,7 +188,9 @@ void initShrinkFatten(TransInfo *t)
{
/* If not in mesh edit mode, fallback to Resize. */
if ((t->flag & T_EDIT) == 0 || (t->obedit_type != OB_MESH)) {
initResize(t);
float no_mouse_dir_constraint[3];
zero_v3(no_mouse_dir_constraint);
initResize(t, no_mouse_dir_constraint);
}
else {
t->mode = TFM_SHRINKFATTEN;

View File

@ -59,6 +59,7 @@ typedef struct TransformModeItem {
void (*opfunc)(wmOperatorType *);
} TransformModeItem;
static const float VecZero[3] = {0, 0, 0};
static const float VecOne[3] = {1, 1, 1};
static const char OP_TRANSLATION[] = "TRANSFORM_OT_translate";
@ -783,6 +784,19 @@ static void TRANSFORM_OT_resize(struct wmOperatorType *ot)
RNA_def_float_vector(
ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Scale", "", -FLT_MAX, FLT_MAX);
PropertyRNA *prop;
prop = RNA_def_float_vector(ot->srna,
"mouse_dir_constraint",
3,
VecZero,
-FLT_MAX,
FLT_MAX,
"Mouse Directional Constraint",
"",
-FLT_MAX,
FLT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
WM_operatortype_props_advanced_begin(ot);
Transform_Properties(ot,

View File

@ -30,6 +30,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
@ -52,6 +53,8 @@
#include "ED_armature.h"
#include "SEQ_select.h"
#include "transform.h"
#include "transform_orientations.h"
@ -602,6 +605,16 @@ short transform_orientation_matrix_get(bContext *C,
return V3D_ORIENT_CUSTOM_MATRIX;
}
if (t->spacetype == SPACE_SEQ && t->options & CTX_SEQUENCER_IMAGE) {
Scene *scene = t->scene;
Sequence *seq = SEQ_select_active_get(scene);
if (seq && seq->strip->transform && orient_index == V3D_ORIENT_LOCAL) {
unit_m3(r_spacemtx);
rotate_m3(r_spacemtx, seq->strip->transform->rotation);
return orient_index;
}
}
Object *ob = CTX_data_active_object(C);
Object *obedit = CTX_data_edit_object(C);
Scene *scene = t->scene;

View File

@ -66,6 +66,10 @@ void SEQ_image_transform_mirror_factor_get(const struct Sequence *seq, float r_m
void SEQ_image_transform_origin_offset_pixelspace_get(const struct Scene *scene,
const struct Sequence *seq,
float r_origin[2]);
void SEQ_image_transform_quad_get(const struct Scene *scene,
const struct Sequence *seq,
bool apply_rotation,
float r_quad[4][2]);
void SEQ_image_transform_final_quad_get(const struct Scene *scene,
const struct Sequence *seq,
float r_quad[4][2]);

View File

@ -473,40 +473,41 @@ void SEQ_image_transform_origin_offset_pixelspace_get(const Scene *scene,
*
* \param scene: Scene in which strips are located
* \param seq: Sequence to calculate image transform origin
* \param apply_rotation: Apply sequence rotation transform to the quad
* \param r_origin: return value
*/
void SEQ_image_transform_final_quad_get(const Scene *scene,
const Sequence *seq,
float r_quad[4][2])
static void seq_image_transform_quad_get_ex(const Scene *scene,
const Sequence *seq,
bool apply_rotation,
float r_quad[4][2])
{
StripTransform *transform = seq->strip->transform;
StripCrop *crop = seq->strip->crop;
int imgage_size[2] = {scene->r.xsch, scene->r.ysch};
int image_size[2] = {scene->r.xsch, scene->r.ysch};
if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) {
imgage_size[0] = seq->strip->stripdata->orig_width;
imgage_size[1] = seq->strip->stripdata->orig_height;
image_size[0] = seq->strip->stripdata->orig_width;
image_size[1] = seq->strip->stripdata->orig_height;
}
float transform_matrix[3][3];
loc_rot_size_to_mat3(transform_matrix,
(const float[]){transform->xofs, transform->yofs},
transform->rotation,
apply_rotation ? transform->rotation : 0.0f,
(const float[]){transform->scale_x, transform->scale_y});
const float origin[2] = {imgage_size[0] * transform->origin[0],
imgage_size[1] * transform->origin[1]};
const float pivot[2] = {origin[0] - (imgage_size[0] / 2), origin[1] - (imgage_size[1] / 2)};
const float origin[2] = {image_size[0] * transform->origin[0],
image_size[1] * transform->origin[1]};
const float pivot[2] = {origin[0] - (image_size[0] / 2), origin[1] - (image_size[1] / 2)};
transform_pivot_set_m3(transform_matrix, pivot);
r_quad[0][0] = (imgage_size[0] / 2) - crop->right;
r_quad[0][1] = (imgage_size[1] / 2) - crop->top;
r_quad[1][0] = (imgage_size[0] / 2) - crop->right;
r_quad[1][1] = (-imgage_size[1] / 2) + crop->bottom;
r_quad[2][0] = (-imgage_size[0] / 2) + crop->left;
r_quad[2][1] = (-imgage_size[1] / 2) + crop->bottom;
r_quad[3][0] = (-imgage_size[0] / 2) + crop->left;
r_quad[3][1] = (imgage_size[1] / 2) - crop->top;
r_quad[0][0] = (image_size[0] / 2) - crop->right;
r_quad[0][1] = (image_size[1] / 2) - crop->top;
r_quad[1][0] = (image_size[0] / 2) - crop->right;
r_quad[1][1] = (-image_size[1] / 2) + crop->bottom;
r_quad[2][0] = (-image_size[0] / 2) + crop->left;
r_quad[2][1] = (-image_size[1] / 2) + crop->bottom;
r_quad[3][0] = (-image_size[0] / 2) + crop->left;
r_quad[3][1] = (image_size[1] / 2) - crop->top;
mul_m3_v2(transform_matrix, r_quad[0]);
mul_m3_v2(transform_matrix, r_quad[1]);
@ -521,6 +522,21 @@ void SEQ_image_transform_final_quad_get(const Scene *scene,
mul_v2_v2(r_quad[3], mirror);
}
void SEQ_image_transform_quad_get(const Scene *scene,
const Sequence *seq,
bool apply_rotation,
float r_quad[4][2])
{
seq_image_transform_quad_get_ex(scene, seq, apply_rotation, r_quad);
}
void SEQ_image_transform_final_quad_get(const Scene *scene,
const Sequence *seq,
float r_quad[4][2])
{
seq_image_transform_quad_get_ex(scene, seq, true, r_quad);
}
void SEQ_image_preview_unit_to_px(const Scene *scene, const float co_src[2], float co_dst[2])
{
co_dst[0] = co_src[0] * scene->r.xsch;