Page MenuHome

VSE: Improved Snapping
ClosedPublic

Authored by Richard Antalik (ISS) on Jun 18 2021, 12:19 PM.

Details

Summary

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

Diff Detail

Repository
rB Blender
Branch
snap2 (branched from master)
Build Status
Buildable 15380
Build 15380: arc lint + arc unit

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
  • Update rebased on master, arc pulled some very weird base, and I didn't notice

So with no extend/ripple(+alt), the strip end will overwrite or else ripple? Then the snap point will be quite important, to avoid overwriting when users just want to extend to next strip.

A thought on all of the snapping options, I hope you'll make a snap-all default, so when a casual user clicks the magnet, it'll snap at all points.

  • Update copyright
  • Add snap to hold offset

So with no extend/ripple(+alt), the strip end will overwrite or else ripple? Then the snap point will be quite important, to avoid overwriting when users just want to extend to next strip.

Idea is to make process of actual overwriting of footage simpler, so you don't have to adjust 2 strips but only one. I haven't tried that in practice how that works though. Probably won't work great for me, but I don't think it would break my current workflow. It shouldn't break current workflow at all really. Once I make patch it can be tested.

A thought on all of the snapping options, I hope you'll make a snap-all default, so when a casual user clicks the magnet, it'll snap at all points.

Sure, there should be sensible defaults.

Typically in NLEs, there would be button to toggle between insert(ripple) and overwrite for trimming, adding material and drag and drop.

There seems to be a small inaccuracy on snapping on hold:

Also normal splits seem to snap to an added distance of plus one frame, making the strip outline red.

Btw. the snapping function isn't written out in a word in the UI of the other editors.
Layout:


Vs. VSE:

There seems to be a small inaccuracy on snapping on hold:

Can you provide your file? I can't reproduce this issue

Could it be because the snap_threshold was set very high?

Could it be because the snap_threshold was set very high?

Actually I think I can reproduce this with 2 color strips, but not video strips, will check where the problem is

  • Fix hold offset snapping, add defauts to versioning.
Germano Cavalcante (mano-wii) requested changes to this revision.Jun 22 2021, 5:46 PM
Germano Cavalcante (mano-wii) added inline comments.
source/blender/editors/transform/transform_convert.h
51

The source indicates that the header should be transform_snap.h

52

The naming convention for the new non-static functions of the transform is to prefix the header.

source/blender/editors/transform/transform_convert_sequencer.c
512

If SeqTransDataBounds is no longer used, it can be removed.

source/blender/editors/transform/transform_snap.c
190

Where does the name source_points come from?

source/blender/editors/transform/transform_snap.h
60–61

Names that follow the convention:

  • transform_snap_seq or
  • transform_snap_seq_apply

Also add a comment indicating which file it is defined.

This revision now requires changes to proceed.Jun 22 2021, 5:46 PM
Richard Antalik (ISS) marked 5 inline comments as done.
  • Simplify snapping settings
  • Fix defaults for new scenes
  • Add tool settings accessor functions
  • Address inlines
source/blender/editors/transform/transform_snap.c
190

These were renamed by accident

  • Remove unused variable from struct TransSeqSnapData
  • Use const as much as possible
  • Remove unused source_point_ min/max
  • Don't pass mouse position to calculate snap distance - it's unnecessary
Richard Antalik (ISS) edited the summary of this revision. (Show Details)Jun 23 2021, 10:19 AM

@Germano Cavalcante (mano-wii) I want to implement simple visual indication of where snapping ocurs, would you recommend to add that here? otherwise this should be ready to review.

Is is working really well. Thank you for this major improvement of the UX.

One minor thing I noticed is when having a very long timeline range ex. 100000 frames and you zoom out to this range, the 15px doesn't seem to take the zoom level into account. I don't know if it should, but moving anything at this zoom level will snap everything when you enter transform. Maybe this is related to this: T85868

On the UI, this is "before":

If you want to simplify the UI like this:

(I removed the "Snapping" next to the icon, since in ex. 3D View there is no word to the snapping drop down in the header. And tried to remove the redundant texts.)

Looking at it again, maybe Distance can be placed on top, so there is no misunderstanding of the "Distance" somehow should be related to "Ignore"?

Here's a bit of code:

class SEQUENCER_PT_snapping(Panel):
    bl_space_type = 'SEQUENCE_EDITOR'
    bl_region_type = 'HEADER'
    bl_label = ""

    def draw(self, context):
        tool_settings = context.tool_settings.sequencer_tool_settings

        layout = self.layout
        layout.use_property_split = True
        layout.use_property_decorate = False

        col = layout.column(heading="Snap to", align=True)
        col.prop(tool_settings, "snap_to_strip_hold_offset", text="Hold Offset")
        col.prop(tool_settings, "snap_to_playhead", text="Playhead")

        col = layout.column(heading="Ignore", align=True)
        col.prop(tool_settings, "snap_ignore_muted", text="Muted Strips")
        col.prop(tool_settings, "snap_ignore_sound",text="Sound Strips")

        col = layout.column()
        col.prop(tool_settings, "snap_distance", slider=True, text="Distance")

@Germano Cavalcante (mano-wii) I want to implement simple visual indication of where snapping ocurs, would you recommend to add that here? otherwise this should be ready to review.

If not essential, it's better in a separate patch. (This even helps when organizing commits).


Looking at the patch again, I noticed things that were outside convention.
If transform_snap_sequencer_apply is only used for transform_mode_edge_seq_slide mode, I see no reason to spread the definitions across the transform code. Everything should be defined inside transform_mode_edge_seq_slide.c.`
Custom data used only by the mode are stored in t->custom.mode.data

If this type of snap is intended to be shared in different modes (like in transform_mode_translate.c), it should set the callbacks t->tsnap.calcSnap and t->tsnap.targetSnap.
The existing custom sequencer snap code itself looks like it should also just be in the mode it's used for. (That was a slip at the time of splitting all the transform code into multiple files).
(I should have noticed this before)

Is is working really well. Thank you for this major improvement of the UX.

One minor thing I noticed is when having a very long timeline range ex. 100000 frames and you zoom out to this range, the 15px doesn't seem to take the zoom level into account.

I can't reproduce this issue even on minimum possible zoom level

If you want to simplify the UI like this:

(I removed the "Snapping" next to the icon, since in ex. 3D View there is no word to the snapping drop down in the header. And tried to remove the redundant texts.)

I think this looks better, so I will use this layout. Thanks for suggestion.

@Germano Cavalcante (mano-wii) I want to implement simple visual indication of where snapping ocurs, would you recommend to add that here? otherwise this should be ready to review.

Looking at the patch again, I noticed things that were outside convention.
If transform_snap_sequencer_apply is only used for transform_mode_edge_seq_slide mode, I see no reason to spread the definitions across the transform code. Everything should be defined inside transform_mode_edge_seq_slide.c.`
Custom data used only by the mode are stored in t->custom.mode.data

If this type of snap is intended to be shared in different modes (like in transform_mode_translate.c), it should set the callbacks t->tsnap.calcSnap and t->tsnap.targetSnap.
The existing custom sequencer snap code itself looks like it should also just be in the mode it's used for. (That was a slip at the time of splitting all the transform code into multiple files).
(I should have noticed this before)

This snapping probably can be shared with extend feature. What is convention then - to have separate file for snapping functions? Or reuse snapping from different mode, but snapping functions should be defined in file for one of transform modes?
I will check how t->tsnap.calcSnap and t->tsnap.targetSnap works too.

  • Use nicer UI, refactor transform code as suggested.
  • Use seq_slide_snap prefix

Here are the changes I made in the patch to make the transform code more integrated:

1diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
2index c94e0915730..8aff0b7055c 100644
3--- a/release/scripts/startup/bl_ui/space_sequencer.py
4+++ b/release/scripts/startup/bl_ui/space_sequencer.py
5@@ -163,9 +163,9 @@ class SEQUENCER_HT_header(Header):
6 row.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
7
8 if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
9- tool_settings = context.tool_settings.sequencer_tool_settings
10+ tool_settings = context.tool_settings
11 row = layout.row(align=True)
12- row.prop(tool_settings, "use_snapping", text="")
13+ row.prop(tool_settings, "use_snap_sequencer", text="")
14 sub = row.row(align=True)
15 sub.popover(panel="SEQUENCER_PT_snapping")
16 layout.separator_spacer()
17@@ -2278,22 +2278,22 @@ class SEQUENCER_PT_snapping(Panel):
18 bl_label = ""
19
20 def draw(self, context):
21- tool_settings = context.tool_settings.sequencer_tool_settings
22+ tool_settings = context.tool_settings
23+ sequencer_tool_settings = tool_settings.sequencer_tool_settings
24
25 layout = self.layout
26 layout.use_property_split = True
27 layout.use_property_decorate = False
28
29 col = layout.column(heading="Snap to", align=True)
30- col.prop(tool_settings, "snap_to_strip_hold_offset", text="Hold Offset")
31- col.prop(tool_settings, "snap_to_playhead", text="Playhead")
32+ col.prop(sequencer_tool_settings, "snap_seq_element", expand=True)
33
34 col = layout.column(heading="Ignore", align=True)
35 col.prop(tool_settings, "snap_ignore_muted", text="Muted Strips")
36 col.prop(tool_settings, "snap_ignore_sound",text="Sound Strips")
37
38 col = layout.column()
39- col.prop(tool_settings, "snap_distance", slider=True, text="Distance")
40+ col.prop(sequencer_tool_settings, "snap_distance", slider=True, text="Distance")
41
42
43 classes = (
44diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
45index f4ac76425fb..55ac9f86107 100644
46--- a/source/blender/blenloader/intern/versioning_300.c
47+++ b/source/blender/blenloader/intern/versioning_300.c
48@@ -444,9 +444,33 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
49 {
50 /* Keep this block, even when empty. */
51 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
52- SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
53- tool_settings->snap_flag = SEQ_USE_SNAPPING | SEQ_SNAP_TO_PLAYHEAD | SEQ_SNAP_TO_STRIP_HOLD;
54- tool_settings->snap_distance = 15;
55+ ToolSettings *tool_settings = scene->toolsettings;
56+ if (tool_settings->snap_flag & SCE_SNAP) {
57+ tool_settings->snap_flag |= SCE_SNAP_SEQ;
58+ }
59+ short snap_mode = tool_settings->snap_mode;
60+ short snap_node_mode = tool_settings->snap_node_mode;
61+ tool_settings->snap_mode &= ~((1 << 4) | (1 << 5) | (1 << 6));
62+ tool_settings->snap_node_mode &= ~((1 << 5) | (1 << 6));
63+ if (snap_mode & (1 << 4)) {
64+ tool_settings->snap_mode |= (1 << 6); /* SCE_SNAP_MODE_INCREMENT */
65+ }
66+ if (snap_mode & (1 << 5)) {
67+ tool_settings->snap_mode |= (1 << 4); /* SCE_SNAP_MODE_EDGE_MIDPOINT */
68+ }
69+ if (snap_mode & (1 << 6)) {
70+ tool_settings->snap_mode |= (1 << 5); /* SCE_SNAP_MODE_EDGE_PERPENDICULAR */
71+ }
72+ if (snap_node_mode & (1 << 5)) {
73+ tool_settings->snap_node_mode |= (1 << 0); /* SCE_SNAP_MODE_NODE_X */
74+ }
75+ if (snap_node_mode & (1 << 6)) {
76+ tool_settings->snap_node_mode |= (1 << 1); /* SCE_SNAP_MODE_NODE_Y */
77+ }
78+
79+ SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene);
80+ sequencer_tool_settings->snap_flag = SEQ_SNAP_TO_PLAYHEAD | SEQ_SNAP_TO_STRIP_HOLD;
81+ sequencer_tool_settings->snap_distance = 15;
82 }
83 }
84 }
85diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
86index b0bc5c6abda..ad0a330f0f4 100644
87--- a/source/blender/editors/transform/CMakeLists.txt
88+++ b/source/blender/editors/transform/CMakeLists.txt
89@@ -102,6 +102,7 @@ set(SRC
90 transform_orientations.c
91 transform_snap.c
92 transform_snap_object.c
93+ transform_snap_sequencer.c
94
95 transform.h
96 transform_constraints.h
97diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
98index 2df0d86d02b..cf45f38da3a 100644
99--- a/source/blender/editors/transform/transform.h
100+++ b/source/blender/editors/transform/transform.h
101@@ -335,7 +335,10 @@ typedef struct TransSnap {
102 /**
103 * Re-usable snap context data.
104 */
105- struct SnapObjectContext *object_context;
106+ union {
107+ struct SnapObjectContext *object_context;
108+ struct TransSeqSnapData *seq_context;
109+ };
110 } TransSnap;
111
112 typedef struct TransCon {
113diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
114index f6d45e48b6d..960fa0b5909 100644
115--- a/source/blender/editors/transform/transform_convert.h
116+++ b/source/blender/editors/transform/transform_convert.h
117@@ -49,7 +49,7 @@ void clipUVData(TransInfo *t);
118 void transform_convert_mesh_customdatacorrect_init(TransInfo *t);
119
120 /* transform_convert_sequencer.c */
121-void transform_convert_sequencer_channel_clamp(TransInfo *t);
122+void transform_convert_sequencer_channel_clamp(TransInfo *t, float r_val[2]);
123
124 /********************* intern **********************/
125
126diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
127index eb93a7af112..e5ade925bb2 100644
128--- a/source/blender/editors/transform/transform_convert_sequencer.c
129+++ b/source/blender/editors/transform/transform_convert_sequencer.c
130@@ -672,18 +672,18 @@ void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t)
131 }
132 }
133
134-void transform_convert_sequencer_channel_clamp(TransInfo *t)
135+void transform_convert_sequencer_channel_clamp(TransInfo *t, float r_val[2])
136 {
137 const TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
138- const int channel_offset = round_fl_to_int(t->values[1]);
139+ const int channel_offset = round_fl_to_int(r_val[1]);
140 const int min_channel_after_transform = ts->selection_channel_range_min + channel_offset;
141 const int max_channel_after_transform = ts->selection_channel_range_max + channel_offset;
142
143 if (max_channel_after_transform > MAXSEQ) {
144- t->values[1] -= max_channel_after_transform - MAXSEQ;
145+ r_val[1] -= max_channel_after_transform - MAXSEQ;
146 }
147 if (min_channel_after_transform < 1) {
148- t->values[1] -= min_channel_after_transform - 1;
149+ r_val[1] -= min_channel_after_transform - 1;
150 }
151 }
152
153diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
154index ebf13c445cf..d13be81544b 100644
155--- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c
156+++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
157@@ -48,6 +48,7 @@
158 #include "transform.h"
159 #include "transform_convert.h"
160 #include "transform_mode.h"
161+#include "transform_snap.h"
162
163 typedef struct TransSeqSnapData {
164 int *source_snap_points;
165@@ -70,7 +71,8 @@ static int seq_slide_snap_source_points_count(SeqCollection *snap_sources)
166 return SEQ_collection_count(snap_sources) * 2;
167 }
168
169-static void seq_slide_snap_source_points_alloc(TransSeqSnapData *snap_data, SeqCollection *snap_sources)
170+static void seq_slide_snap_source_points_alloc(TransSeqSnapData *snap_data,
171+ SeqCollection *snap_sources)
172 {
173 const size_t point_count = seq_slide_snap_source_points_count(snap_sources);
174 snap_data->source_snap_points = MEM_callocN(sizeof(int) * point_count, __func__);
175@@ -84,8 +86,8 @@ static int cmp_fn(const void *a, const void *b)
176 }
177
178 static void seq_slide_snap_source_points_build(const TransInfo *t,
179- TransSeqSnapData *snap_data,
180- SeqCollection *snap_sources)
181+ TransSeqSnapData *snap_data,
182+ SeqCollection *snap_sources)
183 {
184 int i = 0;
185 Sequence *seq;
186@@ -139,8 +141,8 @@ static SeqCollection *seq_slide_snap_targets_query(const TransInfo *t)
187 }
188
189 static int seq_slide_snap_target_points_count(const TransInfo *t,
190- TransSeqSnapData *snap_data,
191- SeqCollection *snap_targets)
192+ TransSeqSnapData *snap_data,
193+ SeqCollection *snap_targets)
194 {
195 const eSeqSnapFlag snap_flag = SEQ_tool_settings_snap_flag_get(t->scene);
196
197@@ -160,8 +162,8 @@ static int seq_slide_snap_target_points_count(const TransInfo *t,
198 }
199
200 static void seq_slide_snap_target_points_alloc(const TransInfo *t,
201- TransSeqSnapData *snap_data,
202- SeqCollection *snap_targets)
203+ TransSeqSnapData *snap_data,
204+ SeqCollection *snap_targets)
205 {
206 const size_t point_count = seq_slide_snap_target_points_count(t, snap_data, snap_targets);
207 snap_data->target_snap_points = MEM_callocN(sizeof(int) * point_count, __func__);
208@@ -170,8 +172,8 @@ static void seq_slide_snap_target_points_alloc(const TransInfo *t,
209 }
210
211 static void seq_slide_snap_target_points_build(const TransInfo *t,
212- TransSeqSnapData *snap_data,
213- SeqCollection *snap_targets)
214+ TransSeqSnapData *snap_data,
215+ SeqCollection *snap_targets)
216 {
217 const Scene *scene = t->scene;
218 const eSeqSnapFlag snap_flag = SEQ_tool_settings_snap_flag_get(t->scene);
219@@ -363,8 +365,6 @@ static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2]))
220 char str[UI_MAX_DRAW_STR];
221 float values_final[3] = {0.0f};
222
223- seq_slide_snap_sequencer_apply(t);
224- transform_convert_sequencer_channel_clamp(t);
225 if (applyNumInput(&t->num, values_final)) {
226 if (t->con.mode & CON_APPLY) {
227 if (t->con.mode & CON_AXIS0) {
228@@ -375,11 +375,14 @@ static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2]))
229 }
230 }
231 }
232- else if (t->con.mode & CON_APPLY) {
233- t->con.applyVec(t, NULL, NULL, t->values, values_final);
234- }
235 else {
236 copy_v2_v2(values_final, t->values);
237+ applySnapping(t, values_final);
238+ transform_convert_sequencer_channel_clamp(t, values_final);
239+
240+ if (t->con.mode & CON_APPLY) {
241+ t->con.applyVec(t, NULL, NULL, t->values, values_final);
242+ }
243 }
244
245 values_final[0] = floorf(values_final[0] + 0.5f);
246diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
247index 975c86f9dc8..4b1bafef604 100644
248--- a/source/blender/editors/transform/transform_snap.c
249+++ b/source/blender/editors/transform/transform_snap.c
250@@ -52,6 +52,10 @@
251 #include "UI_resources.h"
252 #include "UI_view2d.h"
253
254+#include "SEQ_iterator.h"
255+#include "SEQ_sequencer.h"
256+#include "SEQ_time.h"
257+
258 #include "MEM_guardedalloc.h"
259
260 #include "transform.h"
261@@ -161,10 +165,7 @@ bool transformModeUseSnap(const TransInfo *t)
262 if (t->mode == TFM_RESIZE) {
263 return (ts->snap_transform_mode_flag & SCE_SNAP_TRANSFORM_MODE_SCALE) != 0;
264 }
265- if (t->mode == TFM_VERT_SLIDE) {
266- return true;
267- }
268- if (t->mode == TFM_EDGE_SLIDE) {
269+ if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE, TFM_SEQ_SLIDE)) {
270 return true;
271 }
272
273@@ -473,11 +474,14 @@ void applySnapping(TransInfo *t, float *vec)
274 /* TODO: add exception for object mode, no need to slow it down then. */
275 if (current - t->tsnap.last >= 0.01) {
276 t->tsnap.calcSnap(t, vec);
277- t->tsnap.targetSnap(t);
278-
279- t->tsnap.last = current;
280+ if (t->tsnap.targetSnap) {
281+ t->tsnap.targetSnap(t);
282+ }
283 }
284- if (validSnap(t)) {
285+
286+ t->tsnap.last = current;
287+
288+ if (t->tsnap.applySnap && validSnap(t)) {
289 t->tsnap.applySnap(t, vec);
290 }
291 }
292@@ -564,6 +568,9 @@ static void initSnappingMode(TransInfo *t)
293
294 t->tsnap.mode = ts->snap_uv_mode;
295 }
296+ else if (t->spacetype == SPACE_SEQ) {
297+ t->tsnap.mode = SEQ_tool_settings_snap_flag_get(t->scene);
298+ }
299 else {
300 /* force project off when not supported */
301 if ((ts->snap_mode & SCE_SNAP_MODE_FACE) == 0) {
302@@ -623,16 +630,12 @@ static void initSnappingMode(TransInfo *t)
303 t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
304 }
305 }
306- else if (t->spacetype == SPACE_NODE) {
307+ else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
308 setSnappingCallback(t);
309 t->tsnap.modeSelect = SNAP_NOT_SELECTED;
310 }
311- else if (t->spacetype == SPACE_SEQ) {
312- /* We do our own snapping currently, so nothing here */
313- t->tsnap.mode = SCE_SNAP_MODE_GRID; /* Dummy, should we rather add a NOP mode? */
314- }
315 else {
316- /* Always increment outside of 3D view */
317+ /* Fallback. */
318 t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
319 }
320
321@@ -653,6 +656,11 @@ static void initSnappingMode(TransInfo *t)
322 }
323 }
324 }
325+ else if (t->spacetype == SPACE_SEQ) {
326+ if (t->tsnap.seq_context == NULL) {
327+ t->tsnap.seq_context = transform_snap_sequencer_data_alloc(t);
328+ }
329+ }
330 }
331
332 void initSnapping(TransInfo *t, wmOperator *op)
333@@ -705,6 +713,9 @@ void initSnapping(TransInfo *t, wmOperator *op)
334 t->tsnap.snap_self = !((t->settings->snap_flag & SCE_SNAP_NO_SELF) != 0);
335 t->tsnap.peel = ((t->settings->snap_flag & SCE_SNAP_PROJECT) != 0);
336 }
337+ else if ((t->spacetype == SPACE_SEQ) && (ts->snap_flag & SCE_SNAP_SEQ)) {
338+ t->modifiers |= MOD_SNAP;
339+ }
340 }
341
342 t->tsnap.target = snap_target;
343@@ -714,7 +725,11 @@ void initSnapping(TransInfo *t, wmOperator *op)
344
345 void freeSnapping(TransInfo *t)
346 {
347- if (t->tsnap.object_context) {
348+ if ((t->spacetype == SPACE_SEQ) && t->tsnap.seq_context) {
349+ transform_snap_sequencer_data_free(t->tsnap.seq_context);
350+ t->tsnap.seq_context = NULL;
351+ }
352+ else if (t->tsnap.object_context) {
353 ED_transform_snap_object_context_destroy(t->tsnap.object_context);
354 t->tsnap.object_context = NULL;
355 }
356@@ -724,6 +739,11 @@ static void setSnappingCallback(TransInfo *t)
357 {
358 t->tsnap.calcSnap = CalcSnapGeometry;
359
360+ if (t->spacetype == SPACE_SEQ) {
361+ /* The target is calculated along with the snap point. */
362+ return;
363+ }
364+
365 switch (t->tsnap.target) {
366 case SCE_SNAP_TARGET_CLOSEST:
367 t->tsnap.targetSnap = TargetSnapClosest;
368@@ -846,7 +866,7 @@ void getSnapPoint(const TransInfo *t, float vec[3])
369 /** \name Calc Snap (Generic)
370 * \{ */
371
372-static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
373+static void CalcSnapGeometry(TransInfo *t, float *vec)
374 {
375 if (t->spacetype == SPACE_VIEW3D) {
376 float loc[3];
377@@ -927,6 +947,14 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
378 }
379 }
380 }
381+ else if (t->spacetype == SPACE_SEQ) {
382+ if (transform_snap_sequencer_apply(t, vec)) {
383+ t->tsnap.status |= POINT_INIT;
384+ }
385+ else {
386+ t->tsnap.status &= ~POINT_INIT;
387+ }
388+ }
389 }
390
391 /** \} */
392diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
393index 6c6f5a3ee12..47957ba853a 100644
394--- a/source/blender/editors/transform/transform_snap.h
395+++ b/source/blender/editors/transform/transform_snap.h
396@@ -82,7 +82,6 @@ void removeSnapPoint(TransInfo *t);
397 float transform_snap_distance_len_squared_fn(TransInfo *t, const float p1[3], const float p2[3]);
398
399 /* transform_snap_sequencer.c */
400-struct TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t,
401- struct ListBase *seqbase);
402+struct TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t);
403 void transform_snap_sequencer_data_free(struct TransSeqSnapData *data);
404-void transform_snap_sequencer_apply(TransInfo *t);
405+bool transform_snap_sequencer_apply(struct TransInfo *t, float *vec);
406diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c
407new file mode 100644
408index 00000000000..9ad94f1d2a5
409--- /dev/null
410+++ b/source/blender/editors/transform/transform_snap_sequencer.c
411@@ -0,0 +1,264 @@
412+/*
413+ * This program is free software; you can redistribute it and/or
414+ * modify it under the terms of the GNU General Public License
415+ * as published by the Free Software Foundation; either version 2
416+ * of the License, or (at your option) any later version.
417+ *
418+ * This program is distributed in the hope that it will be useful,
419+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
420+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
421+ * GNU General Public License for more details.
422+ *
423+ * You should have received a copy of the GNU General Public License
424+ * along with this program; if not, write to the Free Software Foundation,
425+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
426+ *
427+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
428+ * All rights reserved.
429+ */
430+
431+/** \file
432+ * \ingroup edtransform
433+ */
434+
435+#include <stdlib.h>
436+
437+#include "MEM_guardedalloc.h"
438+
439+#include "BLI_blenlib.h"
440+#include "BLI_math.h"
441+
442+#include "BKE_context.h"
443+
444+#include "ED_screen.h"
445+
446+#include "UI_view2d.h"
447+
448+#include "SEQ_iterator.h"
449+#include "SEQ_sequencer.h"
450+
451+#include "transform.h"
452+#include "transform_snap.h"
453+
454+typedef struct TransSeqSnapData {
455+ int *source_snap_points;
456+ int source_snap_point_count;
457+ int *target_snap_points;
458+ int target_snap_point_count;
459+} TransSeqSnapData;
460+
461+/* -------------------------------------------------------------------- */
462+/** \name Snap sources
463+ * \{ */
464+
465+static int seq_get_snap_source_points_count(SeqCollection *snap_sources)
466+{
467+ return SEQ_collection_count(snap_sources) * 2;
468+}
469+
470+static void seq_snap_source_points_alloc(TransSeqSnapData *snap_data, SeqCollection *snap_sources)
471+{
472+ const size_t point_count = seq_get_snap_source_points_count(snap_sources);
473+ snap_data->source_snap_points = MEM_callocN(sizeof(int) * point_count, __func__);
474+ memset(snap_data->source_snap_points, 0, sizeof(int));
475+ snap_data->source_snap_point_count = point_count;
476+}
477+
478+static int cmp_fn(const void *a, const void *b)
479+{
480+ return (*(int *)a - *(int *)b);
481+}
482+
483+static void seq_snap_source_points_build(const TransInfo *t,
484+ TransSeqSnapData *snap_data,
485+ SeqCollection *snap_sources)
486+{
487+ int i = 0;
488+ Sequence *seq;
489+ SEQ_ITERATOR_FOREACH (seq, snap_sources) {
490+ int left = 0, right = 0;
491+ if (seq->flag & SEQ_LEFTSEL) {
492+ left = right = seq->startdisp;
493+ }
494+ else if (seq->flag & SEQ_RIGHTSEL) {
495+ left = right = seq->enddisp;
496+ }
497+ else {
498+ left = seq->startdisp;
499+ right = seq->enddisp;
500+ }
501+
502+ snap_data->source_snap_points[i] = left;
503+ snap_data->source_snap_points[i + 1] = right;
504+ i += 2;
505+ BLI_assert(i <= snap_data->source_snap_point_count);
506+ }
507+
508+ qsort(snap_data->source_snap_points, snap_data->source_snap_point_count, sizeof(int), cmp_fn);
509+}
510+
511+/** \} */
512+
513+/* -------------------------------------------------------------------- */
514+/** \name Snap targets
515+ * \{ */
516+
517+static SeqCollection *query_snap_targets(const TransInfo *t)
518+{
519+ const ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false));
520+ const short snap_flag = t->tsnap.mode;
521+
522+ SeqCollection *collection = SEQ_collection_create();
523+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
524+ if ((seq->flag & SELECT)) {
525+ continue; /* Selected are being transformed. */
526+ }
527+ if ((seq->flag & SEQ_MUTE) && (snap_flag & SCE_SNAP_SEQ_IGNORE_MUTED)) {
528+ continue;
529+ }
530+ if (seq->type == SEQ_TYPE_SOUND_RAM && (snap_flag & SCE_SNAP_SEQ_IGNORE_SOUND)) {
531+ continue;
532+ }
533+ SEQ_collection_append_strip(seq, collection);
534+ }
535+ return collection;
536+}
537+
538+static int seq_get_snap_target_points_count(const TransInfo *t,
539+ TransSeqSnapData *snap_data,
540+ SeqCollection *snap_targets)
541+{
542+ const short snap_flag = t->tsnap.mode;
543+
544+ int count = 2; /* Strip start and end are always used. */
545+
546+ if (snap_flag & SEQ_SNAP_TO_STRIP_HOLD) {
547+ count += 2;
548+ }
549+
550+ count *= SEQ_collection_count(snap_targets);
551+
552+ if (snap_flag & SEQ_SNAP_TO_PLAYHEAD) {
553+ count++;
554+ }
555+
556+ return count;
557+}
558+
559+static void seq_snap_target_points_alloc(const TransInfo *t,
560+ TransSeqSnapData *snap_data,
561+ SeqCollection *snap_targets)
562+{
563+ const size_t point_count = seq_get_snap_target_points_count(t, snap_data, snap_targets);
564+ snap_data->target_snap_points = MEM_callocN(sizeof(int) * point_count, __func__);
565+ memset(snap_data->target_snap_points, 0, sizeof(int));
566+ snap_data->target_snap_point_count = point_count;
567+}
568+
569+static void seq_snap_target_points_build(const TransInfo *t,
570+ TransSeqSnapData *snap_data,
571+ SeqCollection *snap_targets)
572+{
573+ const Scene *scene = t->scene;
574+ const short snap_flag = t->tsnap.mode;
575+
576+ int i = 0;
577+
578+ if (snap_flag & SEQ_SNAP_TO_PLAYHEAD) {
579+ snap_data->target_snap_points[i] = CFRA;
580+ i++;
581+ }
582+
583+ Sequence *seq;
584+ SEQ_ITERATOR_FOREACH (seq, snap_targets) {
585+ snap_data->target_snap_points[i] = seq->startdisp;
586+ snap_data->target_snap_points[i + 1] = seq->enddisp;
587+ i += 2;
588+
589+ if (snap_flag & SEQ_SNAP_TO_STRIP_HOLD) {
590+ int content_start = min_ii(seq->enddisp, seq->start);
591+ int content_end = max_ii(seq->startdisp, seq->start + seq->len);
592+ if (seq->anim_startofs == 0) {
593+ content_start = seq->startdisp;
594+ }
595+ if (seq->anim_endofs == 0) {
596+ content_end = seq->enddisp;
597+ }
598+ snap_data->target_snap_points[i] = content_start;
599+ snap_data->target_snap_points[i + 1] = content_end;
600+ i += 2;
601+ }
602+ }
603+ BLI_assert(i <= snap_data->target_snap_point_count);
604+ qsort(snap_data->target_snap_points, snap_data->target_snap_point_count, sizeof(int), cmp_fn);
605+}
606+
607+/** \} */
608+
609+/* -------------------------------------------------------------------- */
610+/** \name Snap utilities
611+ * \{ */
612+
613+static int seq_snap_threshold_get_frame_distance(const TransInfo *t)
614+{
615+ const int snap_distance = SEQ_tool_settings_snap_distance_get(t->scene);
616+ const struct View2D *v2d = &t->region->v2d;
617+ return round_fl_to_int(UI_view2d_region_to_view_x(v2d, snap_distance) -
618+ UI_view2d_region_to_view_x(v2d, 0));
619+}
620+
621+/** \} */
622+
623+TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t)
624+{
625+ TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__);
626+ ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false));
627+
628+ /* Build arrays of snap points. */
629+ SeqCollection *snap_sources = SEQ_query_selected_strips(seqbase);
630+ seq_snap_source_points_alloc(snap_data, snap_sources);
631+ seq_snap_source_points_build(t, snap_data, snap_sources);
632+ SEQ_collection_free(snap_sources);
633+
634+ SeqCollection *snap_targets = query_snap_targets(t);
635+ seq_snap_target_points_alloc(t, snap_data, snap_targets);
636+ seq_snap_target_points_build(t, snap_data, snap_targets);
637+ SEQ_collection_free(snap_targets);
638+ return snap_data;
639+}
640+
641+void transform_snap_sequencer_data_free(TransSeqSnapData *data)
642+{
643+ MEM_freeN(data->source_snap_points);
644+ MEM_freeN(data->target_snap_points);
645+ MEM_freeN(data);
646+}
647+
648+bool transform_snap_sequencer_apply(TransInfo *t, float *vec)
649+{
650+ const TransSeqSnapData *snap_data = t->tsnap.seq_context;
651+ int best_dist = MAXFRAME, best_target_frame = 0, best_source_frame = 0;
652+
653+ for (int i = 0; i < snap_data->source_snap_point_count; i++) {
654+ int snap_source_frame = snap_data->source_snap_points[i] + round_fl_to_int(t->values[0]);
655+ for (int j = 0; j < snap_data->target_snap_point_count; j++) {
656+ int snap_target_frame = snap_data->target_snap_points[j];
657+
658+ int dist = abs(snap_target_frame - snap_source_frame);
659+ if (dist > best_dist) {
660+ continue;
661+ }
662+
663+ best_dist = dist;
664+ best_target_frame = snap_target_frame;
665+ best_source_frame = snap_source_frame;
666+ }
667+ }
668+
669+ if (best_dist > seq_snap_threshold_get_frame_distance(t)) {
670+ return false;
671+ }
672+
673+ *vec += best_target_frame - best_source_frame;
674+ return true;
675+}
676diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
677index 283b7362033..31694864fce 100644
678--- a/source/blender/makesdna/DNA_scene_types.h
679+++ b/source/blender/makesdna/DNA_scene_types.h
680@@ -483,7 +483,7 @@ typedef struct ImageFormatData {
681 #define R_IMF_IMTYPE_INVALID 255
682
683 /** #ImageFormatData.flag */
684-#define R_IMF_FLAG_ZBUF (1 << 0) /* was R_OPENEXR_ZBUF */
685+#define R_IMF_FLAG_ZBUF (1 << 0) /* was R_OPENEXR_ZBUF */
686 #define R_IMF_FLAG_PREVIEW_JPG (1 << 1) /* was R_PREVIEW_JPG */
687
688 /* Return values from #BKE_imtype_valid_depths, note this is depths per channel. */
689@@ -525,8 +525,8 @@ typedef enum eImageFormatDepth {
690
691 /** #ImageFormatData.jp2_flag */
692 #define R_IMF_JP2_FLAG_YCC (1 << 0) /* when disabled use RGB */ /* was R_JPEG2K_YCC */
693-#define R_IMF_JP2_FLAG_CINE_PRESET (1 << 1) /* was R_JPEG2K_CINE_PRESET */
694-#define R_IMF_JP2_FLAG_CINE_48 (1 << 2) /* was R_JPEG2K_CINE_48FPS */
695+#define R_IMF_JP2_FLAG_CINE_PRESET (1 << 1) /* was R_JPEG2K_CINE_PRESET */
696+#define R_IMF_JP2_FLAG_CINE_48 (1 << 2) /* was R_JPEG2K_CINE_48FPS */
697
698 /** #ImageFormatData.jp2_codec */
699 #define R_IMF_JP2_CODEC_JP2 0
700@@ -1336,7 +1336,6 @@ typedef struct MeshStatVis {
701 typedef struct SequencerToolSettings {
702 /* eSeqImageFitMethod */
703 int fit_method;
704- /*eSeqSnapFlag */
705 int snap_flag;
706 /** When there are many snap points, 0-1 range corresponds to resolution from boundbox to all
707 * possible snap points. */
708@@ -1849,12 +1848,12 @@ typedef struct Scene {
709
710 #define R_MODE_UNUSED_20 (1 << 20) /* cleared */
711 #define R_MODE_UNUSED_21 (1 << 21) /* cleared */
712-#define R_NO_OVERWRITE (1 << 22) /* skip existing files */
713-#define R_TOUCH (1 << 23) /* touch files before rendering */
714+#define R_NO_OVERWRITE (1 << 22) /* skip existing files */
715+#define R_TOUCH (1 << 23) /* touch files before rendering */
716 #define R_SIMPLIFY (1 << 24)
717-#define R_EDGE_FRS (1 << 25) /* R_EDGE reserved for Freestyle */
718+#define R_EDGE_FRS (1 << 25) /* R_EDGE reserved for Freestyle */
719 #define R_PERSISTENT_DATA (1 << 26) /* keep data around for re-render */
720-#define R_MODE_UNUSED_27 (1 << 27) /* cleared */
721+#define R_MODE_UNUSED_27 (1 << 27) /* cleared */
722
723 /** #RenderData.seq_flag */
724 enum {
725@@ -2052,6 +2051,9 @@ enum {
726 #define SCE_SNAP_NO_SELF (1 << 4)
727 #define SCE_SNAP_ABS_GRID (1 << 5)
728 #define SCE_SNAP_BACKFACE_CULLING (1 << 6)
729+#define SCE_SNAP_SEQ (1 << 7)
730+#define SCE_SNAP_SEQ_IGNORE_MUTED (1 << 8)
731+#define SCE_SNAP_SEQ_IGNORE_SOUND (1 << 9)
732
733 /** #ToolSettings.snap_target */
734 #define SCE_SNAP_TARGET_CLOSEST 0
735@@ -2064,15 +2066,23 @@ enum {
736 #define SCE_SNAP_MODE_EDGE (1 << 1)
737 #define SCE_SNAP_MODE_FACE (1 << 2)
738 #define SCE_SNAP_MODE_VOLUME (1 << 3)
739-#define SCE_SNAP_MODE_INCREMENT (1 << 4)
740-#define SCE_SNAP_MODE_EDGE_MIDPOINT (1 << 5)
741-#define SCE_SNAP_MODE_EDGE_PERPENDICULAR (1 << 6)
742+#define SCE_SNAP_MODE_EDGE_MIDPOINT (1 << 4)
743+#define SCE_SNAP_MODE_EDGE_PERPENDICULAR (1 << 5)
744+
745+/** #SequencerToolSettings.snap_flag */
746+#define SEQ_SNAP_TO_PLAYHEAD (1 << 0)
747+#define SEQ_SNAP_TO_STRIP_HOLD (1 << 1)
748
749 /** #ToolSettings.snap_node_mode */
750-#define SCE_SNAP_MODE_NODE_X (1 << 5)
751-#define SCE_SNAP_MODE_NODE_Y (1 << 6)
752+#define SCE_SNAP_MODE_NODE_X (1 << 0)
753+#define SCE_SNAP_MODE_NODE_Y (1 << 1)
754
755-/** #ToolSettings.snap_mode and #ToolSettings.snap_node_mode */
756+/**
757+ * #ToolSettings.snap_mode,
758+ * #ToolSettings.snap_node_mode and
759+ * #SequencerToolSettings.snap_flag
760+ */
761+#define SCE_SNAP_MODE_INCREMENT (1 << 6)
762 #define SCE_SNAP_MODE_GRID (1 << 7)
763
764 /** #ToolSettings.snap_transform_mode_flag */
765diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
766index b2407cbbe27..51d17c8df90 100644
767--- a/source/blender/makesrna/intern/rna_scene.c
768+++ b/source/blender/makesrna/intern/rna_scene.c
769@@ -190,6 +190,12 @@ const EnumPropertyItem rna_enum_snap_node_element_items[] = {
770 {0, NULL, 0, NULL, NULL},
771 };
772
773+const EnumPropertyItem rna_enum_snap_seq_element_items[] = {
774+ {SEQ_SNAP_TO_PLAYHEAD, "PLAYHEAD", ICON_NONE, "Playhead", "Snap to current frame"},
775+ {SEQ_SNAP_TO_STRIP_HOLD, "STRIP_HOLD", ICON_NONE, "Hold Offset", "Snap to strip hold offset"},
776+ {0, NULL, 0, NULL, NULL},
777+};
778+
779 #ifndef RNA_RUNTIME
780 static const EnumPropertyItem snap_uv_element_items[] = {
781 {SCE_SNAP_MODE_INCREMENT,
782@@ -3111,6 +3117,22 @@ static void rna_def_tool_settings(BlenderRNA *brna)
783 "Absolute grid alignment while translating (based on the pivot center)");
784 RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
785
786+ prop = RNA_def_property(srna, "use_snap_sequencer", PROP_BOOLEAN, PROP_NONE);
787+ RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_SEQ);
788+ RNA_def_property_ui_text(prop, "Use Snapping", "Snap to strip edges or current frame");
789+ RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1);
790+ RNA_def_property_boolean_default(prop, true);
791+
792+ prop = RNA_def_property(srna, "snap_ignore_muted", PROP_BOOLEAN, PROP_NONE);
793+ RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_SEQ_IGNORE_MUTED);
794+ RNA_def_property_boolean_default(prop, true);
795+ RNA_def_property_ui_text(prop, "Ignore Muted Strips", "Don't snap to hidden strips");
796+
797+ prop = RNA_def_property(srna, "snap_ignore_sound", PROP_BOOLEAN, PROP_NONE);
798+ RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_SEQ_IGNORE_SOUND);
799+ RNA_def_property_boolean_default(prop, true);
800+ RNA_def_property_ui_text(prop, "Ignore Sound Strips", "Don't snap to sound strips");
801+
802 prop = RNA_def_property(srna, "snap_elements", PROP_ENUM, PROP_NONE);
803 RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_mode");
804 RNA_def_property_enum_items(prop, rna_enum_snap_element_items);
805@@ -3130,8 +3152,6 @@ static void rna_def_tool_settings(BlenderRNA *brna)
806 prop = RNA_def_property(srna, "snap_uv_element", PROP_ENUM, PROP_NONE);
807 RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_uv_mode");
808 RNA_def_property_enum_items(prop, snap_uv_element_items);
809- RNA_def_property_ui_text(prop, "Snap UV Element", "Type of element to snap to");
810- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
811
812 prop = RNA_def_property(srna, "snap_target", PROP_ENUM, PROP_NONE);
813 RNA_def_property_enum_sdna(prop, NULL, "snap_target");
814@@ -3511,31 +3531,14 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna)
815 RNA_def_property_ui_text(prop, "Fit Method", "Scale fit method");
816
817 /* Transform snapping. */
818- prop = RNA_def_property(srna, "use_snapping", PROP_BOOLEAN, PROP_NONE);
819- RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SEQ_USE_SNAPPING);
820- RNA_def_property_boolean_default(prop, true);
821- RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1);
822- RNA_def_property_ui_text(prop, "Use Snapping", "Snap to strip edges or current frame");
823-
824- prop = RNA_def_property(srna, "snap_to_strip_hold_offset", PROP_BOOLEAN, PROP_NONE);
825- RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SEQ_SNAP_TO_STRIP_HOLD);
826- RNA_def_property_boolean_default(prop, true);
827- RNA_def_property_ui_text(prop, "Snap to Hold Offset", "Snap to strip hold offset");
828-
829- prop = RNA_def_property(srna, "snap_to_playhead", PROP_BOOLEAN, PROP_NONE);
830- RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SEQ_SNAP_TO_PLAYHEAD);
831- RNA_def_property_boolean_default(prop, true);
832- RNA_def_property_ui_text(prop, "Snap to Playhead", "Snap to current frame");
833-
834- prop = RNA_def_property(srna, "snap_ignore_muted", PROP_BOOLEAN, PROP_NONE);
835- RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SEQ_SNAP_IGNORE_MUTED);
836- RNA_def_property_boolean_default(prop, true);
837- RNA_def_property_ui_text(prop, "Ignore Muted Strips", "Don't snap to hidden strips");
838
839- prop = RNA_def_property(srna, "snap_ignore_sound", PROP_BOOLEAN, PROP_NONE);
840- RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SEQ_SNAP_IGNORE_SOUND);
841- RNA_def_property_boolean_default(prop, true);
842- RNA_def_property_ui_text(prop, "Ignore Sound Strips", "Don't snap to sound strips");
843+ /* Sequencer editor uses own set of snap modes */
844+ prop = RNA_def_property(srna, "snap_seq_element", PROP_ENUM, PROP_NONE);
845+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_flag");
846+ RNA_def_property_enum_items(prop, rna_enum_snap_seq_element_items);
847+ RNA_def_property_ui_text(prop, "Snap Sequencer Element", "Type of element to snap to");
848+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
849+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
850
851 prop = RNA_def_property(srna, "snap_distance", PROP_INT, PROP_PIXEL);
852 RNA_def_property_int_sdna(prop, NULL, "snap_distance");
853diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
854index 5fe68850260..8322ecf9384 100644
855--- a/source/blender/sequencer/SEQ_sequencer.h
856+++ b/source/blender/sequencer/SEQ_sequencer.h
857@@ -55,7 +55,7 @@ struct SequencerToolSettings *SEQ_tool_settings_ensure(struct Scene *scene);
858 void SEQ_tool_settings_free(struct SequencerToolSettings *tool_settings);
859 eSeqImageFitMethod SEQ_tool_settings_fit_method_get(struct Scene *scene);
860 void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fit_method);
861-eSeqSnapFlag SEQ_tool_settings_snap_flag_get(struct Scene *scene);
862+short SEQ_tool_settings_snap_flag_get(struct Scene *scene);
863 int SEQ_tool_settings_snap_distance_get(struct Scene *scene);
864 struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings);
865 struct Editing *SEQ_editing_get(struct Scene *scene, bool alloc);
866diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
867index a0a9a240eef..5075dab78af 100644
868--- a/source/blender/sequencer/intern/sequencer.c
869+++ b/source/blender/sequencer/intern/sequencer.c
870@@ -311,7 +311,7 @@ SequencerToolSettings *SEQ_tool_settings_init(void)
871 SequencerToolSettings *tool_settings = MEM_callocN(sizeof(SequencerToolSettings),
872 "Sequencer tool settings");
873 tool_settings->fit_method = SEQ_SCALE_TO_FIT;
874- tool_settings->snap_flag = SEQ_USE_SNAPPING | SEQ_SNAP_TO_PLAYHEAD | SEQ_SNAP_TO_STRIP_HOLD;
875+ tool_settings->snap_flag = SEQ_SNAP_TO_PLAYHEAD | SEQ_SNAP_TO_STRIP_HOLD;
876 tool_settings->snap_distance = 15;
877 return tool_settings;
878 }
879@@ -338,10 +338,10 @@ eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene)
880 return tool_settings->fit_method;
881 }
882
883-eSeqSnapFlag SEQ_tool_settings_snap_flag_get(Scene *scene)
884+short SEQ_tool_settings_snap_flag_get(Scene *scene)
885 {
886 const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
887- return tool_settings->snap_flag;
888+ return (short)tool_settings->snap_flag;
889 }
890
891 int SEQ_tool_settings_snap_distance_get(Scene *scene)

With this changes the sequencer snap works in other modes.
(There were other things I saw that could change, but it's not important).

This is one way if you want to make the sequencer snap system more generic in the transform code.


Note: I am seeing memory leaks with the original patch:

Error: Not freed memory blocks: 5, total unfreed memory 0.001137 MB
SeqCollection len: 24 00000177F4DF9438
SeqCollection GSet len: 64 00000177F5C6C978
ghash_buckets_resize len: 40 00000177F478E1F8
memory pool len: 48 00000177F4790038
BLI_Mempool Chunk len: 1016 00000177DF6ADF58

I think your approach should work for this purpose.

I was confused by API though - why would you use targetSnap callback if you set it always to CalcSnapGeometry which that dispatches actions based on t->spacetype? I assumed it was meant to be assigned directly to snapping function for particular mode.

In any case I will look at this mem leaks.

  • Use better integraton with snapping system, add drawing, since it's only few lines
  • Add missed file
  • Set snapping on by default
  • Add snapping to strip flag, this must be always set.

Some problems I only noticed after seeing a second time.

source/blender/editors/transform/transform_snap.c
926

We could pass t->tsnap.snapPoint[0] as a parameter of transform_snap_sequencer_apply.

source/blender/editors/transform/transform_snap_sequencer.c
46

Optional: To avoid a pad, int source_snap_point_count could be under int *target_snap_points.

118

SCE_SNAP_SEQ_IGNORE_MUTED and SCE_SNAP_SEQ_IGNORE_SOUND won't work here :\

133

A better name for this variable would be "snap_mode"

165

A better name for this variable would be "snap_mode"

source/blender/makesdna/DNA_scene_types.h
1337

A better name for this member would be "snap_mode"

1357

This enum is not being used.

2055

Optional: if you prefer, these values can go to a new "snap_flag" member in SequencerToolSettings

2076

"#SequencerToolSettings.snap_flag" in this comment can be removed. Neither SCE_SNAP_MODE_INCREMENT nor SCE_SNAP_MODE_GRID are part of this member.
(Can be added later)

source/blender/makesrna/intern/rna_scene.c
3134

Accidentally removed

Richard Antalik (ISS) marked 10 inline comments as done.
  • address inlines
source/blender/makesdna/DNA_scene_types.h
2055

IMO all relevant flags should be there, also ToolSettings.snap_flag is char type so I think I will use SequencerToolSettings

Some problems I only noticed after seeing a second time.

I haven't gone over all changes yet too, so if I found some issue I will fix.

source/blender/makesdna/DNA_scene_types.h
2075

For the transform code. It is not very safe to mix the snap mode with the snap flag.
snap mode - Elements that will be the focus of the snap (verts, edges, increment, Playhead, etc)
snap flag - varied parameter.

I suggest creating 2 members: snap_flag and snap_mode

  • Cleanup
  • Separate snap_flag and snap_mode
Richard Antalik (ISS) marked an inline comment as done.Jun 29 2021, 3:41 PM

Overall it's good, but with some observations:

source/blender/makesdna/DNA_scene_types.h
1338

Both snap_mode and snap_flag could be shorts to match the other members.

source/blender/makesrna/intern/rna_scene.c
193

Should SEQ_SNAP_TO_STRIPS be added here?

This revision is now accepted and ready to land.Jun 29 2021, 7:08 PM
Richard Antalik (ISS) marked an inline comment as done.Jun 29 2021, 7:23 PM

Thanks for review!

source/blender/makesdna/DNA_scene_types.h
1338

Yes, I will change DNA because this may save some headscratching in future, though snap_flag doesn't have to be cast to short here, I probably assumed I will copy value to tsnap which is not true and forgot to change...

source/blender/makesrna/intern/rna_scene.c
193

Idea was that this will be basic snap mode that will always be active, so SEQ_SNAP_TO_STRIPS is ommited by choice.

This revision was automatically updated to reflect the committed changes.