VSE: Add option to translate pivot point

This feature allows you to change postion of origin/pivot for images
without changing their position.

It is implemented as property of transform operator. It is activated
by pressing `Ctrl + .` shortcut.
Move Origin item was also added to transform menu.

Origin can be snapped to 3x3 grid on strip image. This represents
most usual anchor points.

Ref: #134251
Pull Request: https://projects.blender.org/blender/blender/pulls/134206
This commit is contained in:
Richard Antalik
2025-05-06 05:16:56 +02:00
committed by Richard Antalik
parent 509b39f90e
commit 4a11be2656
9 changed files with 341 additions and 101 deletions

View File

@@ -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'},

View File

@@ -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):

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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<float2> 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<TransDataSeq>("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<void *>(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<TransDataSeq *>((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<TransData>(tc->data_len, "TransSeq TransData");
TransData2D *td2d = tc->data_2d = MEM_calloc_arrayN<TransData2D>(tc->data_len,
"TransSeq TransData2D");
TransDataSeq *tdseq = MEM_calloc_arrayN<TransDataSeq>(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<TransDataSeq *>(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<float2> 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<TransDataSeq *>(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<TransDataSeq *>(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;
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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<Strip *> snap_sources)
static void points_build_sources_preview_image(const Scene *scene,
TransSeqSnapData *snap_data,
const Span<Strip *> snap_sources)
{
for (Strip *strip : snap_sources) {
const Array<float2> seq_image_quad = seq::image_transform_final_quad_get(scene, strip);
const Array<float2> 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<Strip *> 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<Strip *> query_snap_targets_timeline(Scene *scene,
return snap_targets;
}
static VectorSet<Strip *> query_snap_targets_preview(Scene *scene,
const short snap_mode,
const bool exclude_selected)
static VectorSet<Strip *> query_snap_targets_preview(const TransInfo *t)
{
Scene *scene = t->scene;
short snap_mode = t->tsnap.mode;
VectorSet<Strip *> snap_targets;
/* We don't need to calculate strip snap targets if the option is unselected. */
@@ -226,8 +253,10 @@ static VectorSet<Strip *> 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<Strip *> 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<Strip *> 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<float2> 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<float2> 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<Strip *> snap_sources,
const Span<Strip *> 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<Strip *> snap_sources = query_snap_sources_preview(scene);
VectorSet<Strip *> 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<float>::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<float>::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)

View File

@@ -103,6 +103,10 @@ blender::Array<blender::float2> 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