tornavis/source/blender/editors/transform/transform_convert_sequencer.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

729 lines
22 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edtransform
*/
#include "DNA_space_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "BKE_context.hh"
#include "BKE_main.h"
#include "BKE_report.h"
#include "ED_markers.hh"
#include "ED_time_scrub_ui.hh"
#include "SEQ_animation.hh"
#include "SEQ_channels.hh"
#include "SEQ_edit.hh"
#include "SEQ_effects.hh"
#include "SEQ_iterator.hh"
#include "SEQ_relations.hh"
#include "SEQ_sequencer.hh"
#include "SEQ_time.hh"
#include "SEQ_transform.hh"
#include "SEQ_utils.hh"
#include "UI_view2d.hh"
#include "transform.hh"
#include "transform_convert.hh"
#define SEQ_EDGE_PAN_INSIDE_PAD 3.5
#define SEQ_EDGE_PAN_OUTSIDE_PAD 0 /* Disable clamping for panning, use whole screen. */
#define SEQ_EDGE_PAN_SPEED_RAMP 1
#define SEQ_EDGE_PAN_MAX_SPEED 4 /* In UI units per second, slower than default. */
#define SEQ_EDGE_PAN_DELAY 1.0f
#define SEQ_EDGE_PAN_ZOOM_INFLUENCE 0.5f
/** Used for sequencer transform. */
struct TransDataSeq {
Sequence *seq;
/** A copy of #Sequence.flag that may be modified for nested strips. */
int flag;
/** Use this so we can have transform data at the strips start,
* but apply correctly to the start frame. */
int start_offset;
/** one of #SELECT, #SEQ_LEFTSEL and #SEQ_RIGHTSEL. */
short sel_flag;
};
/**
* Sequencer transform customdata (stored in #TransCustomDataContainer).
*/
struct TransSeq {
TransDataSeq *tdseq;
int selection_channel_range_min;
int selection_channel_range_max;
/* Initial rect of the view2d, used for computing offset during edge panning */
rctf initial_v2d_cur;
View2DEdgePanData edge_pan;
/* Strips that aren't selected, but their position entirely depends on transformed strips. */
blender::VectorSet<Sequence *> time_dependent_strips;
};
/* -------------------------------------------------------------------- */
/** \name Sequencer Transform Creation
* \{ */
/* This function applies the rules for transforming a strip so duplicate
* checks don't need to be added in multiple places.
*
* count and flag MUST be set.
*/
static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_count, int *r_flag)
{
Scene *scene = t->scene;
Editing *ed = SEQ_editing_get(t->scene);
ListBase *channels = SEQ_channels_displayed_get(ed);
/* for extend we need to do some tricks */
if (t->mode == TFM_TIME_EXTEND) {
/* *** Extend Transform *** */
int cfra = scene->r.cfra;
int left = SEQ_time_left_handle_frame_get(scene, seq);
int right = SEQ_time_right_handle_frame_get(scene, seq);
if ((seq->flag & SELECT) == 0 || SEQ_transform_is_locked(channels, seq)) {
*r_count = 0;
*r_flag = 0;
}
else {
*r_count = 1; /* unless its set to 0, extend will never set 2 handles at once */
*r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
if (t->frame_side == 'R') {
if (right <= cfra) {
*r_count = *r_flag = 0;
} /* ignore */
else if (left > cfra) {
} /* keep the selection */
else {
*r_flag |= SEQ_RIGHTSEL;
}
}
else {
if (left >= cfra) {
*r_count = *r_flag = 0;
} /* ignore */
else if (right < cfra) {
} /* keep the selection */
else {
*r_flag |= SEQ_LEFTSEL;
}
}
}
}
else {
t->frame_side = 'B';
/* *** Normal Transform *** */
/* Count */
2023-07-15 07:45:06 +02:00
/* Non nested strips (reset selection and handles). */
if ((seq->flag & SELECT) == 0 || SEQ_transform_is_locked(channels, seq)) {
*r_count = 0;
*r_flag = 0;
}
else {
if ((seq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == (SEQ_LEFTSEL | SEQ_RIGHTSEL)) {
*r_flag = seq->flag;
*r_count = 2; /* we need 2 transdata's */
}
else {
*r_flag = seq->flag;
*r_count = 1; /* selected or with a handle selected */
}
}
}
}
static int SeqTransCount(TransInfo *t, ListBase *seqbase)
{
int tot = 0, count, flag;
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
SeqTransInfo(t, seq, &count, &flag); /* ignore the flag */
tot += count;
}
return tot;
}
static TransData *SeqToTransData(Scene *scene,
TransData *td,
TransData2D *td2d,
TransDataSeq *tdsq,
Sequence *seq,
int flag,
int sel_flag)
{
int start_left;
switch (sel_flag) {
case SELECT:
/* Use seq_tx_get_final_left() and an offset here
* so transform has the left hand location of the strip.
* tdsq->start_offset is used when flushing the tx data back */
start_left = SEQ_time_left_handle_frame_get(scene, seq);
td2d->loc[0] = start_left;
tdsq->start_offset = start_left - seq->start; /* use to apply the original location */
break;
case SEQ_LEFTSEL:
start_left = SEQ_time_left_handle_frame_get(scene, seq);
td2d->loc[0] = start_left;
break;
case SEQ_RIGHTSEL:
td2d->loc[0] = SEQ_time_right_handle_frame_get(scene, seq);
break;
}
td2d->loc[1] = seq->machine; /* channel - Y location */
td2d->loc[2] = 0.0f;
td2d->loc2d = nullptr;
tdsq->seq = seq;
/* Use instead of seq->flag for nested strips and other
* cases where the selection may need to be modified */
tdsq->flag = flag;
tdsq->sel_flag = sel_flag;
td->extra = (void *)tdsq; /* allow us to update the strip from here */
td->flag = 0;
td->loc = td2d->loc;
copy_v3_v3(td->center, td->loc);
copy_v3_v3(td->iloc, td->loc);
memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
td->ext = nullptr;
td->val = nullptr;
td->flag |= TD_SELECTED;
td->dist = 0.0;
unit_m3(td->mtx);
unit_m3(td->smtx);
/* Time Transform (extend) */
td->val = td2d->loc;
td->ival = td2d->loc[0];
return td;
}
static int SeqToTransData_build(
TransInfo *t, ListBase *seqbase, TransData *td, TransData2D *td2d, TransDataSeq *tdsq)
{
Scene *scene = t->scene;
int count, flag;
int tot = 0;
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
SeqTransInfo(t, seq, &count, &flag);
/* use 'flag' which is derived from seq->flag but modified for special cases */
if (flag & SELECT) {
if (flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) {
if (flag & SEQ_LEFTSEL) {
SeqToTransData(scene, td++, td2d++, tdsq++, seq, flag, SEQ_LEFTSEL);
tot++;
}
if (flag & SEQ_RIGHTSEL) {
SeqToTransData(scene, td++, td2d++, tdsq++, seq, flag, SEQ_RIGHTSEL);
tot++;
}
}
else {
SeqToTransData(scene, td++, td2d++, tdsq++, seq, flag, SELECT);
tot++;
}
}
}
return tot;
}
static void free_transform_custom_data(TransCustomData *custom_data)
{
if ((custom_data->data != nullptr) && custom_data->use_free) {
TransSeq *ts = static_cast<TransSeq *>(custom_data->data);
MEM_freeN(ts->tdseq);
MEM_delete(ts);
custom_data->data = nullptr;
}
}
/* Canceled, need to update the strips display. */
static void seq_transform_cancel(TransInfo *t, blender::Span<Sequence *> transformed_strips)
{
ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene));
for (Sequence *seq : transformed_strips) {
/* Handle pre-existing overlapping strips even when operator is canceled.
* This is necessary for SEQUENCER_OT_duplicate_move macro for example. */
if (SEQ_transform_test_overlap(t->scene, seqbase, seq)) {
SEQ_transform_seqbase_shuffle(seqbase, seq, t->scene);
}
}
}
static ListBase *seqbase_active_get(const TransInfo *t)
{
Editing *ed = SEQ_editing_get(t->scene);
return SEQ_active_seqbase_get(ed);
}
static bool seq_transform_check_overlap(blender::Span<Sequence *> transformed_strips)
{
for (Sequence *seq : transformed_strips) {
if (seq->flag & SEQ_OVERLAP) {
return true;
}
}
return false;
}
static blender::VectorSet<Sequence *> seq_transform_collection_from_transdata(
TransDataContainer *tc)
{
blender::VectorSet<Sequence *> strips;
TransData *td = tc->data;
for (int a = 0; a < tc->data_len; a++, td++) {
Sequence *seq = ((TransDataSeq *)td->extra)->seq;
strips.add(seq);
}
return strips;
}
static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
{
Editing *ed = SEQ_editing_get(t->scene);
if (ed == nullptr) {
free_transform_custom_data(custom_data);
return;
}
blender::VectorSet transformed_strips = seq_transform_collection_from_transdata(tc);
SEQ_iterator_set_expand(
t->scene, seqbase_active_get(t), transformed_strips, SEQ_query_strip_effect_chain);
for (Sequence *seq : transformed_strips) {
seq->flag &= ~SEQ_IGNORE_CHANNEL_LOCK;
}
if (t->state == TRANS_CANCEL) {
seq_transform_cancel(t, transformed_strips);
free_transform_custom_data(custom_data);
return;
}
TransSeq *ts = static_cast<TransSeq *>(tc->custom.type.data);
ListBase *seqbasep = seqbase_active_get(t);
Scene *scene = t->scene;
const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag &
SEQ_MARKER_TRANS) != 0;
if (seq_transform_check_overlap(transformed_strips)) {
SEQ_transform_handle_overlap(
scene, seqbasep, transformed_strips, ts->time_dependent_strips, use_sync_markers);
}
DEG_id_tag_update(&t->scene->id, ID_RECALC_SEQUENCER_STRIPS);
free_transform_custom_data(custom_data);
}
static blender::VectorSet<Sequence *> query_selected_strips_no_handles(ListBase *seqbase)
{
blender::VectorSet<Sequence *> strips;
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
if ((seq->flag & SELECT) != 0 && ((seq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == 0)) {
strips.add(seq);
}
}
return strips;
}
enum SeqInputSide {
SEQ_INPUT_LEFT = -1,
SEQ_INPUT_RIGHT = 1,
};
static Sequence *effect_input_get(const Scene *scene, Sequence *effect, SeqInputSide side)
{
Sequence *input = effect->seq1;
if (effect->seq2 && (SEQ_time_left_handle_frame_get(scene, effect->seq2) -
SEQ_time_left_handle_frame_get(scene, effect->seq1)) *
VSE: Make time operations self-contained This patch makes it possible to manipulate strips without need to use update functions to recalculate effect and meta strips. Prior to this change function `SEQ_time_update_sequence` had to be used to update mainly effects and meta strips. This was implemented in a way that relied on sorted list of strips, which can't always be done and in rare cases this approach failed. In case of meta strips, `seqbase` had to be passed and compared with "active" one to determine whether meta strip should be updated or not. This is especially weak system that is prone to bugs when functions are used by python API functions. Finally, other strip types had startdisp` and `enddisp` fields updated by this function and a lot of code relied on these fields even if strip start, length and offsets are available. This is completely unnecessary. Implemented changes: All effects and meta strips are updated when strip handles are moved or strip is translated, without need to call any update function. Function `SEQ_time_update_sequence` has been split to `SEQ_time_update_meta_strip_range` and `seq_time_update_effects_strip_range`. These functions should be only used within sequencer module code. Meta update is used for versioning, which is only reason for it not being declared internally. Sequence fields `startdisp` and `enddisp` are now only used for effects to store strip start and end points. These fields should be used only internally within sequencer module code. Use function `SEQ_time_*_handle_frame_get` to get strip start and end points. To update effects and meta strips with reasonable performance, cache for "parent" meta strip and attached effects is added to `SequenceLookup` cache, so it shares invalidation mechanisms. All caches are populated during single iteration. There should be no functional changes. Differential Revision: https://developer.blender.org/D14990
2022-06-02 01:39:40 +02:00
side >
0)
{
input = effect->seq2;
}
return input;
}
static Sequence *effect_base_input_get(const Scene *scene, Sequence *effect, SeqInputSide side)
{
Sequence *input = effect, *seq_iter = effect;
while (seq_iter != nullptr) {
input = seq_iter;
seq_iter = effect_input_get(scene, seq_iter, side);
}
return input;
}
/**
* Strips that aren't stime_dependent_stripselected, but their position entirely depends on
* transformed strips. This collection is used to offset animation.
*/
static void query_time_dependent_strips_strips(
TransInfo *t, blender::VectorSet<Sequence *> &time_dependent_strips)
{
ListBase *seqbase = seqbase_active_get(t);
/* Query dependent strips where used strips do not have handles selected.
* If all inputs of any effect even indirectly(through another effect) points to selected strip,
2023-06-13 20:51:49 +02:00
* its position will change. */
blender::VectorSet<Sequence *> strips_no_handles = query_selected_strips_no_handles(seqbase);
time_dependent_strips.add_multiple(strips_no_handles);
SEQ_iterator_set_expand(t->scene, seqbase, strips_no_handles, SEQ_query_strip_effect_chain);
bool strip_added = true;
while (strip_added) {
strip_added = false;
for (Sequence *seq : strips_no_handles) {
if (time_dependent_strips.contains(seq)) {
continue; /* Strip is already in collection, skip it. */
}
/* If both seq1 and seq2 exist, both must be selected. */
if (seq->seq1 && time_dependent_strips.contains(seq->seq1)) {
if (seq->seq2 && !time_dependent_strips.contains(seq->seq2)) {
continue;
}
strip_added = true;
time_dependent_strips.add(seq);
}
}
}
/* Query dependent strips where used strips do have handles selected.
* If any 2-input effect changes position because handles were moved, animation should be offset.
* With single input effect, it is less likely desirable to move animation. */
blender::VectorSet selected_strips = SEQ_query_selected_strips(seqbase);
SEQ_iterator_set_expand(t->scene, seqbase, selected_strips, SEQ_query_strip_effect_chain);
for (Sequence *seq : selected_strips) {
/* Check only 2 input effects. */
if (seq->seq1 == nullptr || seq->seq2 == nullptr) {
continue;
}
/* Find immediate base inputs(left and right side). */
Sequence *input_left = effect_base_input_get(t->scene, seq, SEQ_INPUT_LEFT);
Sequence *input_right = effect_base_input_get(t->scene, seq, SEQ_INPUT_RIGHT);
if ((input_left->flag & SEQ_RIGHTSEL) != 0 && (input_right->flag & SEQ_LEFTSEL) != 0) {
time_dependent_strips.add(seq);
}
}
/* Remove all non-effects. */
time_dependent_strips.remove_if(
[&](Sequence *seq) { return SEQ_transform_sequence_can_be_translated(seq); });
}
static void createTransSeqData(bContext * /*C*/, TransInfo *t)
{
Scene *scene = t->scene;
Editing *ed = SEQ_editing_get(t->scene);
TransData *td = nullptr;
TransData2D *td2d = nullptr;
TransDataSeq *tdsq = nullptr;
TransSeq *ts = nullptr;
int count = 0;
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
if (ed == nullptr) {
tc->data_len = 0;
return;
}
/* Disable cursor wrapping for edge pan. */
if (t->mode == TFM_TRANSLATION) {
t->flag |= T_NO_CURSOR_WRAP;
}
tc->custom.type.free_cb = freeSeqData;
t->frame_side = transform_convert_frame_side_dir_get(t, float(scene->r.cfra));
count = SeqTransCount(t, ed->seqbasep);
/* allocate memory for data */
tc->data_len = count;
/* stop if trying to build list if nothing selected */
if (count == 0) {
return;
}
tc->custom.type.data = ts = MEM_new<TransSeq>(__func__);
tc->custom.type.use_free = true;
td = tc->data = static_cast<TransData *>(
MEM_callocN(tc->data_len * sizeof(TransData), "TransSeq TransData"));
td2d = tc->data_2d = static_cast<TransData2D *>(
MEM_callocN(tc->data_len * sizeof(TransData2D), "TransSeq TransData2D"));
ts->tdseq = tdsq = static_cast<TransDataSeq *>(
MEM_callocN(tc->data_len * sizeof(TransDataSeq), "TransSeq TransDataSeq"));
/* Custom data to enable edge panning during transformation. */
UI_view2d_edge_pan_init(t->context,
&ts->edge_pan,
SEQ_EDGE_PAN_INSIDE_PAD,
SEQ_EDGE_PAN_OUTSIDE_PAD,
SEQ_EDGE_PAN_SPEED_RAMP,
SEQ_EDGE_PAN_MAX_SPEED,
SEQ_EDGE_PAN_DELAY,
SEQ_EDGE_PAN_ZOOM_INFLUENCE);
UI_view2d_edge_pan_set_limits(&ts->edge_pan, -FLT_MAX, FLT_MAX, 1, MAXSEQ + 1);
ts->initial_v2d_cur = t->region->v2d.cur;
/* loop 2: build transdata array */
SeqToTransData_build(t, ed->seqbasep, td, td2d, tdsq);
ts->selection_channel_range_min = MAXSEQ + 1;
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if ((seq->flag & SELECT) != 0) {
ts->selection_channel_range_min = min_ii(ts->selection_channel_range_min, seq->machine);
ts->selection_channel_range_max = max_ii(ts->selection_channel_range_max, seq->machine);
}
}
query_time_dependent_strips_strips(t, ts->time_dependent_strips);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name UVs Transform Flush
* \{ */
static void view2d_edge_pan_loc_compensate(TransInfo *t, float loc_in[2], float r_loc[2])
{
TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
/* Initial and current view2D rects for additional transform due to view panning and zooming */
const rctf *rect_src = &ts->initial_v2d_cur;
const rctf *rect_dst = &t->region->v2d.cur;
if (t->options & CTX_VIEW2D_EDGE_PAN) {
if (t->state == TRANS_CANCEL) {
UI_view2d_edge_pan_cancel(t->context, &ts->edge_pan);
}
else {
/* Edge panning functions expect window coordinates, mval is relative to region */
const int xy[2] = {
t->region->winrct.xmin + int(t->mval[0]),
t->region->winrct.ymin + int(t->mval[1]),
};
UI_view2d_edge_pan_apply(t->context, &ts->edge_pan, xy);
}
}
copy_v2_v2(r_loc, loc_in);
/* Additional offset due to change in view2D rect. */
BLI_rctf_transform_pt_v(rect_dst, rect_src, r_loc, r_loc);
}
static void flushTransSeq(TransInfo *t)
{
/* Editing null check already done */
ListBase *seqbasep = seqbase_active_get(t);
int a, new_frame, offset;
TransData *td = nullptr;
TransData2D *td2d = nullptr;
TransDataSeq *tdsq = nullptr;
Sequence *seq;
Scene *scene = t->scene;
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* This is calculated for offsetting animation of effects that change position with inputs.
* Maximum(positive or negative) value is used, because individual strips can be clamped. This
* works fairly well in most scenarios, but there can be some edge cases.
*
* Better solution would be to store effect position and calculate real offset. However with many
* (>5) effects in chain, there is visible lag in strip position update, because during
* recalculation, hierarchy is not taken into account. */
int max_offset = 0;
/* Flush to 2D vector from internally used 3D vector. */
for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) {
tdsq = (TransDataSeq *)td->extra;
seq = tdsq->seq;
float loc[2];
view2d_edge_pan_loc_compensate(t, td->loc, loc);
new_frame = round_fl_to_int(loc[0]);
switch (tdsq->sel_flag) {
case SELECT: {
if (SEQ_transform_sequence_can_be_translated(seq)) {
offset = new_frame - tdsq->start_offset - seq->start;
SEQ_transform_translate_sequence(scene, seq, offset);
if (abs(offset) > abs(max_offset)) {
max_offset = offset;
}
}
seq->machine = round_fl_to_int(loc[1]);
CLAMP(seq->machine, 1, MAXSEQ);
break;
}
case SEQ_LEFTSEL: { /* No vertical transform. */
int old_startdisp = SEQ_time_left_handle_frame_get(scene, seq);
VSE: Make time operations self-contained This patch makes it possible to manipulate strips without need to use update functions to recalculate effect and meta strips. Prior to this change function `SEQ_time_update_sequence` had to be used to update mainly effects and meta strips. This was implemented in a way that relied on sorted list of strips, which can't always be done and in rare cases this approach failed. In case of meta strips, `seqbase` had to be passed and compared with "active" one to determine whether meta strip should be updated or not. This is especially weak system that is prone to bugs when functions are used by python API functions. Finally, other strip types had startdisp` and `enddisp` fields updated by this function and a lot of code relied on these fields even if strip start, length and offsets are available. This is completely unnecessary. Implemented changes: All effects and meta strips are updated when strip handles are moved or strip is translated, without need to call any update function. Function `SEQ_time_update_sequence` has been split to `SEQ_time_update_meta_strip_range` and `seq_time_update_effects_strip_range`. These functions should be only used within sequencer module code. Meta update is used for versioning, which is only reason for it not being declared internally. Sequence fields `startdisp` and `enddisp` are now only used for effects to store strip start and end points. These fields should be used only internally within sequencer module code. Use function `SEQ_time_*_handle_frame_get` to get strip start and end points. To update effects and meta strips with reasonable performance, cache for "parent" meta strip and attached effects is added to `SequenceLookup` cache, so it shares invalidation mechanisms. All caches are populated during single iteration. There should be no functional changes. Differential Revision: https://developer.blender.org/D14990
2022-06-02 01:39:40 +02:00
SEQ_time_left_handle_frame_set(t->scene, seq, new_frame);
if (abs(SEQ_time_left_handle_frame_get(scene, seq) - old_startdisp) > abs(max_offset)) {
max_offset = SEQ_time_left_handle_frame_get(scene, seq) - old_startdisp;
}
break;
}
case SEQ_RIGHTSEL: { /* No vertical transform. */
int old_enddisp = SEQ_time_right_handle_frame_get(scene, seq);
VSE: Make time operations self-contained This patch makes it possible to manipulate strips without need to use update functions to recalculate effect and meta strips. Prior to this change function `SEQ_time_update_sequence` had to be used to update mainly effects and meta strips. This was implemented in a way that relied on sorted list of strips, which can't always be done and in rare cases this approach failed. In case of meta strips, `seqbase` had to be passed and compared with "active" one to determine whether meta strip should be updated or not. This is especially weak system that is prone to bugs when functions are used by python API functions. Finally, other strip types had startdisp` and `enddisp` fields updated by this function and a lot of code relied on these fields even if strip start, length and offsets are available. This is completely unnecessary. Implemented changes: All effects and meta strips are updated when strip handles are moved or strip is translated, without need to call any update function. Function `SEQ_time_update_sequence` has been split to `SEQ_time_update_meta_strip_range` and `seq_time_update_effects_strip_range`. These functions should be only used within sequencer module code. Meta update is used for versioning, which is only reason for it not being declared internally. Sequence fields `startdisp` and `enddisp` are now only used for effects to store strip start and end points. These fields should be used only internally within sequencer module code. Use function `SEQ_time_*_handle_frame_get` to get strip start and end points. To update effects and meta strips with reasonable performance, cache for "parent" meta strip and attached effects is added to `SequenceLookup` cache, so it shares invalidation mechanisms. All caches are populated during single iteration. There should be no functional changes. Differential Revision: https://developer.blender.org/D14990
2022-06-02 01:39:40 +02:00
SEQ_time_right_handle_frame_set(t->scene, seq, new_frame);
if (abs(SEQ_time_right_handle_frame_get(scene, seq) - old_enddisp) > abs(max_offset)) {
max_offset = SEQ_time_right_handle_frame_get(scene, seq) - old_enddisp;
}
break;
}
}
}
TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
/* Update animation for effects. */
for (Sequence *seq : ts->time_dependent_strips) {
SEQ_offset_animdata(t->scene, seq, max_offset);
}
/* need to do the overlap check in a new loop otherwise adjacent strips
* will not be updated and we'll get false positives */
blender::VectorSet transformed_strips = seq_transform_collection_from_transdata(tc);
SEQ_iterator_set_expand(
t->scene, seqbase_active_get(t), transformed_strips, SEQ_query_strip_effect_chain);
for (Sequence *seq : transformed_strips) {
/* test overlap, displays red outline */
seq->flag &= ~SEQ_OVERLAP;
if (SEQ_transform_test_overlap(scene, seqbasep, seq)) {
seq->flag |= SEQ_OVERLAP;
}
}
}
static void recalcData_sequencer(TransInfo *t)
{
TransData *td;
int a;
Sequence *seq_prev = nullptr;
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
for (a = 0, td = tc->data; a < tc->data_len; a++, td++) {
TransDataSeq *tdsq = (TransDataSeq *)td->extra;
Sequence *seq = tdsq->seq;
if (seq != seq_prev) {
SEQ_relations_invalidate_cache_composite(t->scene, seq);
}
seq_prev = seq;
}
DEG_id_tag_update(&t->scene->id, ID_RECALC_SEQUENCER_STRIPS);
flushTransSeq(t);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Special After Transform Sequencer
* \{ */
static void special_aftertrans_update__sequencer(bContext * /*C*/, TransInfo *t)
{
if (t->state == TRANS_CANCEL) {
return;
}
/* freeSeqData in transform_conversions.c does this
* keep here so the else at the end won't run... */
SpaceSeq *sseq = (SpaceSeq *)t->area->spacedata.first;
/* Marker transform, not especially nice but we may want to move markers
* at the same time as strips in the Video Sequencer. */
if (sseq->flag & SEQ_MARKER_TRANS) {
/* can't use TFM_TIME_EXTEND
* for some reason EXTEND is changed into TRANSLATE, so use frame_side instead */
if (t->mode == TFM_SEQ_SLIDE) {
if (t->frame_side == 'B') {
ED_markers_post_apply_transform(
&t->scene->markers, t->scene, TFM_TIME_TRANSLATE, t->values_final[0], t->frame_side);
}
}
else if (ELEM(t->frame_side, 'L', 'R')) {
ED_markers_post_apply_transform(
&t->scene->markers, t->scene, TFM_TIME_EXTEND, t->values_final[0], t->frame_side);
}
}
}
void transform_convert_sequencer_channel_clamp(TransInfo *t, float r_val[2])
{
const TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
const int channel_offset = round_fl_to_int(r_val[1]);
const int min_channel_after_transform = ts->selection_channel_range_min + channel_offset;
const int max_channel_after_transform = ts->selection_channel_range_max + channel_offset;
if (max_channel_after_transform > MAXSEQ) {
r_val[1] -= max_channel_after_transform - MAXSEQ;
}
if (min_channel_after_transform < 1) {
r_val[1] -= min_channel_after_transform - 1;
}
}
/** \} */
TransConvertTypeInfo TransConvertType_Sequencer = {
/*flags*/ (T_POINTS | T_2D_EDIT),
/*create_trans_data*/ createTransSeqData,
/*recalc_data*/ recalcData_sequencer,
/*special_aftertrans_update*/ special_aftertrans_update__sequencer,
};