2022-02-10 23:07:11 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
2021-06-29 20:12:19 +02:00
|
|
|
|
|
|
|
/** \file
|
|
|
|
* \ingroup edtransform
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "BLI_blenlib.h"
|
|
|
|
#include "BLI_math.h"
|
|
|
|
|
|
|
|
#include "BKE_context.h"
|
|
|
|
|
|
|
|
#include "ED_screen.h"
|
|
|
|
|
|
|
|
#include "UI_view2d.h"
|
|
|
|
|
2021-07-26 14:55:14 +02:00
|
|
|
#include "SEQ_effects.h"
|
2021-06-29 20:12:19 +02:00
|
|
|
#include "SEQ_iterator.h"
|
|
|
|
#include "SEQ_sequencer.h"
|
|
|
|
|
|
|
|
#include "transform.h"
|
|
|
|
#include "transform_snap.h"
|
|
|
|
|
|
|
|
typedef struct TransSeqSnapData {
|
|
|
|
int *source_snap_points;
|
|
|
|
int *target_snap_points;
|
|
|
|
int source_snap_point_count;
|
|
|
|
int target_snap_point_count;
|
|
|
|
int final_snap_frame;
|
|
|
|
} TransSeqSnapData;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Snap sources
|
|
|
|
* \{ */
|
|
|
|
|
2021-06-30 06:35:01 +02:00
|
|
|
static int seq_get_snap_source_points_len(SeqCollection *snap_sources)
|
2021-06-29 20:12:19 +02:00
|
|
|
{
|
2021-06-30 06:35:01 +02:00
|
|
|
return SEQ_collection_len(snap_sources) * 2;
|
2021-06-29 20:12:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void seq_snap_source_points_alloc(TransSeqSnapData *snap_data, SeqCollection *snap_sources)
|
|
|
|
{
|
2021-06-30 06:35:01 +02:00
|
|
|
const size_t point_count = seq_get_snap_source_points_len(snap_sources);
|
2021-06-29 20:12:19 +02:00
|
|
|
snap_data->source_snap_points = MEM_callocN(sizeof(int) * point_count, __func__);
|
|
|
|
memset(snap_data->source_snap_points, 0, sizeof(int));
|
|
|
|
snap_data->source_snap_point_count = point_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cmp_fn(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
return (*(int *)a - *(int *)b);
|
|
|
|
}
|
|
|
|
|
2021-06-30 06:32:44 +02:00
|
|
|
static void seq_snap_source_points_build(const TransInfo *UNUSED(t),
|
2021-06-29 20:12:19 +02:00
|
|
|
TransSeqSnapData *snap_data,
|
|
|
|
SeqCollection *snap_sources)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
Sequence *seq;
|
|
|
|
SEQ_ITERATOR_FOREACH (seq, snap_sources) {
|
|
|
|
int left = 0, right = 0;
|
|
|
|
if (seq->flag & SEQ_LEFTSEL) {
|
|
|
|
left = right = seq->startdisp;
|
|
|
|
}
|
|
|
|
else if (seq->flag & SEQ_RIGHTSEL) {
|
|
|
|
left = right = seq->enddisp;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
left = seq->startdisp;
|
|
|
|
right = seq->enddisp;
|
|
|
|
}
|
|
|
|
|
|
|
|
snap_data->source_snap_points[i] = left;
|
|
|
|
snap_data->source_snap_points[i + 1] = right;
|
|
|
|
i += 2;
|
|
|
|
BLI_assert(i <= snap_data->source_snap_point_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort(snap_data->source_snap_points, snap_data->source_snap_point_count, sizeof(int), cmp_fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Snap targets
|
|
|
|
* \{ */
|
|
|
|
|
2021-07-26 14:55:14 +02:00
|
|
|
/* Add effect strips directly or indirectly connected to `seq_reference` to `collection`. */
|
|
|
|
static void query_strip_effects_fn(Sequence *seq_reference,
|
|
|
|
ListBase *seqbase,
|
|
|
|
SeqCollection *collection)
|
2021-06-29 20:12:19 +02:00
|
|
|
{
|
2021-07-26 14:55:14 +02:00
|
|
|
if (!SEQ_collection_append_strip(seq_reference, collection)) {
|
|
|
|
return; /* Strip is already in set, so all effects connected to it are as well. */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find all strips connected to `seq_reference`. */
|
|
|
|
LISTBASE_FOREACH (Sequence *, seq_test, seqbase) {
|
|
|
|
if (seq_test->seq1 == seq_reference || seq_test->seq2 == seq_reference ||
|
|
|
|
seq_test->seq3 == seq_reference) {
|
|
|
|
query_strip_effects_fn(seq_test, seqbase, collection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static SeqCollection *seq_collection_extract_effects(SeqCollection *collection)
|
|
|
|
{
|
|
|
|
SeqCollection *effects = SEQ_collection_create(__func__);
|
|
|
|
Sequence *seq;
|
|
|
|
SEQ_ITERATOR_FOREACH (seq, collection) {
|
|
|
|
if (SEQ_effect_get_num_inputs(seq->type) > 0) {
|
|
|
|
SEQ_collection_append_strip(seq, effects);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return effects;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SeqCollection *query_snap_targets(const TransInfo *t, SeqCollection *snap_sources)
|
|
|
|
{
|
2021-09-02 03:29:32 +02:00
|
|
|
ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene));
|
2021-06-29 20:12:19 +02:00
|
|
|
const short snap_flag = SEQ_tool_settings_snap_flag_get(t->scene);
|
2021-07-26 14:55:14 +02:00
|
|
|
SeqCollection *snap_targets = SEQ_collection_create(__func__);
|
2021-06-29 20:12:19 +02:00
|
|
|
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
|
2021-08-05 08:48:29 +02:00
|
|
|
if (seq->flag & SELECT) {
|
2021-06-29 20:12:19 +02:00
|
|
|
continue; /* Selected are being transformed. */
|
|
|
|
}
|
|
|
|
if ((seq->flag & SEQ_MUTE) && (snap_flag & SEQ_SNAP_IGNORE_MUTED)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (seq->type == SEQ_TYPE_SOUND_RAM && (snap_flag & SEQ_SNAP_IGNORE_SOUND)) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-07-26 14:55:14 +02:00
|
|
|
SEQ_collection_append_strip(seq, snap_targets);
|
2021-06-29 20:12:19 +02:00
|
|
|
}
|
2021-07-26 14:55:14 +02:00
|
|
|
|
|
|
|
/* Effects will always change position with strip to which they are connected and they don't have
|
|
|
|
* to be selected. Remove such strips from `snap_targets` collection. */
|
|
|
|
SeqCollection *snap_sources_temp = SEQ_collection_duplicate(snap_sources);
|
|
|
|
SEQ_collection_expand(seqbase, snap_sources_temp, query_strip_effects_fn);
|
|
|
|
SeqCollection *snap_sources_effects = seq_collection_extract_effects(snap_sources_temp);
|
|
|
|
SEQ_collection_exclude(snap_targets, snap_sources_effects);
|
|
|
|
SEQ_collection_free(snap_sources_temp);
|
|
|
|
|
|
|
|
return snap_targets;
|
2021-06-29 20:12:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int seq_get_snap_target_points_count(const TransInfo *t,
|
2021-06-30 06:32:44 +02:00
|
|
|
TransSeqSnapData *UNUSED(snap_data),
|
2021-06-29 20:12:19 +02:00
|
|
|
SeqCollection *snap_targets)
|
|
|
|
{
|
|
|
|
const short snap_mode = t->tsnap.mode;
|
|
|
|
|
|
|
|
int count = 2; /* Strip start and end are always used. */
|
|
|
|
|
|
|
|
if (snap_mode & SEQ_SNAP_TO_STRIP_HOLD) {
|
|
|
|
count += 2;
|
|
|
|
}
|
|
|
|
|
2021-06-30 06:35:01 +02:00
|
|
|
count *= SEQ_collection_len(snap_targets);
|
2021-06-29 20:12:19 +02:00
|
|
|
|
2021-07-01 03:02:30 +02:00
|
|
|
if (snap_mode & SEQ_SNAP_TO_CURRENT_FRAME) {
|
2021-06-29 20:12:19 +02:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void seq_snap_target_points_alloc(const TransInfo *t,
|
|
|
|
TransSeqSnapData *snap_data,
|
|
|
|
SeqCollection *snap_targets)
|
|
|
|
{
|
|
|
|
const size_t point_count = seq_get_snap_target_points_count(t, snap_data, snap_targets);
|
|
|
|
snap_data->target_snap_points = MEM_callocN(sizeof(int) * point_count, __func__);
|
|
|
|
memset(snap_data->target_snap_points, 0, sizeof(int));
|
|
|
|
snap_data->target_snap_point_count = point_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void seq_snap_target_points_build(const TransInfo *t,
|
|
|
|
TransSeqSnapData *snap_data,
|
|
|
|
SeqCollection *snap_targets)
|
|
|
|
{
|
|
|
|
const Scene *scene = t->scene;
|
|
|
|
const short snap_mode = t->tsnap.mode;
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
2021-07-01 03:02:30 +02:00
|
|
|
if (snap_mode & SEQ_SNAP_TO_CURRENT_FRAME) {
|
2021-06-29 20:12:19 +02:00
|
|
|
snap_data->target_snap_points[i] = CFRA;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
Sequence *seq;
|
|
|
|
SEQ_ITERATOR_FOREACH (seq, snap_targets) {
|
|
|
|
snap_data->target_snap_points[i] = seq->startdisp;
|
|
|
|
snap_data->target_snap_points[i + 1] = seq->enddisp;
|
|
|
|
i += 2;
|
|
|
|
|
|
|
|
if (snap_mode & SEQ_SNAP_TO_STRIP_HOLD) {
|
|
|
|
int content_start = min_ii(seq->enddisp, seq->start);
|
|
|
|
int content_end = max_ii(seq->startdisp, seq->start + seq->len);
|
2021-07-26 14:55:14 +02:00
|
|
|
/* Effects and single image strips produce incorrect content length. Skip these strips. */
|
|
|
|
if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->len == 1) {
|
2021-10-25 04:33:43 +02:00
|
|
|
content_start = seq->startdisp;
|
|
|
|
content_end = seq->enddisp;
|
2021-06-29 20:12:19 +02:00
|
|
|
}
|
2021-10-25 04:24:41 +02:00
|
|
|
|
|
|
|
CLAMP(content_start, seq->startdisp, seq->enddisp);
|
|
|
|
CLAMP(content_end, seq->startdisp, seq->enddisp);
|
|
|
|
|
2021-06-29 20:12:19 +02:00
|
|
|
snap_data->target_snap_points[i] = content_start;
|
|
|
|
snap_data->target_snap_points[i + 1] = content_end;
|
|
|
|
i += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BLI_assert(i <= snap_data->target_snap_point_count);
|
|
|
|
qsort(snap_data->target_snap_points, snap_data->target_snap_point_count, sizeof(int), cmp_fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Snap utilities
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
static int seq_snap_threshold_get_frame_distance(const TransInfo *t)
|
|
|
|
{
|
|
|
|
const int snap_distance = SEQ_tool_settings_snap_distance_get(t->scene);
|
|
|
|
const struct View2D *v2d = &t->region->v2d;
|
|
|
|
return round_fl_to_int(UI_view2d_region_to_view_x(v2d, snap_distance) -
|
|
|
|
UI_view2d_region_to_view_x(v2d, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t)
|
|
|
|
{
|
2021-09-21 09:38:30 +02:00
|
|
|
if (t->data_type == TC_SEQ_IMAGE_DATA) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-06-29 20:12:19 +02:00
|
|
|
TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__);
|
2021-09-02 03:29:32 +02:00
|
|
|
ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene));
|
2021-06-29 20:12:19 +02:00
|
|
|
|
|
|
|
SeqCollection *snap_sources = SEQ_query_selected_strips(seqbase);
|
2021-07-26 14:55:14 +02:00
|
|
|
SeqCollection *snap_targets = query_snap_targets(t, snap_sources);
|
|
|
|
|
2021-09-07 23:39:50 +02:00
|
|
|
if (SEQ_collection_len(snap_sources) == 0) {
|
2021-07-26 14:55:14 +02:00
|
|
|
SEQ_collection_free(snap_targets);
|
|
|
|
SEQ_collection_free(snap_sources);
|
|
|
|
MEM_freeN(snap_data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Build arrays of snap points. */
|
2021-06-29 20:12:19 +02:00
|
|
|
seq_snap_source_points_alloc(snap_data, snap_sources);
|
|
|
|
seq_snap_source_points_build(t, snap_data, snap_sources);
|
|
|
|
SEQ_collection_free(snap_sources);
|
|
|
|
|
|
|
|
seq_snap_target_points_alloc(t, snap_data, snap_targets);
|
|
|
|
seq_snap_target_points_build(t, snap_data, snap_targets);
|
|
|
|
SEQ_collection_free(snap_targets);
|
2021-07-26 14:55:14 +02:00
|
|
|
|
2021-06-29 20:12:19 +02:00
|
|
|
return snap_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void transform_snap_sequencer_data_free(TransSeqSnapData *data)
|
|
|
|
{
|
|
|
|
MEM_freeN(data->source_snap_points);
|
|
|
|
MEM_freeN(data->target_snap_points);
|
|
|
|
MEM_freeN(data);
|
|
|
|
}
|
|
|
|
|
2021-06-29 23:08:41 +02:00
|
|
|
bool transform_snap_sequencer_calc(TransInfo *t)
|
2021-06-29 20:12:19 +02:00
|
|
|
{
|
2021-07-26 14:55:14 +02:00
|
|
|
const TransSeqSnapData *snap_data = t->tsnap.seq_context;
|
|
|
|
if (snap_data == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-07-14 13:22:40 +02:00
|
|
|
/* Prevent snapping when constrained to Y axis. */
|
|
|
|
if (t->con.mode & CON_APPLY && t->con.mode & CON_AXIS1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-06-29 20:12:19 +02:00
|
|
|
int best_dist = MAXFRAME, best_target_frame = 0, best_source_frame = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < snap_data->source_snap_point_count; i++) {
|
|
|
|
int snap_source_frame = snap_data->source_snap_points[i] + round_fl_to_int(t->values[0]);
|
|
|
|
for (int j = 0; j < snap_data->target_snap_point_count; j++) {
|
|
|
|
int snap_target_frame = snap_data->target_snap_points[j];
|
|
|
|
|
|
|
|
int dist = abs(snap_target_frame - snap_source_frame);
|
|
|
|
if (dist > best_dist) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
best_dist = dist;
|
|
|
|
best_target_frame = snap_target_frame;
|
|
|
|
best_source_frame = snap_source_frame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (best_dist > seq_snap_threshold_get_frame_distance(t)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-06-29 23:08:41 +02:00
|
|
|
t->tsnap.snapPoint[0] = best_target_frame;
|
|
|
|
t->tsnap.snapTarget[0] = best_source_frame;
|
2021-06-29 20:12:19 +02:00
|
|
|
return true;
|
|
|
|
}
|
2021-06-29 23:08:41 +02:00
|
|
|
|
|
|
|
void transform_snap_sequencer_apply_translate(TransInfo *t, float *vec)
|
|
|
|
{
|
|
|
|
*vec += t->tsnap.snapPoint[0] - t->tsnap.snapTarget[0];
|
|
|
|
}
|