Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Antalik <richardantalik@gmail.com>2021-06-29 21:12:19 +0300
committerRichard Antalik <richardantalik@gmail.com>2021-06-29 21:30:31 +0300
commitfba9cd019f21f29bad1a6f3713370c5172dbc97f (patch)
tree5c42ca8e6f4d2f7051088306e99676d7e8b47b7b /source/blender/editors
parentea43ae4194e293599997f1d0d47430b462b4fd7f (diff)
VSE: Improved Snapping
Change snapping behavior to snap strip edges when they are close to snap point. Default behavior is, that each transformed strip is snapped to any other strip. Implement snapping controls in sequencer tool settings. These controls include: - Snapping on/off - Ability to snap to playhead and strip hold offset points - Filter snap points by excluding sound or muted strips - Control snapping distance Snapping controls are placed in timeline header similar to 3D viewport Reviewed By: mano-wii Differential Revision: https://developer.blender.org/D11646
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/transform/CMakeLists.txt1
-rw-r--r--source/blender/editors/transform/transform.c4
-rw-r--r--source/blender/editors/transform/transform.h5
-rw-r--r--source/blender/editors/transform/transform_convert.h4
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c62
-rw-r--r--source/blender/editors/transform/transform_mode_edge_seq_slide.c22
-rw-r--r--source/blender/editors/transform/transform_snap.c100
-rw-r--r--source/blender/editors/transform/transform_snap.h7
-rw-r--r--source/blender/editors/transform/transform_snap_sequencer.c266
9 files changed, 360 insertions, 111 deletions
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
index b0bc5c6abda..ad0a330f0f4 100644
--- a/source/blender/editors/transform/CMakeLists.txt
+++ b/source/blender/editors/transform/CMakeLists.txt
@@ -102,6 +102,7 @@ set(SRC
transform_orientations.c
transform_snap.c
transform_snap_object.c
+ transform_snap_sequencer.c
transform.h
transform_constraints.h
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index b03fb2c370f..02a5dcbc622 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -1727,6 +1727,10 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
t->draw_handle_cursor = WM_paint_cursor_activate(
SPACE_TYPE_ANY, RGN_TYPE_ANY, transform_draw_cursor_poll, transform_draw_cursor_draw, t);
}
+ else if (t->spacetype == SPACE_SEQ) {
+ t->draw_handle_view = ED_region_draw_cb_activate(
+ t->region->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
+ }
createTransData(C, t); /* Make #TransData structs from selection. */
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 4f97c3b6713..bd0ee1a51c6 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -335,7 +335,10 @@ typedef struct TransSnap {
/**
* Re-usable snap context data.
*/
- struct SnapObjectContext *object_context;
+ union {
+ struct SnapObjectContext *object_context;
+ struct TransSeqSnapData *seq_context;
+ };
} TransSnap;
typedef struct TransCon {
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index 918ce0739ed..971c23b8c69 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -48,8 +48,8 @@ void clipUVData(TransInfo *t);
void transform_convert_mesh_customdatacorrect_init(TransInfo *t);
/* transform_convert_sequencer.c */
-int transform_convert_sequencer_get_snap_bound(TransInfo *t);
-void transform_convert_sequencer_channel_clamp(TransInfo *t);
+void transform_convert_sequencer_channel_clamp(TransInfo *t, float r_val[2]);
+
/********************* intern **********************/
/* transform_convert.c */
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index 2cb0e3c8589..51914004e70 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -63,9 +63,6 @@ typedef struct TransDataSeq {
*/
typedef struct TransSeq {
TransDataSeq *tdseq;
- int min;
- int max;
- bool snap_left;
int selection_channel_range_min;
int selection_channel_range_max;
} TransSeq;
@@ -252,42 +249,6 @@ static int SeqToTransData_build(
return tot;
}
-static void SeqTransDataBounds(TransInfo *t, ListBase *seqbase, TransSeq *ts)
-{
- Sequence *seq;
- int count, flag;
- int max = INT32_MIN, min = INT32_MAX;
-
- for (seq = seqbase->first; seq; seq = seq->next) {
-
- /* just to get the flag since there are corner cases where this isn't totally obvious */
- 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) {
- min = min_ii(seq->startdisp, min);
- max = max_ii(seq->startdisp, max);
- }
- if (flag & SEQ_RIGHTSEL) {
- min = min_ii(seq->enddisp, min);
- max = max_ii(seq->enddisp, max);
- }
- }
- else {
- min = min_ii(seq->startdisp, min);
- max = max_ii(seq->enddisp, max);
- }
- }
- }
-
- if (ts) {
- ts->max = max;
- ts->min = min;
- }
-}
-
static void free_transform_custom_data(TransCustomData *custom_data)
{
if ((custom_data->data != NULL) && custom_data->use_free) {
@@ -544,15 +505,6 @@ void createTransSeqData(TransInfo *t)
/* loop 2: build transdata array */
SeqToTransData_build(t, ed->seqbasep, td, td2d, tdsq);
- SeqTransDataBounds(t, ed->seqbasep, ts);
-
- if (t->flag & T_MODAL) {
- /* set the snap mode based on how close the mouse is at the end/start points */
- int xmouse = (int)UI_view2d_region_to_view_x((View2D *)t->view, t->mouse.imval[0]);
- if (abs(xmouse - ts->max) > abs(xmouse - ts->min)) {
- ts->snap_left = true;
- }
- }
ts->selection_channel_range_min = MAXSEQ + 1;
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
@@ -719,25 +671,19 @@ void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t)
}
}
-void transform_convert_sequencer_channel_clamp(TransInfo *t)
+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(t->values[1]);
+ 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) {
- t->values[1] -= max_channel_after_transform - MAXSEQ;
+ r_val[1] -= max_channel_after_transform - MAXSEQ;
}
if (min_channel_after_transform < 1) {
- t->values[1] -= min_channel_after_transform - 1;
+ r_val[1] -= min_channel_after_transform - 1;
}
}
-int transform_convert_sequencer_get_snap_bound(TransInfo *t)
-{
- TransSeq *ts = TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
- return ts->snap_left ? ts->min : ts->max;
-}
-
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
index 7e7b79c9f90..1b054696930 100644
--- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
@@ -23,8 +23,10 @@
#include <stdlib.h>
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
#include "BLI_math.h"
-#include "BLI_string.h"
#include "BKE_context.h"
#include "BKE_unit.h"
@@ -37,6 +39,10 @@
#include "UI_interface.h"
#include "UI_view2d.h"
+#include "SEQ_iterator.h"
+#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
+
#include "BLT_translation.h"
#include "transform.h"
@@ -102,13 +108,11 @@ static void applySeqSlideValue(TransInfo *t, const float val[2])
}
}
-static void applySeqSlide(TransInfo *t, const int mval[2])
+static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2]))
{
char str[UI_MAX_DRAW_STR];
float values_final[3] = {0.0f};
- snapSequenceBounds(t, mval);
- transform_convert_sequencer_channel_clamp(t);
if (applyNumInput(&t->num, values_final)) {
if (t->con.mode & CON_APPLY) {
if (t->con.mode & CON_AXIS0) {
@@ -119,11 +123,14 @@ static void applySeqSlide(TransInfo *t, const int mval[2])
}
}
}
- else if (t->con.mode & CON_APPLY) {
- t->con.applyVec(t, NULL, NULL, t->values, values_final);
- }
else {
copy_v2_v2(values_final, t->values);
+ applySnapping(t, values_final);
+ transform_convert_sequencer_channel_clamp(t, values_final);
+
+ if (t->con.mode & CON_APPLY) {
+ t->con.applyVec(t, NULL, NULL, t->values, values_final);
+ }
}
values_final[0] = floorf(values_final[0] + 0.5f);
@@ -164,4 +171,5 @@ void initSeqSlide(TransInfo *t)
t->custom.mode.data = (void *)WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_TRANSLATE);
}
}
+
/** \} */
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index b6e0f07cc70..34a380f9199 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -41,9 +41,6 @@
#include "RNA_access.h"
-#include "SEQ_sequencer.h"
-#include "SEQ_time.h"
-
#include "WM_types.h"
#include "ED_gizmo_library.h"
@@ -55,6 +52,10 @@
#include "UI_resources.h"
#include "UI_view2d.h"
+#include "SEQ_iterator.h"
+#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
+
#include "MEM_guardedalloc.h"
#include "transform.h"
@@ -164,10 +165,7 @@ bool transformModeUseSnap(const TransInfo *t)
if (t->mode == TFM_RESIZE) {
return (ts->snap_transform_mode_flag & SCE_SNAP_TRANSFORM_MODE_SCALE) != 0;
}
- if (t->mode == TFM_VERT_SLIDE) {
- return true;
- }
- if (t->mode == TFM_EDGE_SLIDE) {
+ if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE, TFM_SEQ_SLIDE)) {
return true;
}
@@ -295,6 +293,21 @@ void drawSnapping(const struct bContext *C, TransInfo *t)
GPU_blend(GPU_BLEND_NONE);
}
}
+ else if (t->spacetype == SPACE_SEQ) {
+ if (validSnap(t)) {
+ const ARegion *region = CTX_wm_region(C);
+ GPU_blend(GPU_BLEND_ALPHA);
+ uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex2f(pos, t->tsnap.snapPoint[0], region->v2d.cur.ymin);
+ immVertex2f(pos, t->tsnap.snapPoint[0], region->v2d.cur.ymax);
+ immEnd();
+ immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
+ }
+ }
}
eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event)
@@ -476,11 +489,14 @@ void applySnapping(TransInfo *t, float *vec)
/* TODO: add exception for object mode, no need to slow it down then. */
if (current - t->tsnap.last >= 0.01) {
t->tsnap.calcSnap(t, vec);
- t->tsnap.targetSnap(t);
-
- t->tsnap.last = current;
+ if (t->tsnap.targetSnap) {
+ t->tsnap.targetSnap(t);
+ }
}
- if (validSnap(t)) {
+
+ t->tsnap.last = current;
+
+ if (t->tsnap.applySnap && validSnap(t)) {
t->tsnap.applySnap(t, vec);
}
}
@@ -567,6 +583,9 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.mode = ts->snap_uv_mode;
}
+ else if (t->spacetype == SPACE_SEQ) {
+ t->tsnap.mode = SEQ_tool_settings_snap_mode_get(t->scene);
+ }
else {
/* force project off when not supported */
if ((ts->snap_mode & SCE_SNAP_MODE_FACE) == 0) {
@@ -626,16 +645,12 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
}
}
- else if (t->spacetype == SPACE_NODE) {
+ else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
setSnappingCallback(t);
t->tsnap.modeSelect = SNAP_NOT_SELECTED;
}
- else if (t->spacetype == SPACE_SEQ) {
- /* We do our own snapping currently, so nothing here */
- t->tsnap.mode = SCE_SNAP_MODE_GRID; /* Dummy, should we rather add a NOP mode? */
- }
else {
- /* Always increment outside of 3D view */
+ /* Fallback. */
t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
}
@@ -656,6 +671,11 @@ static void initSnappingMode(TransInfo *t)
}
}
}
+ else if (t->spacetype == SPACE_SEQ) {
+ if (t->tsnap.seq_context == NULL) {
+ t->tsnap.seq_context = transform_snap_sequencer_data_alloc(t);
+ }
+ }
}
void initSnapping(TransInfo *t, wmOperator *op)
@@ -708,6 +728,9 @@ void initSnapping(TransInfo *t, wmOperator *op)
t->tsnap.snap_self = !((t->settings->snap_flag & SCE_SNAP_NO_SELF) != 0);
t->tsnap.peel = ((t->settings->snap_flag & SCE_SNAP_PROJECT) != 0);
}
+ else if ((t->spacetype == SPACE_SEQ) && (ts->snap_flag & SCE_SNAP_SEQ)) {
+ t->modifiers |= MOD_SNAP;
+ }
}
t->tsnap.target = snap_target;
@@ -717,7 +740,11 @@ void initSnapping(TransInfo *t, wmOperator *op)
void freeSnapping(TransInfo *t)
{
- if (t->tsnap.object_context) {
+ if ((t->spacetype == SPACE_SEQ) && t->tsnap.seq_context) {
+ transform_snap_sequencer_data_free(t->tsnap.seq_context);
+ t->tsnap.seq_context = NULL;
+ }
+ else if (t->tsnap.object_context) {
ED_transform_snap_object_context_destroy(t->tsnap.object_context);
t->tsnap.object_context = NULL;
}
@@ -727,6 +754,11 @@ static void setSnappingCallback(TransInfo *t)
{
t->tsnap.calcSnap = CalcSnapGeometry;
+ if (t->spacetype == SPACE_SEQ) {
+ /* The target is calculated along with the snap point. */
+ return;
+ }
+
switch (t->tsnap.target) {
case SCE_SNAP_TARGET_CLOSEST:
t->tsnap.targetSnap = TargetSnapClosest;
@@ -849,7 +881,7 @@ void getSnapPoint(const TransInfo *t, float vec[3])
/** \name Calc Snap (Generic)
* \{ */
-static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
+static void CalcSnapGeometry(TransInfo *t, float *vec)
{
if (t->spacetype == SPACE_VIEW3D) {
float loc[3];
@@ -930,6 +962,14 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
}
}
}
+ else if (t->spacetype == SPACE_SEQ) {
+ if (transform_snap_sequencer_apply(t, vec, t->tsnap.snapPoint)) {
+ t->tsnap.status |= (POINT_INIT | TARGET_INIT);
+ }
+ else {
+ t->tsnap.status &= ~(POINT_INIT | TARGET_INIT);
+ }
+ }
}
/** \} */
@@ -1441,28 +1481,6 @@ void snapFrameTransform(TransInfo *t,
*r_val = (float)val;
}
-/*================================================================*/
-
-void snapSequenceBounds(TransInfo *t, const int mval[2])
-{
- /* Reuse increment, strictly speaking could be another snap mode, but leave as is. */
- if (!(t->modifiers & MOD_SNAP_INVERT)) {
- return;
- }
-
- /* Convert to frame range. */
- float xmouse, ymouse;
- UI_view2d_region_to_view(&t->region->v2d, mval[0], mval[1], &xmouse, &ymouse);
- const int frame_curr = round_fl_to_int(xmouse);
-
- /* Now find the closest sequence. */
- const int frame_near = SEQ_time_find_next_prev_edit(
- t->scene, frame_curr, SEQ_SIDE_BOTH, true, false, true);
-
- const int frame_snap = transform_convert_sequencer_get_snap_bound(t);
- t->values[0] = frame_near - frame_snap;
-}
-
static void snap_grid_apply(
TransInfo *t, const int max_index, const float grid_dist, const float loc[3], float r_out[3])
{
diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
index bf14e564380..e0989418c1c 100644
--- a/source/blender/editors/transform/transform_snap.h
+++ b/source/blender/editors/transform/transform_snap.h
@@ -58,8 +58,6 @@ bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float
bool transform_snap_increment(const TransInfo *t, float *val);
bool transform_snap_grid(TransInfo *t, float *val);
-void snapSequenceBounds(TransInfo *t, const int mval[2]);
-
bool activeSnap(const TransInfo *t);
bool activeSnap_with_project(const TransInfo *t);
@@ -82,3 +80,8 @@ eRedrawFlag updateSelectedSnapPoint(TransInfo *t);
void removeSnapPoint(TransInfo *t);
float transform_snap_distance_len_squared_fn(TransInfo *t, const float p1[3], const float p2[3]);
+
+/* transform_snap_sequencer.c */
+struct TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t);
+void transform_snap_sequencer_data_free(struct TransSeqSnapData *data);
+bool transform_snap_sequencer_apply(struct TransInfo *t, float *vec, float *snap_point);
diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c
new file mode 100644
index 00000000000..22d738e75ca
--- /dev/null
+++ b/source/blender/editors/transform/transform_snap_sequencer.c
@@ -0,0 +1,266 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \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"
+
+#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
+ * \{ */
+
+static int seq_get_snap_source_points_count(SeqCollection *snap_sources)
+{
+ return SEQ_collection_count(snap_sources) * 2;
+}
+
+static void seq_snap_source_points_alloc(TransSeqSnapData *snap_data, SeqCollection *snap_sources)
+{
+ const size_t point_count = seq_get_snap_source_points_count(snap_sources);
+ 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);
+}
+
+static void seq_snap_source_points_build(const TransInfo *t,
+ 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
+ * \{ */
+
+static SeqCollection *query_snap_targets(const TransInfo *t)
+{
+ const ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false));
+ const short snap_flag = SEQ_tool_settings_snap_flag_get(t->scene);
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if ((seq->flag & SELECT)) {
+ 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;
+ }
+ SEQ_collection_append_strip(seq, collection);
+ }
+ return collection;
+}
+
+static int seq_get_snap_target_points_count(const TransInfo *t,
+ TransSeqSnapData *snap_data,
+ 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;
+ }
+
+ count *= SEQ_collection_count(snap_targets);
+
+ if (snap_mode & SEQ_SNAP_TO_PLAYHEAD) {
+ 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;
+
+ if (snap_mode & SEQ_SNAP_TO_PLAYHEAD) {
+ 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);
+ if (seq->anim_startofs == 0) {
+ content_start = seq->startdisp;
+ }
+ if (seq->anim_endofs == 0) {
+ content_end = seq->enddisp;
+ }
+ 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)
+{
+ TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__);
+ ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false));
+
+ /* Build arrays of snap points. */
+ SeqCollection *snap_sources = SEQ_query_selected_strips(seqbase);
+ seq_snap_source_points_alloc(snap_data, snap_sources);
+ seq_snap_source_points_build(t, snap_data, snap_sources);
+ SEQ_collection_free(snap_sources);
+
+ SeqCollection *snap_targets = query_snap_targets(t);
+ 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);
+ 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);
+}
+
+bool transform_snap_sequencer_apply(TransInfo *t, float *vec, float *snap_point)
+{
+ const TransSeqSnapData *snap_data = t->tsnap.seq_context;
+ int best_dist = MAXFRAME, best_target_frame = 0, best_source_frame = 0;
+ *snap_point = 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;
+ }
+
+ *snap_point = best_target_frame;
+ *vec += best_target_frame - best_source_frame;
+ return true;
+}