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:
committed by
Richard Antalik
parent
509b39f90e
commit
4a11be2656
@@ -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'},
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user