diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 73d0263dfb1..cc31791630e 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -3204,6 +3204,8 @@ def km_sequencer_preview(params): # Transform Actions. *_template_items_transform_actions(params, use_mirror=True), + ("transform.translate", {"type": 'PERIOD', "ctrl": True, "value": 'PRESS'}, + {"properties": [("translate_origin", True)]}), # Edit. ("sequencer.strip_transform_clear", {"type": 'G', "alt": True, "value": 'PRESS'}, diff --git a/scripts/startup/bl_ui/space_sequencer.py b/scripts/startup/bl_ui/space_sequencer.py index 9dea29028c0..33e8f58f46b 100644 --- a/scripts/startup/bl_ui/space_sequencer.py +++ b/scripts/startup/bl_ui/space_sequencer.py @@ -1180,6 +1180,8 @@ class SEQUENCER_MT_image_transform(Menu): layout.operator("transform.translate") layout.operator("transform.rotate") layout.operator("transform.resize", text="Scale") + layout.separator() + layout.operator("transform.translate", text="Move Origin").translate_origin = True class SEQUENCER_MT_image_clear(Menu): diff --git a/source/blender/editors/include/ED_transform.hh b/source/blender/editors/include/ED_transform.hh index 18e61add463..fa455518d96 100644 --- a/source/blender/editors/include/ED_transform.hh +++ b/source/blender/editors/include/ED_transform.hh @@ -125,6 +125,7 @@ int BIF_countTransformOrientation(const bContext *C); #define P_VIEW2D_EDGE_PAN (1 << 17) /* For properties performed when confirming the transformation. */ #define P_POST_TRANSFORM (1 << 18) +#define P_TRANSLATE_ORIGIN (1 << 19) void properties_register(wmOperatorType *ot, int flags); diff --git a/source/blender/editors/transform/transform.hh b/source/blender/editors/transform/transform.hh index 11c5efaf901..b5309dfc58a 100644 --- a/source/blender/editors/transform/transform.hh +++ b/source/blender/editors/transform/transform.hh @@ -184,8 +184,11 @@ enum eTFlag { /** Special flag for when the transform code is called after keys have been duplicated. */ T_DUPLICATED_KEYFRAMES = 1 << 26, + + /** Transform origin. */ + T_ORIGIN = 1 << 27, }; -ENUM_OPERATORS(eTFlag, T_DUPLICATED_KEYFRAMES); +ENUM_OPERATORS(eTFlag, T_ORIGIN); /** #TransInfo.modifiers */ enum eTModifier { diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.cc b/source/blender/editors/transform/transform_convert_sequencer_image.cc index d5b429f506a..75eccac0fb9 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.cc +++ b/source/blender/editors/transform/transform_convert_sequencer_image.cc @@ -11,9 +11,12 @@ #include "DNA_sequence_types.h" #include "DNA_space_types.h" +#include "BLI_array.hh" #include "BLI_math_matrix.h" +#include "BLI_math_matrix.hh" #include "BLI_math_rotation.h" #include "BLI_math_vector.h" +#include "BLI_math_vector.hh" #include "SEQ_channels.hh" #include "SEQ_iterator.hh" @@ -36,9 +39,13 @@ namespace { /** Used for sequencer transform. */ struct TransDataSeq { Strip *strip; - float orig_origin_position[2]; - float orig_translation[2]; - float orig_scale[2]; + Array quad_orig; + float3x3 orig_matrix; + + float2 orig_origin_relative; /* 0-1 range within image bounds. */ + float2 orig_origin_pixelspace; + float2 orig_translation; + float2 orig_scale; float orig_rotation; int orig_flag; float active_seq_orig_rotation; @@ -47,17 +54,38 @@ struct TransDataSeq { } // namespace -static TransData *SeqToTransData(const Scene *scene, - Strip *strip, - TransData *td, - TransData2D *td2d, - TransDataSeq *tdseq, - int vert_index) +static void store_transform_properties(const Scene *scene, + Strip *strip, + float2 origin, + TransData *td) +{ + Editing *ed = seq::editing_get(scene); + const StripTransform *transform = strip->data->transform; + TransDataSeq *tdseq = MEM_new("TransSeq TransDataSeq"); + tdseq->strip = strip; + copy_v2_v2(tdseq->orig_origin_relative, transform->origin); + tdseq->orig_origin_pixelspace = origin; + tdseq->quad_orig = seq::image_transform_final_quad_get(scene, strip); + tdseq->orig_matrix = math::invert(seq::image_transform_matrix_get(scene, strip)); + + tdseq->orig_translation[0] = transform->xofs; + tdseq->orig_translation[1] = transform->yofs; + tdseq->orig_scale[0] = transform->scale_x; + tdseq->orig_scale[1] = transform->scale_y; + tdseq->orig_rotation = transform->rotation; + tdseq->orig_flag = strip->flag; + tdseq->orig_mirror = seq::image_transform_mirror_factor_get(strip); + tdseq->active_seq_orig_rotation = ed->act_strip->data->transform->rotation; + tdseq->strip = strip; + td->extra = static_cast(tdseq); +} + +static TransData *SeqToTransData( + const Scene *scene, Strip *strip, TransData *td, TransData2D *td2d, int vert_index) { const StripTransform *transform = strip->data->transform; const float2 origin = seq::image_transform_origin_offset_pixelspace_get(scene, strip); const float2 mirror = seq::image_transform_mirror_factor_get(strip); - Editing *ed = seq::editing_get(scene); float vertex[2] = {origin[0], origin[1]}; /* Add control vertex, so rotation and scale can be calculated. @@ -87,18 +115,11 @@ static TransData *SeqToTransData(const Scene *scene, axis_angle_to_mat3_single(td->axismtx, 'Z', transform->rotation * mirror[0] * mirror[1]); normalize_m3(td->axismtx); - tdseq->strip = strip; - copy_v2_v2(tdseq->orig_origin_position, origin); - tdseq->orig_translation[0] = transform->xofs; - tdseq->orig_translation[1] = transform->yofs; - tdseq->orig_scale[0] = transform->scale_x; - tdseq->orig_scale[1] = transform->scale_y; - tdseq->orig_rotation = transform->rotation; - tdseq->orig_flag = strip->flag; - tdseq->orig_mirror = mirror; - tdseq->active_seq_orig_rotation = ed->act_strip->data->transform->rotation; + /* Store properties only once per vertex "triad". */ + if (vert_index == 0) { + store_transform_properties(scene, strip, origin, td); + } - td->extra = (void *)tdseq; td->ext = nullptr; td->flag |= TD_SELECTED; td->dist = 0.0; @@ -111,7 +132,10 @@ static void freeSeqData(TransInfo * /*t*/, TransCustomData * /*custom_data*/) { TransData *td = tc->data; - MEM_freeN(td->extra); + for (int i = 0; i < tc->data_len; i += 3) { + TransDataSeq *tdseq = static_cast((td + i)->extra); + MEM_delete(tdseq); + } } static void createTransSeqImageData(bContext * /*C*/, TransInfo *t) @@ -146,15 +170,14 @@ static void createTransSeqImageData(bContext * /*C*/, TransInfo *t) TransData *td = tc->data = MEM_calloc_arrayN(tc->data_len, "TransSeq TransData"); TransData2D *td2d = tc->data_2d = MEM_calloc_arrayN(tc->data_len, "TransSeq TransData2D"); - TransDataSeq *tdseq = MEM_calloc_arrayN(tc->data_len, "TransSeq TransDataSeq"); for (Strip *strip : strips) { /* One `Sequence` needs 3 `TransData` entries - center point placed in image origin, then 2 * points offset by 1 in X and Y direction respectively, so rotation and scale can be * calculated from these points. */ - SeqToTransData(t->scene, strip, td++, td2d++, tdseq++, 0); - SeqToTransData(t->scene, strip, td++, td2d++, tdseq++, 1); - SeqToTransData(t->scene, strip, td++, td2d++, tdseq++, 2); + SeqToTransData(t->scene, strip, td++, td2d++, 0); + SeqToTransData(t->scene, strip, td++, td2d++, 1); + SeqToTransData(t->scene, strip, td++, td2d++, 2); } } @@ -199,7 +222,31 @@ static bool autokeyframe_sequencer_image(bContext *C, return changed; } -static void recalcData_sequencer_image(TransInfo *t) +struct TransformResult { + float2 translation; + float2 scale; + float rotation; +}; + +static TransformResult transform_result_get(TransInfo *t, + TransDataSeq *tdseq, + TransData2D *td2d, + Strip *strip) +{ + float2 handle_origin = {td2d->loc[0], td2d->loc[1]}; + /* X and Y control points used to read scale and rotation. */ + float2 handle_x = float2((td2d + 1)->loc) - handle_origin; + float2 handle_y = float2((td2d + 2)->loc) - handle_origin; + float2 aspect = {t->scene->r.yasp / t->scene->r.xasp, 1.0f}; + float2 mirror = seq::image_transform_mirror_factor_get(strip); + float2 orig_strip_origin_pixelspace = tdseq->orig_origin_pixelspace; + + return TransformResult{(orig_strip_origin_pixelspace - handle_origin) * mirror * aspect, + {math::length(handle_x), math::length(handle_y)}, + t->values_final[0] * mirror[0] * mirror[1]}; +} + +static void image_transform_set(TransInfo *t) { TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); TransData *td = nullptr; @@ -207,53 +254,27 @@ static void recalcData_sequencer_image(TransInfo *t) Editing *ed = seq::editing_get(t->scene); int i; - for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { - /* Origin. */ - 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, origin); - i++; - td++; - td2d++; - - float handle_y[2]; - copy_v2_v2(handle_y, td2d->loc); - sub_v2_v2(handle_y, origin); - + for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i += 3, td += 3, td2d += 3) { TransDataSeq *tdseq = static_cast(td->extra); Strip *strip = tdseq->strip; StripTransform *transform = strip->data->transform; - - /* Calculate translation. */ - float translation[2]; - copy_v2_v2(translation, tdseq->orig_origin_position); - sub_v2_v2(translation, origin); - mul_v2_v2(translation, tdseq->orig_mirror); - translation[0] *= t->scene->r.yasp / t->scene->r.xasp; + TransformResult result = transform_result_get(t, tdseq, td2d, strip); /* Round resulting position to integer pixels. Resulting strip * will more often end up using faster interpolation (without bilinear), * and avoids "text edges are too dark" artifacts with light text strips * on light backgrounds. The latter happens because bilinear filtering * does not do full alpha pre-multiplication. */ - transform->xofs = roundf(tdseq->orig_translation[0] - translation[0]); - transform->yofs = roundf(tdseq->orig_translation[1] - translation[1]); + transform->xofs = roundf(tdseq->orig_translation.x - result.translation.x); + transform->yofs = roundf(tdseq->orig_translation.y - result.translation.y); /* Scale. */ - transform->scale_x = tdseq->orig_scale[0] * fabs(len_v2(handle_x)); - transform->scale_y = tdseq->orig_scale[1] * fabs(len_v2(handle_y)); + transform->scale_x = tdseq->orig_scale.x * result.scale.x; + transform->scale_y = tdseq->orig_scale.x * result.scale.x; /* Rotation. Scaling can cause negative rotation. */ if (t->mode == TFM_ROTATION) { - transform->rotation = tdseq->orig_rotation - - (t->values_final[0] * tdseq->orig_mirror[0] * tdseq->orig_mirror[1]); + transform->rotation = tdseq->orig_rotation - result.rotation; } if (t->mode == TFM_MIRROR) { @@ -290,6 +311,81 @@ static void recalcData_sequencer_image(TransInfo *t) } } +static float2 calculate_translation_offset(TransInfo *t, TransDataSeq *tdseq) +{ + Strip *strip = tdseq->strip; + StripTransform *transform = strip->data->transform; + + /* During modal operation, transform->*ofs is adjusted. Reset this value to original state, so + * that new offset can be calculated. */ + transform->xofs = tdseq->orig_translation[0]; + transform->yofs = tdseq->orig_translation[1]; + + const float2 viewport_pixel_aspect = {t->scene->r.xasp / t->scene->r.yasp, 1.0f}; + float2 mirror = seq::image_transform_mirror_factor_get(strip); + + Array quad_new = seq::image_transform_final_quad_get(t->scene, strip); + return (quad_new[0] - tdseq->quad_orig[0]) * mirror / viewport_pixel_aspect; +} + +static float2 calculate_new_origin_position(TransInfo *t, TransDataSeq *tdseq, TransData2D *td2d) +{ + Strip *strip = tdseq->strip; + + float2 image_size(float(t->scene->r.xsch), float(t->scene->r.ysch)); + if (ELEM(strip->type, STRIP_TYPE_MOVIE, STRIP_TYPE_IMAGE)) { + image_size.x = strip->data->stripdata->orig_width; + image_size.y = strip->data->stripdata->orig_height; + } + + const float2 viewport_pixel_aspect = {t->scene->r.xasp / t->scene->r.yasp, 1.0f}; + float2 mirror = seq::image_transform_mirror_factor_get(strip); + + const float2 origin = tdseq->orig_origin_pixelspace; + const float2 translation = transform_result_get(t, tdseq, td2d, strip).translation; + const float2 origin_pixelspace_unscaled = origin / viewport_pixel_aspect * mirror; + const float2 origin_translated = origin_pixelspace_unscaled - translation; + const float2 origin_raw_space = math::transform_point(tdseq->orig_matrix, origin_translated); + const float2 origin_abs = origin_raw_space + image_size / 2; + const float2 origin_rel = origin_abs / image_size; + return origin_rel; +} + +static void image_origin_set(TransInfo *t) +{ + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + TransData *td = nullptr; + TransData2D *td2d = nullptr; + int i; + + for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i += 3, td += 3, td2d += 3) { + TransDataSeq *tdseq = static_cast(td->extra); + Strip *strip = tdseq->strip; + StripTransform *transform = strip->data->transform; + + const float2 origin_rel = calculate_new_origin_position(t, tdseq, td2d); + transform->origin[0] = origin_rel.x; + transform->origin[1] = origin_rel.y; + + /* Calculate offset, so image does not change it's position in preview. */ + float2 delta_translation = calculate_translation_offset(t, tdseq); + transform->xofs = tdseq->orig_translation.x - delta_translation.x; + transform->yofs = tdseq->orig_translation.y - delta_translation.y; + + seq::relations_invalidate_cache_preprocessed(t->scene, strip); + } +} + +static void recalcData_sequencer_image(TransInfo *t) +{ + if ((t->flag & T_ORIGIN) == 0) { + image_transform_set(t); + } + else { + image_origin_set(t); + } +} + static void special_aftertrans_update__sequencer_image(bContext * /*C*/, TransInfo *t) { @@ -298,20 +394,19 @@ static void special_aftertrans_update__sequencer_image(bContext * /*C*/, TransIn TransData2D *td2d = nullptr; int i; - for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { + for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i += 3, td += 3, td2d += 3) { TransDataSeq *tdseq = static_cast(td->extra); Strip *strip = tdseq->strip; StripTransform *transform = strip->data->transform; if (t->state == TRANS_CANCEL) { - if (t->mode == TFM_ROTATION) { - transform->rotation = tdseq->orig_rotation; - } - if (t->mode == TFM_MIRROR) { - transform->xofs = tdseq->orig_translation[0]; - transform->yofs = tdseq->orig_translation[1]; - transform->rotation = tdseq->orig_rotation; - strip->flag = tdseq->orig_flag; - } + transform->xofs = tdseq->orig_translation.x; + transform->yofs = tdseq->orig_translation.y; + transform->rotation = tdseq->orig_rotation; + transform->scale_x = tdseq->orig_scale.x; + transform->scale_y = tdseq->orig_scale.y; + transform->origin[0] = tdseq->orig_origin_relative.x; + transform->origin[1] = tdseq->orig_origin_relative.y; + strip->flag = tdseq->orig_flag; continue; } diff --git a/source/blender/editors/transform/transform_generics.cc b/source/blender/editors/transform/transform_generics.cc index 645f3c2acb5..fd2e1d1e40b 100644 --- a/source/blender/editors/transform/transform_generics.cc +++ b/source/blender/editors/transform/transform_generics.cc @@ -699,6 +699,16 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->vod = ED_view3d_navigation_init(C, kmi_passthrough); } + if (t->mode == TFM_TRANSLATION) { + if ((prop = RNA_struct_find_property(op->ptr, "translate_origin")) && + RNA_property_is_set(op->ptr, prop)) + { + if (RNA_property_boolean_get(op->ptr, prop)) { + t->flag |= T_ORIGIN; + } + } + } + setTransformViewMatrices(t); calculateCenter2D(t); calculateCenterLocal(t, t->center_global); diff --git a/source/blender/editors/transform/transform_ops.cc b/source/blender/editors/transform/transform_ops.cc index c92a3b4bf73..1d2b9820585 100644 --- a/source/blender/editors/transform/transform_ops.cc +++ b/source/blender/editors/transform/transform_ops.cc @@ -844,6 +844,15 @@ void properties_register(wmOperatorType *ot, int flags) "Forces the use of Auto Merge and Split"); RNA_def_property_flag(prop, PROP_HIDDEN); } + + if (flags & P_TRANSLATE_ORIGIN) { + prop = RNA_def_boolean(ot->srna, + "translate_origin", + false, + "Translate Origin", + "Translate origin instead of selection"); + RNA_def_property_flag(prop, PROP_HIDDEN); + } } static void TRANSFORM_OT_translate(wmOperatorType *ot) @@ -870,7 +879,7 @@ static void TRANSFORM_OT_translate(wmOperatorType *ot) properties_register(ot, P_ORIENT_MATRIX | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_OPTIONS | P_GPENCIL_EDIT | P_CURSOR_EDIT | P_VIEW2D_EDGE_PAN | - P_POST_TRANSFORM); + P_POST_TRANSFORM | P_TRANSLATE_ORIGIN); } static void TRANSFORM_OT_resize(wmOperatorType *ot) diff --git a/source/blender/editors/transform/transform_snap_sequencer.cc b/source/blender/editors/transform/transform_snap_sequencer.cc index 1d27a4f4469..b2906303b43 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.cc +++ b/source/blender/editors/transform/transform_snap_sequencer.cc @@ -13,6 +13,8 @@ #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_math_base.h" +#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" #include "BLI_vector.hh" #include "MEM_guardedalloc.h" @@ -131,15 +133,15 @@ static void points_build_sources_timeline_retiming( cmp_fn); } -static void points_build_sources_preview_strips(const Scene *scene, - TransSeqSnapData *snap_data, - const Span snap_sources) +static void points_build_sources_preview_image(const Scene *scene, + TransSeqSnapData *snap_data, + const Span snap_sources) { for (Strip *strip : snap_sources) { - const Array seq_image_quad = seq::image_transform_final_quad_get(scene, strip); + const Array strip_image_quad = seq::image_transform_final_quad_get(scene, strip); - for (int j = 0; j < 4; j++) { - snap_data->source_snap_points.append(seq_image_quad[j]); + for (int i = 0; i < 4; i++) { + snap_data->source_snap_points.append(strip_image_quad[i]); } /* Add origins last */ @@ -148,6 +150,30 @@ static void points_build_sources_preview_strips(const Scene *scene, } } +static void points_build_sources_preview_origin(const Scene *scene, + TransSeqSnapData *snap_data, + const Span snap_sources) +{ + + const size_t point_count_source = snap_sources.size(); + + if (point_count_source == 0) { + return; + } + + snap_data->source_snap_points.reinitialize(point_count_source); + int i = 0; + for (Strip *strip : snap_sources) { + /* Add origins last */ + float2 image_origin = seq::image_transform_origin_offset_pixelspace_get(scene, strip); + snap_data->source_snap_points[i][0] = image_origin[0]; + snap_data->source_snap_points[i][1] = image_origin[1]; + i++; + } + + BLI_assert(i <= snap_data->source_snap_points.size()); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -211,10 +237,11 @@ static VectorSet query_snap_targets_timeline(Scene *scene, return snap_targets; } -static VectorSet query_snap_targets_preview(Scene *scene, - const short snap_mode, - const bool exclude_selected) +static VectorSet query_snap_targets_preview(const TransInfo *t) { + Scene *scene = t->scene; + short snap_mode = t->tsnap.mode; + VectorSet snap_targets; /* We don't need to calculate strip snap targets if the option is unselected. */ @@ -226,8 +253,10 @@ static VectorSet query_snap_targets_preview(Scene *scene, ListBase *channels = seq::channels_displayed_get(ed); snap_targets = seq::query_rendered_strips(scene, channels, ed->seqbasep, scene->r.cfra, 0); - if (exclude_selected) { - snap_targets.remove_if([&](Strip *strip) { return (strip->flag & SELECT) == 1; }); + + /* Selected strips are only valid targets when snapping the cursor or origin. */ + if ((t->data_type == &TransConvertType_SequencerImage) && (t->flag & T_ORIGIN) == 0) { + snap_targets.remove_if([&](Strip *strip) { return (strip->flag & SELECT) != 0; }); } return snap_targets; @@ -310,27 +339,36 @@ static void points_build_targets_timeline(const Scene *scene, cmp_fn); } -static void points_build_targets_preview(const Scene *scene, - const View2D *v2d, - const short snap_mode, - TransSeqSnapData *snap_data, - const Span snap_targets) +static void points_build_targets_preview_general(const View2D *v2d, + const short snap_mode, + TransSeqSnapData *snap_data) { if (snap_mode & SEQ_SNAP_TO_PREVIEW_BORDERS) { snap_data->target_snap_points.append(float2(v2d->tot.xmin, v2d->tot.ymin)); snap_data->target_snap_points.append(float2(v2d->tot.xmax, v2d->tot.ymax)); + snap_data->target_snap_points.append(float2(v2d->tot.xmin, v2d->tot.ymax)); + snap_data->target_snap_points.append(float2(v2d->tot.xmax, v2d->tot.ymin)); } if (snap_mode & SEQ_SNAP_TO_PREVIEW_CENTER) { - snap_data->target_snap_points.append(float2(0.0f)); + snap_data->target_snap_points.append(float2(0)); } +} + +static void points_build_targets_preview_image(const Scene *scene, + const View2D *v2d, + const short snap_mode, + TransSeqSnapData *snap_data, + const Span snap_targets) +{ + points_build_targets_preview_general(v2d, snap_mode, snap_data); if (snap_mode & SEQ_SNAP_TO_STRIPS_PREVIEW) { for (Strip *strip : snap_targets) { const Array strip_image_quad = seq::image_transform_final_quad_get(scene, strip); - for (int j = 0; j < 4; j++) { - snap_data->target_snap_points.append(strip_image_quad[j]); + for (int i = 0; i < 4; i++) { + snap_data->target_snap_points.append(strip_image_quad[i]); } const float2 image_origin = seq::image_transform_origin_offset_pixelspace_get(scene, strip); @@ -339,6 +377,42 @@ static void points_build_targets_preview(const Scene *scene, } } +static void points_build_3x3_grid(const Scene *scene, TransSeqSnapData *snap_data, Strip *strip) +{ + const Array strip_image_quad = seq::image_transform_final_quad_get(scene, strip); + /* Corners. */ + for (int i = 0; i < 4; i++) { + snap_data->target_snap_points.append(strip_image_quad[i]); + } + + /* Middle top, bottom and center of the image. */ + const float2 tm = blender::math::interpolate(strip_image_quad[0], strip_image_quad[3], 0.5f); + const float2 bm = blender::math::interpolate(strip_image_quad[1], strip_image_quad[2], 0.5f); + const float2 mm = blender::math::interpolate(bm, tm, 0.5f); + snap_data->target_snap_points.append(tm); + snap_data->target_snap_points.append(mm); + snap_data->target_snap_points.append(bm); + /* Left and right. */ + snap_data->target_snap_points.append( + blender::math::interpolate(strip_image_quad[2], strip_image_quad[3], 0.5f)); + snap_data->target_snap_points.append( + blender::math::interpolate(strip_image_quad[0], strip_image_quad[1], 0.5f)); +} + +static void points_build_targets_preview_origin(const Scene *scene, + TransSeqSnapData *snap_data, + const Span snap_sources, + const Span snap_targets) +{ + for (Strip *strip : snap_sources) { + points_build_3x3_grid(scene, snap_data, strip); + } + + for (Strip *strip : snap_targets) { + points_build_3x3_grid(scene, snap_data, strip); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -392,19 +466,24 @@ static void snap_data_build_preview(const TransInfo *t, TransSeqSnapData *snap_d VectorSet snap_sources = query_snap_sources_preview(scene); VectorSet snap_targets; + snap_targets = query_snap_targets_preview(t); + /* Build arrays of snap points. */ if (t->data_type == &TransConvertType_SequencerImage) { - /* Ignore selected strips if we are not snapping the cursor, - * since they move with the transform. */ - snap_targets = query_snap_targets_preview(scene, snap_mode, true); - points_build_sources_preview_strips(scene, snap_data, snap_sources); + if (t->flag & T_ORIGIN) { + points_build_sources_preview_origin(scene, snap_data, snap_sources); + points_build_targets_preview_origin(scene, snap_data, snap_sources, snap_targets); + } + else { + points_build_sources_preview_image(scene, snap_data, snap_sources); + points_build_targets_preview_image(scene, v2d, snap_mode, snap_data, snap_targets); + } } else if (t->data_type == &TransConvertType_CursorSequencer) { - snap_targets = query_snap_targets_preview(scene, snap_mode, false); float2 cursor_view = float2(sseq->cursor) * float2(t->aspect); snap_data->source_snap_points.append(cursor_view); + points_build_targets_preview_image(scene, v2d, snap_mode, snap_data, snap_targets); } - points_build_targets_preview(scene, v2d, snap_mode, snap_data, snap_targets); } TransSeqSnapData *snap_sequencer_data_alloc(const TransInfo *t) @@ -470,15 +549,47 @@ static bool snap_calc_timeline(TransInfo *t, const TransSeqSnapData *snap_data) return true; } -static bool snap_calc_preview(TransInfo *t, const TransSeqSnapData *snap_data) +static bool snap_calc_preview_origin(TransInfo *t, const TransSeqSnapData *snap_data) +{ + /* Store best snap candidates in x and y directions separately. */ + float best_dist(std::numeric_limits::max()); + float2 best_target_point(0.0f); + float2 best_source_point(0.0f); + + for (const float2 snap_source_point : snap_data->source_snap_points) { + for (const float2 snap_target_point : snap_data->target_snap_points) { + /* First update snaps in x direction, then y direction. */ + const float2 transformed_point(snap_source_point.x + t->values[0], + snap_source_point.y + t->values[1]); + const float dist = blender::math::distance(snap_target_point, transformed_point); + if (dist > best_dist) { + continue; + } + + best_dist = dist; + best_target_point = snap_target_point; + best_source_point = snap_source_point; + } + } + + if (best_dist <= seq_snap_threshold_get_view_distance(t)) { + copy_v2_v2(t->tsnap.snap_target, best_target_point); + copy_v2_v2(t->tsnap.snap_source, best_source_point); + t->tsnap.direction |= DIR_GLOBAL_X | DIR_GLOBAL_Y; + return true; + } + return false; +} + +static bool snap_calc_preview_image(TransInfo *t, const TransSeqSnapData *snap_data) { /* Store best snap candidates in x and y directions separately. */ float2 best_dist(std::numeric_limits::max()); float2 best_target_point(0.0f); float2 best_source_point(0.0f); - for (const float *snap_source_point : snap_data->source_snap_points) { - for (const float *snap_target_point : snap_data->target_snap_points) { + for (const float2 snap_source_point : snap_data->source_snap_points) { + for (const float2 snap_target_point : snap_data->target_snap_points) { /* First update snaps in x direction, then y direction. */ for (int i = 0; i < 2; i++) { int dist = abs(snap_target_point[i] - (snap_source_point[i] + t->values[i])); @@ -521,7 +632,10 @@ bool snap_sequencer_calc(TransInfo *t) if (ELEM(t->data_type, &TransConvertType_Sequencer, &TransConvertType_SequencerRetiming)) { return snap_calc_timeline(t, snap_data); } - return snap_calc_preview(t, snap_data); + if (t->flag & T_ORIGIN) { + return snap_calc_preview_origin(t, snap_data); + } + return snap_calc_preview_image(t, snap_data); } void snap_sequencer_apply_seqslide(TransInfo *t, float *vec) diff --git a/source/blender/sequencer/SEQ_transform.hh b/source/blender/sequencer/SEQ_transform.hh index 21717906c34..4d02a9fc44b 100644 --- a/source/blender/sequencer/SEQ_transform.hh +++ b/source/blender/sequencer/SEQ_transform.hh @@ -103,6 +103,10 @@ blender::Array image_transform_quad_get(const Scene *scene, bool apply_rotation); /** * Get 4 corner points of strip image. Corner vectors are in viewport space. + * Indices correspond to following corners (assuming no rotation): + * 3--0 + * | | + * 2--1 * * \param scene: Scene in which strips are located * \param strip: Strip to calculate transformed image quad