diff options
19 files changed, 526 insertions, 118 deletions
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 13a8e46e2b8..ab05461f185 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -162,6 +162,14 @@ class SEQUENCER_HT_header(Header): if tool_settings.use_proportional_edit: row.prop(tool_settings, "proportional_edit_falloff", icon_only=True) + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: + tool_settings = context.tool_settings + row = layout.row(align=True) + row.prop(tool_settings, "use_snap_sequencer", text="") + sub = row.row(align=True) + sub.popover(panel="SEQUENCER_PT_snapping") + layout.separator_spacer() + row = layout.row(align=True) row.prop(st, "show_strip_overlay", text="", icon='OVERLAY') sub = row.row(align=True) @@ -2264,6 +2272,30 @@ class SEQUENCER_PT_custom_props(SequencerButtonsPanel, PropertyPanel, Panel): bl_category = "Strip" +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 = 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(sequencer_tool_settings, "snap_seq_element", expand=True) + + col = layout.column(heading="Ignore", align=True) + col.prop(sequencer_tool_settings, "snap_ignore_muted", text="Muted Strips") + col.prop(sequencer_tool_settings, "snap_ignore_sound",text="Sound Strips") + + col = layout.column() + col.prop(sequencer_tool_settings, "snap_distance", slider=True, text="Distance") + + classes = ( SEQUENCER_MT_change, SEQUENCER_HT_tool_header, @@ -2333,6 +2365,8 @@ classes = ( SEQUENCER_PT_annotation, SEQUENCER_PT_annotation_onion, + + SEQUENCER_PT_snapping, ) if __name__ == "__main__": # only for live edit. diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 110b9fc4252..d5baeb08ccc 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 6 +#define BLENDER_FILE_SUBVERSION 7 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 791116682c8..84741038164 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -228,7 +228,10 @@ static void scene_init_data(ID *id) /* Curve Profile */ scene->toolsettings->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE); + + /* Sequencer */ scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init(); + scene->toolsettings->snap_flag |= SCE_SNAP_SEQ; for (size_t i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) { scene->orientation_slots[i].index_custom = -1; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 84021755cef..cc350528518 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -44,6 +44,8 @@ #include "readfile.h" #include "versioning_common.h" +#include "SEQ_sequencer.h" + #include "MEM_guardedalloc.h" #include "versioning_common.h" @@ -430,6 +432,37 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 7)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *tool_settings = scene->toolsettings; + tool_settings->snap_flag |= SCE_SNAP_SEQ; + short snap_mode = tool_settings->snap_mode; + short snap_node_mode = tool_settings->snap_node_mode; + tool_settings->snap_mode &= ~((1 << 4) | (1 << 5) | (1 << 6)); + tool_settings->snap_node_mode &= ~((1 << 5) | (1 << 6)); + if (snap_mode & (1 << 4)) { + tool_settings->snap_mode |= (1 << 6); /* SCE_SNAP_MODE_INCREMENT */ + } + if (snap_mode & (1 << 5)) { + tool_settings->snap_mode |= (1 << 4); /* SCE_SNAP_MODE_EDGE_MIDPOINT */ + } + if (snap_mode & (1 << 6)) { + tool_settings->snap_mode |= (1 << 5); /* SCE_SNAP_MODE_EDGE_PERPENDICULAR */ + } + if (snap_node_mode & (1 << 5)) { + tool_settings->snap_node_mode |= (1 << 0); /* SCE_SNAP_MODE_NODE_X */ + } + if (snap_node_mode & (1 << 6)) { + tool_settings->snap_node_mode |= (1 << 1); /* SCE_SNAP_MODE_NODE_Y */ + } + + SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene); + sequencer_tool_settings->snap_mode = SEQ_SNAP_TO_STRIPS | SEQ_SNAP_TO_PLAYHEAD | + SEQ_SNAP_TO_STRIP_HOLD; + sequencer_tool_settings->snap_distance = 15; + } + } + /** * Versioning code until next subversion bump goes here. * 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; +} diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 4da2bbb58c0..f13105ce559 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1336,6 +1336,12 @@ typedef struct MeshStatVis { typedef struct SequencerToolSettings { /* eSeqImageFitMethod */ int fit_method; + short snap_mode; + short snap_flag; + int _pad0; + /** When there are many snap points, 0-1 range corresponds to resolution from boundbox to all + * possible snap points. */ + int snap_distance; } SequencerToolSettings; typedef enum eSeqImageFitMethod { @@ -2039,6 +2045,7 @@ enum { #define SCE_SNAP_NO_SELF (1 << 4) #define SCE_SNAP_ABS_GRID (1 << 5) #define SCE_SNAP_BACKFACE_CULLING (1 << 6) +#define SCE_SNAP_SEQ (1 << 7) /** #ToolSettings.snap_target */ #define SCE_SNAP_TARGET_CLOSEST 0 @@ -2051,15 +2058,26 @@ enum { #define SCE_SNAP_MODE_EDGE (1 << 1) #define SCE_SNAP_MODE_FACE (1 << 2) #define SCE_SNAP_MODE_VOLUME (1 << 3) -#define SCE_SNAP_MODE_INCREMENT (1 << 4) -#define SCE_SNAP_MODE_EDGE_MIDPOINT (1 << 5) -#define SCE_SNAP_MODE_EDGE_PERPENDICULAR (1 << 6) +#define SCE_SNAP_MODE_EDGE_MIDPOINT (1 << 4) +#define SCE_SNAP_MODE_EDGE_PERPENDICULAR (1 << 5) + +/** #SequencerToolSettings.snap_mode */ +#define SEQ_SNAP_TO_STRIPS (1 << 0) +#define SEQ_SNAP_TO_PLAYHEAD (1 << 1) +#define SEQ_SNAP_TO_STRIP_HOLD (1 << 2) + +/** #SequencerToolSettings.snap_flag */ +#define SEQ_SNAP_IGNORE_MUTED (1 << 0) +#define SEQ_SNAP_IGNORE_SOUND (1 << 1) /** #ToolSettings.snap_node_mode */ -#define SCE_SNAP_MODE_NODE_X (1 << 5) -#define SCE_SNAP_MODE_NODE_Y (1 << 6) +#define SCE_SNAP_MODE_NODE_X (1 << 0) +#define SCE_SNAP_MODE_NODE_Y (1 << 1) -/** #ToolSettings.snap_mode and #ToolSettings.snap_node_mode */ +/** + * #ToolSettings.snap_mode and #ToolSettings.snap_node_mode + */ +#define SCE_SNAP_MODE_INCREMENT (1 << 6) #define SCE_SNAP_MODE_GRID (1 << 7) /** #ToolSettings.snap_transform_mode_flag */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index ceeec13b09d..1b8fc692611 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -190,6 +190,12 @@ const EnumPropertyItem rna_enum_snap_node_element_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_snap_seq_element_items[] = { + {SEQ_SNAP_TO_PLAYHEAD, "PLAYHEAD", ICON_NONE, "Playhead", "Snap to current frame"}, + {SEQ_SNAP_TO_STRIP_HOLD, "STRIP_HOLD", ICON_NONE, "Hold Offset", "Snap to strip hold offset"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifndef RNA_RUNTIME static const EnumPropertyItem snap_uv_element_items[] = { {SCE_SNAP_MODE_INCREMENT, @@ -3111,6 +3117,12 @@ static void rna_def_tool_settings(BlenderRNA *brna) "Absolute grid alignment while translating (based on the pivot center)"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "use_snap_sequencer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_SEQ); + RNA_def_property_ui_text(prop, "Use Snapping", "Snap to strip edges or current frame"); + RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1); + RNA_def_property_boolean_default(prop, true); + prop = RNA_def_property(srna, "snap_elements", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_mode"); RNA_def_property_enum_items(prop, rna_enum_snap_element_items); @@ -3505,9 +3517,36 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) RNA_def_struct_path_func(srna, "rna_SequencerToolSettings_path"); RNA_def_struct_ui_text(srna, "Sequencer Tool Settings", ""); + /* Add strip settings. */ prop = RNA_def_property(srna, "fit_method", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, scale_fit_methods); RNA_def_property_ui_text(prop, "Fit Method", "Scale fit method"); + + /* Transform snapping. */ + + /* Sequencer editor uses own set of snap modes */ + prop = RNA_def_property(srna, "snap_seq_element", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_mode"); + RNA_def_property_enum_items(prop, rna_enum_snap_seq_element_items); + RNA_def_property_ui_text(prop, "Snap To", "Type of element to snap to"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + + prop = RNA_def_property(srna, "snap_ignore_muted", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SEQ_SNAP_IGNORE_MUTED); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Ignore Muted Strips", "Don't snap to hidden strips"); + + prop = RNA_def_property(srna, "snap_ignore_sound", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SEQ_SNAP_IGNORE_SOUND); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Ignore Sound Strips", "Don't snap to sound strips"); + + prop = RNA_def_property(srna, "snap_distance", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "snap_distance"); + RNA_def_property_int_default(prop, 15); + RNA_def_property_ui_range(prop, 0, 50, 1, 1); + RNA_def_property_ui_text(prop, "Snapping Distance", "Maximum distance for snapping in pixels"); } static void rna_def_unified_paint_settings(BlenderRNA *brna) diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index c7c2dc275ee..39d8a7241fb 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -71,6 +71,7 @@ bool SEQ_iterator_ensure(SeqCollection *collection, struct Sequence *SEQ_iterator_yield(SeqIterator *iterator); SeqCollection *SEQ_collection_create(void); +uint SEQ_collection_count(SeqCollection *collection); bool SEQ_collection_append_strip(struct Sequence *seq, SeqCollection *data); bool SEQ_collection_remove_strip(struct Sequence *seq, SeqCollection *data); void SEQ_collection_free(SeqCollection *collection); diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index fd4181a5a54..706f4064bf3 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -55,6 +55,9 @@ struct SequencerToolSettings *SEQ_tool_settings_ensure(struct Scene *scene); void SEQ_tool_settings_free(struct SequencerToolSettings *tool_settings); eSeqImageFitMethod SEQ_tool_settings_fit_method_get(struct Scene *scene); void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fit_method); +short SEQ_tool_settings_snap_flag_get(struct Scene *scene); +short SEQ_tool_settings_snap_mode_get(struct Scene *scene); +int SEQ_tool_settings_snap_distance_get(struct Scene *scene); struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings); struct Editing *SEQ_editing_get(struct Scene *scene, bool alloc); struct Editing *SEQ_editing_ensure(struct Scene *scene); diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index 9bbc5362f18..4df92ce7df4 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -115,6 +115,14 @@ SeqCollection *SEQ_collection_create(void) } /** + * Return number of items in collection. + */ +uint SEQ_collection_count(SeqCollection *collection) +{ + return BLI_gset_len(collection->set); +} + +/** * Query strips from seqbase. seq_reference is used by query function as filter condition. * * \param seq_reference: reference strip for query function diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index 8cd67195c30..f6b37bdb3ab 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -311,6 +311,8 @@ SequencerToolSettings *SEQ_tool_settings_init(void) SequencerToolSettings *tool_settings = MEM_callocN(sizeof(SequencerToolSettings), "Sequencer tool settings"); tool_settings->fit_method = SEQ_SCALE_TO_FIT; + tool_settings->snap_mode = SEQ_SNAP_TO_STRIPS | SEQ_SNAP_TO_PLAYHEAD | SEQ_SNAP_TO_STRIP_HOLD; + tool_settings->snap_distance = 15; return tool_settings; } @@ -336,6 +338,24 @@ eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene) return tool_settings->fit_method; } +short SEQ_tool_settings_snap_mode_get(Scene *scene) +{ + const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); + return tool_settings->snap_mode; +} + +short SEQ_tool_settings_snap_flag_get(Scene *scene) +{ + const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); + return tool_settings->snap_flag; +} + +int SEQ_tool_settings_snap_distance_get(Scene *scene) +{ + const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); + return tool_settings->snap_distance; +} + void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_method) { SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); |