Profile editor - Implemented timeline segments

Profile editor - Added snapping to multiple elements of the timeline
This commit is contained in:
SpoinkyNL committed 2020-07-18 23:52:42 +02:00
1 parent c5219bd224
commit 527fef3dc6
17 files changed
+527 -154

No files matched your search

@@ -8,6 +8,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Artemis.Core.Models.Surface;
using RGB.NET.Core;
using Size = System.Windows.Size;
@@ -28,17 +29,20 @@ namespace Artemis.UI.Shared.Controls
private readonly DrawingGroup _backingStore;
private readonly List<DeviceVisualizerLed> _deviceVisualizerLeds;
private BitmapImage _deviceImage;
private bool _subscribed;
private ArtemisDevice _oldDevice;
private Task _lastRenderTask;
private readonly DispatcherTimer _timer;
public DeviceVisualizer()
{
_backingStore = new DrawingGroup();
_deviceVisualizerLeds = new List<DeviceVisualizerLed>();
Loaded += (sender, args) => SubscribeToUpdate(true);
Unloaded += (sender, args) => SubscribeToUpdate(false);
// Run an update timer at 25 fps
_timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(40)};
_timer.Tick += TimerOnTick;
Loaded += (sender, args) => _timer.Start();
Unloaded += (sender, args) => _timer.Stop();
}
public ArtemisDevice Device
@@ -61,7 +65,7 @@ namespace Artemis.UI.Shared.Controls
public void Dispose()
{
RGBSurface.Instance.Updated -= RgbSurfaceOnUpdated;
_timer.Stop();
}
protected override void OnRender(DrawingContext drawingContext)
@@ -73,7 +77,7 @@ namespace Artemis.UI.Shared.Controls
var measureSize = MeasureOverride(Size.Empty);
var scale = Math.Min(DesiredSize.Width / measureSize.Width, DesiredSize.Height / measureSize.Height);
var scaledRect = new Rect(0, 0, measureSize.Width * scale, measureSize.Height * scale);
// Center and scale the visualization in the desired bounding box
if (DesiredSize.Width > 0 && DesiredSize.Height > 0)
{
@@ -95,7 +99,7 @@ namespace Artemis.UI.Shared.Controls
// Render device and LED images
if (_deviceImage != null)
drawingContext.DrawImage(_deviceImage, new Rect(0, 0, Device.RgbDevice.Size.Width, Device.RgbDevice.Size.Height));
foreach (var deviceVisualizerLed in _deviceVisualizerLeds)
deviceVisualizerLed.RenderImage(drawingContext);
@@ -113,26 +117,18 @@ namespace Artemis.UI.Shared.Controls
return rotationRect.Size;
}
private void TimerOnTick(object sender, EventArgs e)
{
if (ShowColors && Visibility == Visibility.Visible)
Render();
}
private void UpdateTransform()
{
InvalidateVisual();
InvalidateMeasure();
}
private void SubscribeToUpdate(bool subscribe)
{
if (_subscribed == subscribe)
return;
if (subscribe)
RGBSurface.Instance.Updated += RgbSurfaceOnUpdated;
else
RGBSurface.Instance.Updated -= RgbSurfaceOnUpdated;
_subscribed = subscribe;
}
private static void DevicePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var deviceVisualizer = (DeviceVisualizer) d;
@@ -208,17 +204,7 @@ namespace Artemis.UI.Shared.Controls
if (e.PropertyName == nameof(Device.RgbDevice.Scale) || e.PropertyName == nameof(Device.RgbDevice.Rotation))
UpdateTransform();
}
private void RgbSurfaceOnUpdated(UpdatedEventArgs e)
{
_lastRenderTask?.Wait();
_lastRenderTask = Dispatcher.InvokeAsync(() =>
{
if (ShowColors && Visibility == Visibility.Visible)
Render();
}).Task;
}
private void Render()
{
@@ -44,7 +44,7 @@ namespace Artemis.UI.Shared.Controls
var r = Led.RgbLed.Color.GetR();
var g = Led.RgbLed.Color.GetG();
var b = Led.RgbLed.Color.GetB();
drawingContext.DrawRectangle(isDimmed
? new SolidColorBrush(Color.FromArgb(100, r, g, b))
: new SolidColorBrush(Color.FromRgb(r, g, b)), null, LedRect);
@@ -74,5 +74,13 @@ namespace Artemis.UI.Shared.Services.Interfaces
PropertyInputRegistration RegisterPropertyInput<T>(PluginInfo pluginInfo) where T : PropertyInputViewModel;
void RemovePropertyInput(PropertyInputRegistration registration);
/// <summary>
/// Snaps the given time to the closest relevant element in the timeline, this can be the cursor, a keyframe or a segment end.
/// </summary>
/// <param name="time"></param>
/// <param name="tolerance">How close the time must be to snap</param>
/// <returns></returns>
TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes);
}
}
@@ -34,7 +34,7 @@ namespace Artemis.UI.Shared.Services
_registeredPropertyEditors = new List<PropertyInputRegistration>();
Kernel = kernel;
PixelsPerSecond = 31;
PixelsPerSecond = 100;
}
public IKernel Kernel { get; }
@@ -48,7 +48,7 @@ namespace Artemis.UI.Shared.Services
set
{
if (_currentTime.Equals(value)) return;
if (value > SelectedProfileElement.TimelineLength)
if (SelectedProfileElement != null && value > SelectedProfileElement.TimelineLength)
_currentTime = SelectedProfileElement.TimelineLength;
else
_currentTime = value;
@@ -205,6 +205,47 @@ namespace Artemis.UI.Shared.Services
}
}
public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes)
{
if (snapToSegments)
{
// Snap to the end of the start segment
if (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.StartSegmentLength.TotalMilliseconds) < tolerance.TotalMilliseconds)
return SelectedProfileElement.StartSegmentLength;
// Snap to the end of the main segment
var mainSegmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength;
if (Math.Abs(time.TotalMilliseconds - mainSegmentEnd.TotalMilliseconds) < tolerance.TotalMilliseconds)
return mainSegmentEnd;
// Snap to the end of the end segment (end of the timeline)
if (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.TimelineLength.TotalMilliseconds) < tolerance.TotalMilliseconds)
return SelectedProfileElement.TimelineLength;
}
if (snapToCurrentTime)
{
// Snap to the current time
if (Math.Abs(time.TotalMilliseconds - CurrentTime.TotalMilliseconds) < tolerance.TotalMilliseconds)
return SelectedProfileElement.StartSegmentLength;
}
if (snapToKeyframes)
{
// Get all visible keyframes
var keyframes = SelectedProfileElement.GetAllKeyframes()
.Where(k => SelectedProfileElement.IsPropertyGroupExpanded(k.BaseLayerProperty.Parent))
.ToList();
// Find the closest keyframe
var closeKeyframe = keyframes.FirstOrDefault(k => Math.Abs(time.TotalMilliseconds - k.Position.TotalMilliseconds) < tolerance.TotalMilliseconds);
if (closeKeyframe != null)
return closeKeyframe.Position;
}
return time;
}
public Module GetCurrentModule()
{
return (Module) SelectedProfile?.PluginInfo.Instance;