Editor/MovieMaker/Extensions/EditingExtensions.cs
using System.Collections.Immutable;
using Sandbox.MovieMaker;
namespace Editor.MovieMaker;
#nullable enable
[Flags]
public enum SnapFlag
{
None = 0,
// General
Frame = 1,
MinorTick = 2,
MajorTick = 4,
Playhead = 8,
TrackBlock = 0x10,
PasteBlock = 0x20,
// Keyframe edit mode
Keyframe = 0x1_0000,
// Motion edit mode
SelectionTotalStart = 0x1_0000,
SelectionPeakStart = 0x2_0000,
SelectionPeakEnd = 0x4_0000,
SelectionTotalEnd = 0x8_0000,
SelectionStart = SelectionTotalStart | SelectionPeakStart,
SelectionEnd = SelectionPeakEnd | SelectionTotalEnd,
Selection = SelectionStart | SelectionEnd
}
public readonly record struct SnapOptions(
SnapFlag IgnoreFlags = SnapFlag.None,
TrackView? IgnoreTrack = null,
IReadOnlySet<ITrackBlock>? IgnoreBlocks = null,
params MovieTime[] SnapOffsets )
{
public static implicit operator SnapOptions( SnapFlag flags ) => new( flags );
}
public struct TimeSnapHelper
{
public MovieTime Time { get; }
public MovieTime MaxSnap { get; set; }
public SnapOptions Options { get; }
public MovieTime BestTime { get; private set; }
public float BestScore { get; private set; } = float.MaxValue;
public TimeSnapHelper( MovieTime time, MovieTime maxSnap, SnapOptions options )
{
Time = BestTime = time;
BestScore = float.PositiveInfinity;
MaxSnap = maxSnap;
Options = options;
}
public void Add( SnapFlag flag, MovieTime time, int priority = 0, bool force = false )
{
if ( (Options.IgnoreFlags & flag) != 0 ) return;
var timeDiff = (time - Time).Absolute;
if ( !force && timeDiff * Math.Max( 4 - priority, 1 ) > MaxSnap * 4 ) return;
var score = (float)(timeDiff.TotalSeconds / MaxSnap.TotalSeconds) - priority;
if ( score >= BestScore ) return;
BestScore = score;
BestTime = time;
}
public void Add( TimeSnapHelper helper )
{
if ( helper.BestScore >= BestScore ) return;
BestScore = helper.BestScore;
BestTime = helper.BestTime - (helper.Time - Time);
}
}