mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Profiles - Finished moving and improving timeline logic
This commit is contained in:
parent
4ede3876d4
commit
458fd2a704
@ -8,7 +8,7 @@
|
||||
/// <summary>
|
||||
/// Performs an update on the model
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The delta time in seconds</param>
|
||||
void Update(double deltaTime);
|
||||
/// <param name="timeline">The timeline to apply during update</param>
|
||||
void Update(Timeline timeline);
|
||||
}
|
||||
}
|
||||
@ -137,16 +137,18 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Updates the smoothing progress of the data binding
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The time in seconds that passed since the last update</param>
|
||||
public void Update(double deltaTime)
|
||||
/// <param name="timeline">The timeline to apply during update</param>
|
||||
public void Update(Timeline timeline)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBinding");
|
||||
|
||||
// Data bindings cannot go back in time like brushes
|
||||
deltaTime = Math.Max(0, deltaTime);
|
||||
TimeSpan deltaTime = timeline.LastDelta;
|
||||
if (deltaTime < TimeSpan.Zero)
|
||||
deltaTime = TimeSpan.Zero;
|
||||
|
||||
_easingProgress = _easingProgress.Add(TimeSpan.FromSeconds(deltaTime));
|
||||
_easingProgress = _easingProgress.Add(deltaTime);
|
||||
if (_easingProgress > EasingTime)
|
||||
_easingProgress = EasingTime;
|
||||
}
|
||||
|
||||
@ -34,14 +34,12 @@ namespace Artemis.Core
|
||||
_layerEffects = new List<BaseLayerEffect>();
|
||||
_expandedPropertyGroups = new List<string>();
|
||||
|
||||
ApplyRenderElementDefaults();
|
||||
Parent.AddChild(this);
|
||||
}
|
||||
|
||||
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity)
|
||||
{
|
||||
FolderEntity = folderEntity;
|
||||
|
||||
EntityId = folderEntity.Id;
|
||||
|
||||
Profile = profile;
|
||||
@ -52,11 +50,21 @@ namespace Artemis.Core
|
||||
|
||||
_layerEffects = new List<BaseLayerEffect>();
|
||||
_expandedPropertyGroups = new List<string>();
|
||||
|
||||
Load();
|
||||
UpdateChildrenTimelineLength();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this folder is at the root of the profile tree
|
||||
/// </summary>
|
||||
public bool IsRootFolder => Parent == Profile;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the longest timeline of all this folders children
|
||||
/// </summary>
|
||||
public Timeline LongestChildTimeline { get; private set; }
|
||||
|
||||
internal FolderEntity FolderEntity { get; set; }
|
||||
|
||||
internal override RenderElementEntity RenderElementEntity => FolderEntity;
|
||||
@ -80,11 +88,8 @@ namespace Artemis.Core
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
// Ensure the layer must still be displayed
|
||||
UpdateDisplayCondition();
|
||||
|
||||
// Update the layer timeline
|
||||
UpdateTimeLines(deltaTime);
|
||||
UpdateTimeline(deltaTime);
|
||||
|
||||
foreach (ProfileElement child in Children)
|
||||
child.Update(deltaTime);
|
||||
@ -94,8 +99,7 @@ namespace Artemis.Core
|
||||
public override void Reset()
|
||||
{
|
||||
DisplayConditionMet = false;
|
||||
TimeLine = TimelineLength;
|
||||
ExtraTimeLines.Clear();
|
||||
Timeline.JumpToStart();
|
||||
|
||||
foreach (ProfileElement child in Children)
|
||||
child.Reset();
|
||||
@ -108,7 +112,7 @@ namespace Artemis.Core
|
||||
throw new ObjectDisposedException("Folder");
|
||||
|
||||
base.AddChild(child, order);
|
||||
UpdateTimeLineLength();
|
||||
UpdateChildrenTimelineLength();
|
||||
CalculateRenderProperties();
|
||||
}
|
||||
|
||||
@ -119,7 +123,7 @@ namespace Artemis.Core
|
||||
throw new ObjectDisposedException("Folder");
|
||||
|
||||
base.RemoveChild(child);
|
||||
UpdateTimeLineLength();
|
||||
UpdateChildrenTimelineLength();
|
||||
CalculateRenderProperties();
|
||||
}
|
||||
|
||||
@ -147,14 +151,21 @@ namespace Artemis.Core
|
||||
OnRenderPropertiesUpdated();
|
||||
}
|
||||
|
||||
protected internal override void UpdateTimeLineLength()
|
||||
private void UpdateChildrenTimelineLength()
|
||||
{
|
||||
TimelineLength = !Children.Any() ? TimeSpan.Zero : Children.OfType<RenderProfileElement>().Max(c => c.TimelineLength);
|
||||
if (StartSegmentLength + MainSegmentLength + EndSegmentLength > TimelineLength)
|
||||
TimelineLength = StartSegmentLength + MainSegmentLength + EndSegmentLength;
|
||||
Timeline longest = new Timeline() {MainSegmentLength = TimeSpan.Zero};
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
{
|
||||
if (profileElement is Folder folder && folder.LongestChildTimeline.Length > longest.Length)
|
||||
longest = folder.LongestChildTimeline;
|
||||
else if (profileElement is Layer layer &&
|
||||
layer.Timeline.PlayMode == TimelinePlayMode.Once &&
|
||||
layer.Timeline.StopMode == TimelineStopMode.Finish &&
|
||||
layer.Timeline.Length > longest.Length)
|
||||
longest = layer.Timeline;
|
||||
}
|
||||
|
||||
if (Parent is RenderProfileElement parent)
|
||||
parent.UpdateTimeLineLength();
|
||||
LongestChildTimeline = longest;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@ -208,8 +219,6 @@ namespace Artemis.Core
|
||||
|
||||
#region Rendering
|
||||
|
||||
private TimeSpan _lastRenderTime;
|
||||
|
||||
public override void Render(SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
{
|
||||
if (Disposed)
|
||||
@ -222,28 +231,24 @@ namespace Artemis.Core
|
||||
if (Path == null)
|
||||
return;
|
||||
|
||||
RenderFolder(TimeLine, canvas, canvasInfo);
|
||||
RenderFolder(Timeline, canvas, canvasInfo);
|
||||
}
|
||||
|
||||
private void PrepareForRender(TimeSpan timeLine)
|
||||
private void PrepareForRender(Timeline timeline)
|
||||
{
|
||||
double renderDelta = (timeLine - _lastRenderTime).TotalSeconds;
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
{
|
||||
baseLayerEffect.BaseProperties?.Update(timeLine, renderDelta);
|
||||
baseLayerEffect.Update(renderDelta);
|
||||
baseLayerEffect.BaseProperties?.Update(timeline);
|
||||
baseLayerEffect.Update(timeline.LastDelta.TotalSeconds);
|
||||
}
|
||||
|
||||
_lastRenderTime = timeLine;
|
||||
}
|
||||
|
||||
private void RenderFolder(TimeSpan timeLine, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
private void RenderFolder(Timeline timeline, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
{
|
||||
if (timeLine > TimelineLength)
|
||||
if (Timeline.IsFinished && LongestChildTimeline.IsFinished)
|
||||
return;
|
||||
|
||||
PrepareForRender(timeLine);
|
||||
PrepareForRender(timeline);
|
||||
|
||||
if (_folderBitmap == null)
|
||||
{
|
||||
|
||||
@ -45,14 +45,14 @@ namespace Artemis.Core
|
||||
_expandedPropertyGroups = new List<string>();
|
||||
|
||||
Initialize();
|
||||
ApplyRenderElementDefaults();
|
||||
|
||||
Parent.AddChild(this);
|
||||
}
|
||||
|
||||
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity)
|
||||
{
|
||||
LayerEntity = layerEntity;
|
||||
EntityId = layerEntity.Id;
|
||||
|
||||
Profile = profile;
|
||||
Parent = parent;
|
||||
General = new LayerGeneralProperties();
|
||||
@ -176,14 +176,6 @@ namespace Artemis.Core
|
||||
ActivateLayerBrush();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Reset()
|
||||
{
|
||||
DisplayConditionMet = false;
|
||||
TimeLine = TimelineLength;
|
||||
ExtraTimeLines.Clear();
|
||||
}
|
||||
|
||||
#region Storage
|
||||
|
||||
internal override void Load()
|
||||
@ -258,9 +250,7 @@ namespace Artemis.Core
|
||||
#endregion
|
||||
|
||||
#region Rendering
|
||||
|
||||
private TimeSpan _lastRenderTime;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
@ -270,16 +260,15 @@ namespace Artemis.Core
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
// Ensure the layer must still be displayed
|
||||
UpdateDisplayCondition();
|
||||
|
||||
// Update the layer timeline
|
||||
UpdateTimeLines(deltaTime);
|
||||
UpdateTimeline(deltaTime);
|
||||
}
|
||||
|
||||
protected internal override void UpdateTimeLineLength()
|
||||
/// <inheritdoc />
|
||||
public override void Reset()
|
||||
{
|
||||
TimelineLength = StartSegmentLength + MainSegmentLength + EndSegmentLength;
|
||||
DisplayConditionMet = false;
|
||||
Timeline.JumpToStart();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -298,35 +287,31 @@ namespace Artemis.Core
|
||||
if (LayerBrush?.BaseProperties?.PropertiesInitialized == false || LayerBrush?.BrushType != LayerBrushType.Regular)
|
||||
return;
|
||||
|
||||
RenderLayer(TimeLine, canvas);
|
||||
foreach (TimeSpan extraTimeLine in ExtraTimeLines)
|
||||
RenderLayer(extraTimeLine, canvas);
|
||||
RenderLayer(Timeline, canvas);
|
||||
foreach (Timeline extraTimeline in Timeline.ExtraTimelines)
|
||||
RenderLayer(extraTimeline, canvas);
|
||||
}
|
||||
|
||||
private void PrepareForRender(TimeSpan renderTime)
|
||||
private void PrepareForRender(Timeline timeline)
|
||||
{
|
||||
double renderDelta = (renderTime - _lastRenderTime).TotalSeconds;
|
||||
|
||||
General.Update(renderTime, renderDelta);
|
||||
Transform.Update(renderTime, renderDelta);
|
||||
LayerBrush.BaseProperties?.Update(renderTime, renderDelta);
|
||||
LayerBrush.Update(renderDelta);
|
||||
General.Update(timeline);
|
||||
Transform.Update(timeline);
|
||||
LayerBrush.BaseProperties?.Update(timeline);
|
||||
LayerBrush.Update(timeline.LastDelta.TotalSeconds);
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
|
||||
{
|
||||
baseLayerEffect.BaseProperties?.Update(renderTime, renderDelta);
|
||||
baseLayerEffect.Update(renderDelta);
|
||||
baseLayerEffect.BaseProperties?.Update(timeline);
|
||||
baseLayerEffect.Update(timeline.LastDelta.TotalSeconds);
|
||||
}
|
||||
|
||||
_lastRenderTime = renderTime;
|
||||
}
|
||||
|
||||
private void RenderLayer(TimeSpan timeLine, SKCanvas canvas)
|
||||
private void RenderLayer(Timeline timeline, SKCanvas canvas)
|
||||
{
|
||||
if (timeLine > TimelineLength)
|
||||
if (timeline.IsFinished)
|
||||
return;
|
||||
|
||||
PrepareForRender(timeLine);
|
||||
PrepareForRender(timeline);
|
||||
|
||||
if (_layerBitmap == null)
|
||||
{
|
||||
|
||||
@ -40,8 +40,7 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Updates the layer properties internal state
|
||||
/// </summary>
|
||||
/// <param name="renderTime">The current position in the timeline</param>
|
||||
/// <param name="deltaTime">The position difference since last update</param>
|
||||
void Update(TimeSpan renderTime, double deltaTime);
|
||||
/// <param name="timeline">The timeline to apply to the property</param>
|
||||
void Update(Timeline timeline);
|
||||
}
|
||||
}
|
||||
@ -34,20 +34,18 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public string Path { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the property, applying keyframes and data bindings to the current value
|
||||
/// </summary>
|
||||
public void Update(TimeSpan time, double deltaTime)
|
||||
/// <inheritdoc />
|
||||
public void Update(Timeline timeline)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("LayerProperty");
|
||||
|
||||
CurrentValue = BaseValue;
|
||||
|
||||
|
||||
if (ProfileElement.ApplyKeyframesEnabled)
|
||||
UpdateKeyframes(time);
|
||||
UpdateKeyframes(timeline);
|
||||
if (ProfileElement.ApplyDataBindingsEnabled)
|
||||
UpdateDataBindings(deltaTime);
|
||||
UpdateDataBindings(timeline);
|
||||
|
||||
OnUpdated();
|
||||
}
|
||||
@ -125,8 +123,7 @@ namespace Artemis.Core
|
||||
return;
|
||||
|
||||
_baseValue = value;
|
||||
Update(ProfileElement.TimeLine, 0);
|
||||
OnCurrentValueSet();
|
||||
ReapplyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,8 +166,7 @@ namespace Artemis.Core
|
||||
|
||||
// Force an update so that the base value is applied to the current value and
|
||||
// keyframes/data bindings are applied using the new base value
|
||||
Update(ProfileElement.TimeLine, 0);
|
||||
OnCurrentValueSet();
|
||||
ReapplyUpdate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -185,6 +181,13 @@ namespace Artemis.Core
|
||||
CurrentValue = DefaultValue;
|
||||
}
|
||||
|
||||
private void ReapplyUpdate()
|
||||
{
|
||||
ProfileElement.Timeline.ClearDelta();
|
||||
Update(ProfileElement.Timeline);
|
||||
OnCurrentValueSet();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Keyframes
|
||||
@ -294,13 +297,13 @@ namespace Artemis.Core
|
||||
_keyframes = _keyframes.OrderBy(k => k.Position).ToList();
|
||||
}
|
||||
|
||||
private void UpdateKeyframes(TimeSpan time)
|
||||
private void UpdateKeyframes(Timeline timeline)
|
||||
{
|
||||
if (!KeyframesSupported || !KeyframesEnabled)
|
||||
return;
|
||||
|
||||
|
||||
// The current keyframe is the last keyframe before the current time
|
||||
CurrentKeyframe = _keyframes.LastOrDefault(k => k.Position <= time);
|
||||
CurrentKeyframe = _keyframes.LastOrDefault(k => k.Position <= timeline.Position);
|
||||
// Keyframes are sorted by position so we can safely assume the next keyframe's position is after the current
|
||||
int nextIndex = _keyframes.IndexOf(CurrentKeyframe) + 1;
|
||||
NextKeyframe = _keyframes.Count > nextIndex ? _keyframes[nextIndex] : null;
|
||||
@ -314,7 +317,7 @@ namespace Artemis.Core
|
||||
else
|
||||
{
|
||||
TimeSpan timeDiff = NextKeyframe.Position - CurrentKeyframe.Position;
|
||||
float keyframeProgress = (float) ((time - CurrentKeyframe.Position).TotalMilliseconds / timeDiff.TotalMilliseconds);
|
||||
float keyframeProgress = (float) ((timeline.Position - CurrentKeyframe.Position).TotalMilliseconds / timeDiff.TotalMilliseconds);
|
||||
float keyframeProgressEased = (float) Easings.Interpolate(keyframeProgress, CurrentKeyframe.EasingFunction);
|
||||
UpdateCurrentValue(keyframeProgress, keyframeProgressEased);
|
||||
}
|
||||
@ -361,7 +364,7 @@ namespace Artemis.Core
|
||||
{
|
||||
return _dataBindingRegistrations;
|
||||
}
|
||||
|
||||
|
||||
public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyExpression, DataBindingConverter<T, TProperty> converter)
|
||||
{
|
||||
if (_disposed)
|
||||
@ -416,11 +419,11 @@ namespace Artemis.Core
|
||||
OnDataBindingDisabled(new LayerPropertyEventArgs<T>(dataBinding.LayerProperty));
|
||||
}
|
||||
|
||||
private void UpdateDataBindings(double deltaTime)
|
||||
private void UpdateDataBindings(Timeline timeline)
|
||||
{
|
||||
foreach (IDataBinding dataBinding in _dataBindings)
|
||||
{
|
||||
dataBinding.Update(deltaTime);
|
||||
dataBinding.Update(timeline);
|
||||
dataBinding.Apply();
|
||||
}
|
||||
}
|
||||
@ -484,12 +487,10 @@ namespace Artemis.Core
|
||||
_keyframes.Clear();
|
||||
try
|
||||
{
|
||||
_keyframes.AddRange(Entity.KeyframeEntities.Select(k => new LayerPropertyKeyframe<T>(
|
||||
JsonConvert.DeserializeObject<T>(k.Value),
|
||||
k.Position,
|
||||
(Easings.Functions) k.EasingFunction,
|
||||
this
|
||||
)));
|
||||
_keyframes.AddRange(
|
||||
Entity.KeyframeEntities.Where(k => k.Position <= ProfileElement.Timeline.Length)
|
||||
.Select(k => new LayerPropertyKeyframe<T>(JsonConvert.DeserializeObject<T>(k.Value), k.Position, (Easings.Functions) k.EasingFunction, this))
|
||||
);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
|
||||
@ -198,12 +198,12 @@ namespace Artemis.Core
|
||||
layerPropertyGroup.ApplyToEntity();
|
||||
}
|
||||
|
||||
internal void Update(TimeSpan renderTime, double deltaTime)
|
||||
internal void Update(Timeline timeline)
|
||||
{
|
||||
foreach (ILayerProperty layerProperty in LayerProperties)
|
||||
layerProperty.Update(renderTime, deltaTime);
|
||||
layerProperty.Update(timeline);
|
||||
foreach (LayerPropertyGroup layerPropertyGroup in LayerPropertyGroups)
|
||||
layerPropertyGroup.Update(renderTime, deltaTime);
|
||||
layerPropertyGroup.Update(timeline);
|
||||
}
|
||||
|
||||
private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription)
|
||||
|
||||
@ -17,6 +17,7 @@ namespace Artemis.Core
|
||||
{
|
||||
ApplyDataBindingsEnabled = true;
|
||||
ApplyKeyframesEnabled = true;
|
||||
Timeline = new Timeline();
|
||||
|
||||
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
||||
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
||||
@ -30,8 +31,8 @@ namespace Artemis.Core
|
||||
? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
|
||||
: new DataModelConditionGroup(null);
|
||||
|
||||
Timeline = RenderElementEntity.Timeline != null
|
||||
? new Timeline(RenderElementEntity.Timeline)
|
||||
Timeline = RenderElementEntity.Timeline != null
|
||||
? new Timeline(RenderElementEntity.Timeline)
|
||||
: new Timeline();
|
||||
|
||||
ActivateEffects();
|
||||
@ -70,7 +71,17 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Gets the timeline associated with this render element
|
||||
/// </summary>
|
||||
public Timeline? Timeline { get; private set; }
|
||||
public Timeline Timeline { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="Timeline"/> according to the provided <paramref name="deltaTime"/> and current display condition status
|
||||
/// </summary>
|
||||
public void UpdateTimeline(double deltaTime)
|
||||
{
|
||||
// The play mode dictates whether to stick to the main segment unless the display conditions contains events
|
||||
bool stickToMainSegment = Timeline.PlayMode == TimelinePlayMode.Repeat && (DisplayConditionMet || DisplayCondition != null && DisplayCondition.ContainsEvents);
|
||||
Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -297,6 +308,9 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public bool ApplyDataBindingsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the display conditions on this element and applies any required changes to the <see cref="Timeline"/>
|
||||
/// </summary>
|
||||
public void UpdateDisplayCondition()
|
||||
{
|
||||
if (DisplayCondition == null)
|
||||
@ -306,26 +320,30 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
bool conditionMet = DisplayCondition.Evaluate();
|
||||
|
||||
// Regular conditions reset the timeline whenever their condition is met and was not met before that
|
||||
if (!DisplayCondition.ContainsEvents)
|
||||
{
|
||||
// Regular conditions reset the timeline whenever their condition is met and was not met before that
|
||||
if (conditionMet && !DisplayConditionMet && Timeline.IsFinished)
|
||||
Timeline.JumpToStart();
|
||||
// If regular conditions are no longer met, jump to the end segment if stop mode requires it
|
||||
if (!conditionMet && DisplayConditionMet && Timeline.StopMode == TimelineStopMode.SkipToEnd)
|
||||
Timeline.JumpToEndSegment();
|
||||
}
|
||||
// Event conditions reset if the timeline finished and otherwise apply their overlap mode
|
||||
else if (conditionMet)
|
||||
{
|
||||
// Event conditions reset if the timeline finished
|
||||
if (Timeline.IsFinished)
|
||||
{
|
||||
Timeline.JumpToStart();
|
||||
}
|
||||
// and otherwise apply their overlap mode
|
||||
else
|
||||
{
|
||||
if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Restart)
|
||||
Timeline.JumpToStart();
|
||||
else if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Copy)
|
||||
Timeline.AddExtraTimeline();
|
||||
// The third option is ignore which is handled below:
|
||||
|
||||
// done
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -64,11 +64,14 @@ namespace Artemis.Core
|
||||
private TimelinePlayMode _playMode;
|
||||
private TimelineStopMode _stopMode;
|
||||
private readonly List<Timeline> _extraTimelines;
|
||||
private TimeSpan _startSegmentLength;
|
||||
private TimeSpan _mainSegmentLength;
|
||||
private TimeSpan _endSegmentLength;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent this timeline is an extra timeline of
|
||||
/// </summary>
|
||||
public Timeline Parent { get; }
|
||||
public Timeline? Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current position of the timeline
|
||||
@ -81,13 +84,21 @@ namespace Artemis.Core
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delta that was applied during the last call to <see cref="Update" />
|
||||
/// <para>
|
||||
/// Note: If this is an extra timeline <see cref="LastDelta" /> is always equal to <see cref="DeltaToParent" />
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public TimeSpan LastDelta
|
||||
{
|
||||
get => _lastDelta;
|
||||
get => Parent == null ? _lastDelta : DeltaToParent;
|
||||
private set => SetAndNotify(ref _lastDelta, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delta to this timeline's <see cref="Parent" />
|
||||
/// </summary>
|
||||
public TimeSpan DeltaToParent => Parent != null ? Position - Parent.Position : TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element starts its timeline when display conditions are met
|
||||
/// </summary>
|
||||
@ -124,7 +135,7 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the timeline has finished its run
|
||||
/// </summary>
|
||||
public bool IsFinished => Position > Length;
|
||||
public bool IsFinished => Position > Length || Length == TimeSpan.Zero;
|
||||
|
||||
#region Segments
|
||||
|
||||
@ -136,17 +147,41 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the start segment
|
||||
/// </summary>
|
||||
public TimeSpan StartSegmentLength { get; set; }
|
||||
public TimeSpan StartSegmentLength
|
||||
{
|
||||
get => _startSegmentLength;
|
||||
set
|
||||
{
|
||||
if (SetAndNotify(ref _startSegmentLength, value))
|
||||
NotifySegmentShiftAt(TimelineSegment.Start, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the main segment
|
||||
/// </summary>
|
||||
public TimeSpan MainSegmentLength { get; set; }
|
||||
public TimeSpan MainSegmentLength
|
||||
{
|
||||
get => _mainSegmentLength;
|
||||
set
|
||||
{
|
||||
if (SetAndNotify(ref _mainSegmentLength, value))
|
||||
NotifySegmentShiftAt(TimelineSegment.Main, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the end segment
|
||||
/// </summary>
|
||||
public TimeSpan EndSegmentLength { get; set; }
|
||||
public TimeSpan EndSegmentLength
|
||||
{
|
||||
get => _endSegmentLength;
|
||||
set
|
||||
{
|
||||
if (SetAndNotify(ref _endSegmentLength, value))
|
||||
NotifySegmentShiftAt(TimelineSegment.End, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start position of the main segment
|
||||
@ -154,7 +189,11 @@ namespace Artemis.Core
|
||||
public TimeSpan MainSegmentStartPosition
|
||||
{
|
||||
get => StartSegmentEndPosition;
|
||||
set => StartSegmentEndPosition = value;
|
||||
set
|
||||
{
|
||||
StartSegmentEndPosition = value;
|
||||
NotifySegmentShiftAt(TimelineSegment.Main, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -163,7 +202,11 @@ namespace Artemis.Core
|
||||
public TimeSpan EndSegmentStartPosition
|
||||
{
|
||||
get => MainSegmentEndPosition;
|
||||
set => MainSegmentEndPosition = value;
|
||||
set
|
||||
{
|
||||
MainSegmentEndPosition = value;
|
||||
NotifySegmentShiftAt(TimelineSegment.End, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -172,7 +215,11 @@ namespace Artemis.Core
|
||||
public TimeSpan StartSegmentEndPosition
|
||||
{
|
||||
get => StartSegmentLength;
|
||||
set => StartSegmentLength = value;
|
||||
set
|
||||
{
|
||||
StartSegmentLength = value;
|
||||
NotifySegmentShiftAt(TimelineSegment.Start, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -181,7 +228,11 @@ namespace Artemis.Core
|
||||
public TimeSpan MainSegmentEndPosition
|
||||
{
|
||||
get => StartSegmentEndPosition + MainSegmentLength;
|
||||
set => MainSegmentLength = value - StartSegmentEndPosition >= TimeSpan.Zero ? value - StartSegmentEndPosition : TimeSpan.Zero;
|
||||
set
|
||||
{
|
||||
MainSegmentLength = value - StartSegmentEndPosition >= TimeSpan.Zero ? value - StartSegmentEndPosition : TimeSpan.Zero;
|
||||
NotifySegmentShiftAt(TimelineSegment.Main, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -190,11 +241,41 @@ namespace Artemis.Core
|
||||
public TimeSpan EndSegmentEndPosition
|
||||
{
|
||||
get => MainSegmentEndPosition + EndSegmentLength;
|
||||
set => EndSegmentLength = value - MainSegmentEndPosition >= TimeSpan.Zero ? value - MainSegmentEndPosition : TimeSpan.Zero;
|
||||
set
|
||||
{
|
||||
EndSegmentLength = value - MainSegmentEndPosition >= TimeSpan.Zero ? value - MainSegmentEndPosition : TimeSpan.Zero;
|
||||
NotifySegmentShiftAt(TimelineSegment.End, false);
|
||||
}
|
||||
}
|
||||
|
||||
internal TimelineEntity Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the right segments in a way that I don't have to think about it
|
||||
/// </summary>
|
||||
/// <param name="segment">The segment that was updated</param>
|
||||
/// <param name="startUpdated">Whether the start point of the <paramref name="segment" /> was updated</param>
|
||||
private void NotifySegmentShiftAt(TimelineSegment segment, bool startUpdated)
|
||||
{
|
||||
if (segment <= TimelineSegment.End)
|
||||
{
|
||||
if (startUpdated || segment < TimelineSegment.End)
|
||||
NotifyOfPropertyChange(nameof(EndSegmentStartPosition));
|
||||
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
|
||||
}
|
||||
|
||||
if (segment <= TimelineSegment.Main)
|
||||
{
|
||||
if (startUpdated || segment < TimelineSegment.Main)
|
||||
NotifyOfPropertyChange(nameof(MainSegmentStartPosition));
|
||||
NotifyOfPropertyChange(nameof(MainSegmentEndPosition));
|
||||
}
|
||||
|
||||
if (segment <= TimelineSegment.Start) NotifyOfPropertyChange(nameof(StartSegmentEndPosition));
|
||||
|
||||
NotifyOfPropertyChange(nameof(Length));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
@ -213,6 +294,9 @@ namespace Artemis.Core
|
||||
|
||||
if (stickToMainSegment && Position >= MainSegmentStartPosition)
|
||||
Position = MainSegmentStartPosition + TimeSpan.FromMilliseconds(Position.TotalMilliseconds % MainSegmentLength.TotalMilliseconds);
|
||||
|
||||
foreach (Timeline extraTimeline in _extraTimelines)
|
||||
extraTimeline.Update(delta, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -225,6 +309,8 @@ namespace Artemis.Core
|
||||
|
||||
LastDelta = TimeSpan.Zero - Position;
|
||||
Position = TimeSpan.Zero;
|
||||
|
||||
_extraTimelines.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -237,6 +323,8 @@ namespace Artemis.Core
|
||||
|
||||
LastDelta = EndSegmentStartPosition - Position;
|
||||
Position = EndSegmentStartPosition;
|
||||
|
||||
_extraTimelines.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -249,6 +337,8 @@ namespace Artemis.Core
|
||||
|
||||
LastDelta = EndSegmentEndPosition - Position;
|
||||
Position = EndSegmentEndPosition;
|
||||
|
||||
_extraTimelines.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -258,12 +348,20 @@ namespace Artemis.Core
|
||||
/// <param name="stickToMainSegment">Whether to stick to the main segment, wrapping around if needed</param>
|
||||
public void Override(TimeSpan position, bool stickToMainSegment)
|
||||
{
|
||||
_extraTimelines.Clear();
|
||||
|
||||
LastDelta = position - Position;
|
||||
Position = position;
|
||||
if (stickToMainSegment && Position >= MainSegmentStartPosition)
|
||||
Position = MainSegmentStartPosition + TimeSpan.FromMilliseconds(Position.TotalMilliseconds % MainSegmentLength.TotalMilliseconds);
|
||||
|
||||
_extraTimelines.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="LastDelta" /> to <see cref="TimeSpan.Zero" />
|
||||
/// </summary>
|
||||
public void ClearDelta()
|
||||
{
|
||||
LastDelta = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -295,6 +393,13 @@ namespace Artemis.Core
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal enum TimelineSegment
|
||||
{
|
||||
Start,
|
||||
Main,
|
||||
End
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to start their timeline when display conditions are met
|
||||
/// </summary>
|
||||
|
||||
@ -123,7 +123,7 @@ namespace Artemis.Core.Services
|
||||
_introAnimation.Render(args.DeltaTime, args.Canvas, _rgbService.BitmapBrush.Bitmap.Info);
|
||||
}
|
||||
|
||||
TimeSpan introLength = _introAnimation.AnimationProfile.GetAllLayers().Max(l => l.TimelineLength);
|
||||
TimeSpan introLength = _introAnimation.AnimationProfile.GetAllLayers().Max(l => l.Timeline.Length);
|
||||
|
||||
// Stop rendering after the profile finishes (take 1 second extra in case of slow updates)
|
||||
Task.Run(async () =>
|
||||
|
||||
@ -13,31 +13,32 @@ namespace Artemis.Storage.Migrations
|
||||
|
||||
public void Apply(LiteRepository repository)
|
||||
{
|
||||
List<ProfileEntity> profiles = repository.Query<ProfileEntity>().ToList();
|
||||
foreach (ProfileEntity profileEntity in profiles)
|
||||
{
|
||||
foreach (FolderEntity folder in profileEntity.Folders.Where(f => f.MainSegmentLength == TimeSpan.Zero))
|
||||
{
|
||||
if (folder.PropertyEntities.Any(p => p.KeyframeEntities.Any()))
|
||||
folder.MainSegmentLength = folder.PropertyEntities.Where(p => p.KeyframeEntities.Any()).Max(p => p.KeyframeEntities.Max(k => k.Position));
|
||||
if (folder.MainSegmentLength == TimeSpan.Zero)
|
||||
folder.MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||
|
||||
folder.PlayMode = 0;
|
||||
}
|
||||
|
||||
foreach (LayerEntity layer in profileEntity.Layers.Where(l => l.MainSegmentLength == TimeSpan.Zero))
|
||||
{
|
||||
if (layer.PropertyEntities.Any(p => p.KeyframeEntities.Any()))
|
||||
layer.MainSegmentLength = layer.PropertyEntities.Where(p => p.KeyframeEntities.Any()).Max(p => p.KeyframeEntities.Max(k => k.Position));
|
||||
if (layer.MainSegmentLength == TimeSpan.Zero)
|
||||
layer.MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||
|
||||
layer.PlayMode = 0;
|
||||
}
|
||||
|
||||
repository.Update(profileEntity);
|
||||
}
|
||||
// Lesson for next time: Use BsonDocuments in migrations
|
||||
// List<ProfileEntity> profiles = repository.Query<ProfileEntity>().ToList();
|
||||
// foreach (ProfileEntity profileEntity in profiles)
|
||||
// {
|
||||
// foreach (FolderEntity folder in profileEntity.Folders.Where(f => f.MainSegmentLength == TimeSpan.Zero))
|
||||
// {
|
||||
// if (folder.PropertyEntities.Any(p => p.KeyframeEntities.Any()))
|
||||
// folder.MainSegmentLength = folder.PropertyEntities.Where(p => p.KeyframeEntities.Any()).Max(p => p.KeyframeEntities.Max(k => k.Position));
|
||||
// if (folder.MainSegmentLength == TimeSpan.Zero)
|
||||
// folder.MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||
//
|
||||
// folder.PlayMode = 0;
|
||||
// }
|
||||
//
|
||||
// foreach (LayerEntity layer in profileEntity.Layers.Where(l => l.MainSegmentLength == TimeSpan.Zero))
|
||||
// {
|
||||
// if (layer.PropertyEntities.Any(p => p.KeyframeEntities.Any()))
|
||||
// layer.MainSegmentLength = layer.PropertyEntities.Where(p => p.KeyframeEntities.Any()).Max(p => p.KeyframeEntities.Max(k => k.Position));
|
||||
// if (layer.MainSegmentLength == TimeSpan.Zero)
|
||||
// layer.MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||
//
|
||||
// layer.PlayMode = 0;
|
||||
// }
|
||||
//
|
||||
// repository.Update(profileEntity);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52,8 +52,8 @@ namespace Artemis.UI.Shared.Services
|
||||
set
|
||||
{
|
||||
if (_currentTime.Equals(value)) return;
|
||||
if (SelectedProfileElement != null && value > SelectedProfileElement.TimelineLength)
|
||||
_currentTime = SelectedProfileElement.TimelineLength;
|
||||
if (SelectedProfileElement != null && value > SelectedProfileElement.Timeline.Length)
|
||||
_currentTime = SelectedProfileElement.Timeline.Length;
|
||||
else
|
||||
_currentTime = value;
|
||||
UpdateProfilePreview();
|
||||
@ -150,9 +150,9 @@ namespace Artemis.UI.Shared.Services
|
||||
|
||||
// Stick to the main segment for any element that is not currently selected
|
||||
foreach (Folder folder in SelectedProfile.GetAllFolders())
|
||||
folder.OverrideTimeLines(CurrentTime, folder != SelectedProfileElement);
|
||||
folder.Timeline.Override(CurrentTime, folder != SelectedProfileElement);
|
||||
foreach (Layer layer in SelectedProfile.GetAllLayers())
|
||||
layer.OverrideTimeLines(CurrentTime, layer != SelectedProfileElement);
|
||||
layer.Timeline.Override(CurrentTime, layer != SelectedProfileElement);
|
||||
|
||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||
}
|
||||
@ -232,24 +232,23 @@ namespace Artemis.UI.Shared.Services
|
||||
if (snapToSegments)
|
||||
{
|
||||
// Snap to the end of the start segment
|
||||
if (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.StartSegmentLength.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return SelectedProfileElement.StartSegmentLength;
|
||||
if (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.Timeline.StartSegmentEndPosition.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return SelectedProfileElement.Timeline.StartSegmentEndPosition;
|
||||
|
||||
// Snap to the end of the main segment
|
||||
TimeSpan mainSegmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength;
|
||||
if (Math.Abs(time.TotalMilliseconds - mainSegmentEnd.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return mainSegmentEnd;
|
||||
if (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.Timeline.MainSegmentEndPosition.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return SelectedProfileElement.Timeline.MainSegmentEndPosition;
|
||||
|
||||
// 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 (Math.Abs(time.TotalMilliseconds - SelectedProfileElement.Timeline.EndSegmentEndPosition.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return SelectedProfileElement.Timeline.EndSegmentEndPosition;
|
||||
}
|
||||
|
||||
if (snapToCurrentTime)
|
||||
{
|
||||
// Snap to the current time
|
||||
if (Math.Abs(time.TotalMilliseconds - CurrentTime.TotalMilliseconds) < tolerance.TotalMilliseconds)
|
||||
return SelectedProfileElement.StartSegmentLength;
|
||||
return CurrentTime;
|
||||
}
|
||||
|
||||
if (snapTimes != null)
|
||||
|
||||
@ -200,7 +200,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<RadioButton Grid.Column="0"
|
||||
Style="{StaticResource MaterialDesignTabRadioButton}"
|
||||
IsChecked="{Binding Path=RenderProfileElement.EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Restart}}">
|
||||
IsChecked="{Binding Path=RenderProfileElement.Timeline.EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Restart}}">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="12">
|
||||
<materialDesign:PackIcon Kind="Repeat" VerticalAlignment="Center" Margin="-3 0 0 -3" />
|
||||
RESTART
|
||||
@ -215,7 +215,7 @@
|
||||
</RadioButton>
|
||||
<RadioButton Grid.Column="1"
|
||||
Style="{StaticResource MaterialDesignTabRadioButton}"
|
||||
IsChecked="{Binding Path=RenderProfileElement.EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Ignore}}">
|
||||
IsChecked="{Binding Path=RenderProfileElement.Timeline.EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Ignore}}">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="12">
|
||||
<materialDesign:PackIcon Kind="EarHearingOff" VerticalAlignment="Center" Margin="-3 0 0 -3" />
|
||||
IGNORE
|
||||
@ -230,7 +230,7 @@
|
||||
</RadioButton>
|
||||
<RadioButton Grid.Column="2"
|
||||
Style="{StaticResource MaterialDesignTabRadioButton}"
|
||||
IsChecked="{Binding Path=RenderProfileElement.EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Copy}}">
|
||||
IsChecked="{Binding Path=RenderProfileElement.Timeline.EventOverlapMode, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static core:TimeLineEventOverlapMode.Copy}}">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="12">
|
||||
<materialDesign:PackIcon Kind="ContentCopy" VerticalAlignment="Center" Margin="-3 0 0 -3" />
|
||||
COPY
|
||||
|
||||
@ -43,24 +43,24 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
||||
|
||||
public bool DisplayContinuously
|
||||
{
|
||||
get => RenderProfileElement?.PlayMode == TimelinePlayMode.Repeat;
|
||||
get => RenderProfileElement?.Timeline.PlayMode == TimelinePlayMode.Repeat;
|
||||
set
|
||||
{
|
||||
TimelinePlayMode playMode = value ? TimelinePlayMode.Repeat : TimelinePlayMode.Once;
|
||||
if (RenderProfileElement == null || RenderProfileElement?.PlayMode == playMode) return;
|
||||
RenderProfileElement.PlayMode = playMode;
|
||||
if (RenderProfileElement == null || RenderProfileElement?.Timeline.PlayMode == playMode) return;
|
||||
RenderProfileElement.Timeline.PlayMode = playMode;
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
}
|
||||
|
||||
public bool AlwaysFinishTimeline
|
||||
{
|
||||
get => RenderProfileElement?.StopMode == TimelineStopMode.Finish;
|
||||
get => RenderProfileElement?.Timeline.StopMode == TimelineStopMode.Finish;
|
||||
set
|
||||
{
|
||||
TimelineStopMode stopMode = value ? TimelineStopMode.Finish : TimelineStopMode.SkipToEnd;
|
||||
if (RenderProfileElement == null || RenderProfileElement?.StopMode == stopMode) return;
|
||||
RenderProfileElement.StopMode = stopMode;
|
||||
if (RenderProfileElement == null || RenderProfileElement?.Timeline.StopMode == stopMode) return;
|
||||
RenderProfileElement.Timeline.StopMode = stopMode;
|
||||
_profileEditorService.UpdateSelectedProfileElement();
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,7 +237,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
}
|
||||
|
||||
// While playing in preview data bindings aren't updated
|
||||
Registration.DataBinding.Update(0.04);
|
||||
Registration.DataBinding.Update(Registration.LayerProperty.ProfileElement.Timeline);
|
||||
|
||||
if (ActiveItem.SupportsTestValue)
|
||||
{
|
||||
|
||||
@ -567,7 +567,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
{
|
||||
if (Repeating && RepeatTimeline)
|
||||
{
|
||||
if (newTime > SelectedProfileElement.TimelineLength)
|
||||
if (newTime > SelectedProfileElement.Timeline.Length)
|
||||
newTime = TimeSpan.Zero;
|
||||
}
|
||||
else if (Repeating && RepeatSegment)
|
||||
@ -575,9 +575,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
|
||||
if (newTime > GetCurrentSegmentEnd())
|
||||
newTime = GetCurrentSegmentStart();
|
||||
}
|
||||
else if (newTime > SelectedProfileElement.TimelineLength)
|
||||
else if (newTime > SelectedProfileElement.Timeline.Length)
|
||||
{
|
||||
newTime = SelectedProfileElement.TimelineLength;
|
||||
newTime = SelectedProfileElement.Timeline.Length;
|
||||
Pause();
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,8 +148,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
{
|
||||
if (position < TimeSpan.Zero)
|
||||
LayerPropertyKeyframe.Position = TimeSpan.Zero;
|
||||
else if (position > _profileEditorService.SelectedProfileElement.TimelineLength)
|
||||
LayerPropertyKeyframe.Position = _profileEditorService.SelectedProfileElement.TimelineLength;
|
||||
else if (position > _profileEditorService.SelectedProfileElement.Timeline.Length)
|
||||
LayerPropertyKeyframe.Position = _profileEditorService.SelectedProfileElement.Timeline.Length;
|
||||
else
|
||||
LayerPropertyKeyframe.Position = position;
|
||||
|
||||
@ -170,7 +170,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
);
|
||||
// If possible, shift the keyframe to the right by 11 pixels
|
||||
TimeSpan desiredPosition = newKeyframe.Position + TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 11);
|
||||
if (desiredPosition <= newKeyframe.LayerProperty.ProfileElement.TimelineLength)
|
||||
if (desiredPosition <= newKeyframe.LayerProperty.ProfileElement.Timeline.Length)
|
||||
newKeyframe.Position = desiredPosition;
|
||||
// Otherwise if possible shift it to the left by 11 pixels
|
||||
else
|
||||
|
||||
@ -56,10 +56,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
start ??= TimeSpan.Zero;
|
||||
end ??= TimeSpan.MaxValue;
|
||||
|
||||
List<LayerPropertyKeyframe<T>> toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position < end).ToList();
|
||||
List<LayerPropertyKeyframe<T>> toShift = LayerProperty.Keyframes.Where(k => k.Position > start && k.Position < end).ToList();
|
||||
foreach (LayerPropertyKeyframe<T> keyframe in toShift)
|
||||
keyframe.Position += amount;
|
||||
|
||||
|
||||
UpdateKeyframes();
|
||||
}
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
ProfileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
ProfileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
|
||||
if (ProfileEditorService.SelectedProfileElement != null)
|
||||
ProfileEditorService.SelectedProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
ProfileEditorService.SelectedProfileElement.Timeline.PropertyChanged += TimelineOnPropertyChanged;
|
||||
|
||||
Update();
|
||||
}
|
||||
@ -104,13 +104,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
{
|
||||
if (Segment != SegmentViewModelType.Main)
|
||||
return false;
|
||||
return SelectedProfileElement?.PlayMode == TimelinePlayMode.Repeat;
|
||||
return SelectedProfileElement?.Timeline.PlayMode == TimelinePlayMode.Repeat;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Segment != SegmentViewModelType.Main)
|
||||
if (Segment != SegmentViewModelType.Main || SelectedProfileElement == null)
|
||||
return;
|
||||
SelectedProfileElement.PlayMode = value ? TimelinePlayMode.Repeat : TimelinePlayMode.Once;
|
||||
SelectedProfileElement.Timeline.PlayMode = value ? TimelinePlayMode.Repeat : TimelinePlayMode.Once;
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
NotifyOfPropertyChange(nameof(RepeatSegment));
|
||||
}
|
||||
@ -156,32 +156,35 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
{
|
||||
if (SelectedProfileElement == null)
|
||||
{
|
||||
SegmentLength = TimeSpan.Zero;
|
||||
SegmentStart = TimeSpan.Zero;
|
||||
SegmentEnd = TimeSpan.Zero;
|
||||
SegmentLength = TimeSpan.Zero;
|
||||
SegmentStartPosition = 0;
|
||||
SegmentWidth = 0;
|
||||
SegmentEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// It would be nice to do this differently if this patterns end up appearing in more places
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
{
|
||||
SegmentLength = SelectedProfileElement.StartSegmentLength;
|
||||
SegmentStart = TimeSpan.Zero;
|
||||
SegmentEnd = SelectedProfileElement.Timeline.StartSegmentEndPosition;
|
||||
SegmentLength = SelectedProfileElement.Timeline.StartSegmentLength;
|
||||
}
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
{
|
||||
SegmentLength = SelectedProfileElement.MainSegmentLength;
|
||||
SegmentStart = SelectedProfileElement.StartSegmentLength;
|
||||
SegmentStart = SelectedProfileElement.Timeline.MainSegmentStartPosition;
|
||||
SegmentEnd = SelectedProfileElement.Timeline.MainSegmentEndPosition;
|
||||
SegmentLength = SelectedProfileElement.Timeline.MainSegmentLength;
|
||||
}
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
{
|
||||
SegmentLength = SelectedProfileElement.EndSegmentLength;
|
||||
SegmentStart = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength;
|
||||
SegmentStart = SelectedProfileElement.Timeline.EndSegmentStartPosition;
|
||||
SegmentEnd = SelectedProfileElement.Timeline.EndSegmentEndPosition;
|
||||
SegmentLength = SelectedProfileElement.Timeline.EndSegmentLength;
|
||||
}
|
||||
|
||||
SegmentEnd = SegmentStart + SegmentLength;
|
||||
SegmentStartPosition = SegmentStart.TotalSeconds * ProfileEditorService.PixelsPerSecond;
|
||||
SegmentWidth = SegmentLength.TotalSeconds * ProfileEditorService.PixelsPerSecond;
|
||||
SegmentEnabled = SegmentLength != TimeSpan.Zero;
|
||||
@ -195,28 +198,25 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
public void DisableSegment()
|
||||
{
|
||||
TimeSpan startSegmentEnd = SelectedProfileElement.StartSegmentLength;
|
||||
TimeSpan mainSegmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength;
|
||||
|
||||
TimeSpan oldSegmentLength = SegmentLength;
|
||||
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
{
|
||||
// Remove keyframes that fall in this segment
|
||||
WipeKeyframes(null, startSegmentEnd);
|
||||
SelectedProfileElement.StartSegmentLength = TimeSpan.Zero;
|
||||
WipeKeyframes(null, SelectedProfileElement.Timeline.StartSegmentEndPosition);
|
||||
SelectedProfileElement.Timeline.StartSegmentLength = TimeSpan.Zero;
|
||||
}
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
{
|
||||
// Remove keyframes that fall in this segment
|
||||
WipeKeyframes(startSegmentEnd, startSegmentEnd);
|
||||
SelectedProfileElement.MainSegmentLength = TimeSpan.Zero;
|
||||
WipeKeyframes(SelectedProfileElement.Timeline.MainSegmentStartPosition, SelectedProfileElement.Timeline.MainSegmentEndPosition);
|
||||
SelectedProfileElement.Timeline.MainSegmentLength = TimeSpan.Zero;
|
||||
}
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
{
|
||||
// Remove keyframes that fall in this segment
|
||||
WipeKeyframes(mainSegmentEnd, null);
|
||||
SelectedProfileElement.EndSegmentLength = TimeSpan.Zero;
|
||||
WipeKeyframes(SelectedProfileElement.Timeline.EndSegmentStartPosition, SelectedProfileElement.Timeline.EndSegmentEndPosition);
|
||||
SelectedProfileElement.Timeline.EndSegmentLength = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
ShiftNextSegment(SegmentLength - oldSegmentLength);
|
||||
@ -228,11 +228,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
{
|
||||
ShiftNextSegment(TimeSpan.FromSeconds(1));
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
SelectedProfileElement.StartSegmentLength = TimeSpan.FromSeconds(1);
|
||||
SelectedProfileElement.Timeline.StartSegmentLength = TimeSpan.FromSeconds(1);
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
SelectedProfileElement.MainSegmentLength = TimeSpan.FromSeconds(1);
|
||||
SelectedProfileElement.Timeline.MainSegmentLength = TimeSpan.FromSeconds(1);
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
SelectedProfileElement.EndSegmentLength = TimeSpan.FromSeconds(1);
|
||||
SelectedProfileElement.Timeline.EndSegmentLength = TimeSpan.FromSeconds(1);
|
||||
|
||||
ProfileEditorService.UpdateSelectedProfileElement();
|
||||
Update();
|
||||
@ -290,27 +290,24 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
public void UpdateLength(TimeSpan newTime)
|
||||
{
|
||||
TimeSpan oldSegmentLength = SegmentLength;
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
{
|
||||
if (newTime < TimeSpan.FromMilliseconds(100))
|
||||
newTime = TimeSpan.FromMilliseconds(100);
|
||||
SelectedProfileElement.StartSegmentLength = newTime;
|
||||
}
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
{
|
||||
if (newTime < SelectedProfileElement.StartSegmentLength + TimeSpan.FromMilliseconds(100))
|
||||
newTime = SelectedProfileElement.StartSegmentLength + TimeSpan.FromMilliseconds(100);
|
||||
SelectedProfileElement.MainSegmentLength = newTime - SelectedProfileElement.StartSegmentLength;
|
||||
}
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
{
|
||||
if (newTime < SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength + TimeSpan.FromMilliseconds(100))
|
||||
newTime = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength + TimeSpan.FromMilliseconds(100);
|
||||
SelectedProfileElement.EndSegmentLength = newTime - SelectedProfileElement.StartSegmentLength - SelectedProfileElement.MainSegmentLength;
|
||||
}
|
||||
if (newTime < TimeSpan.FromMilliseconds(100))
|
||||
newTime = TimeSpan.FromMilliseconds(100);
|
||||
|
||||
TimeSpan difference = newTime - SegmentEnd;
|
||||
|
||||
if (difference > TimeSpan.Zero)
|
||||
ShiftNextSegment(difference);
|
||||
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
SelectedProfileElement.Timeline.StartSegmentEndPosition = newTime;
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
SelectedProfileElement.Timeline.MainSegmentEndPosition = newTime;
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
SelectedProfileElement.Timeline.EndSegmentEndPosition = newTime;
|
||||
|
||||
if (difference < TimeSpan.Zero)
|
||||
ShiftNextSegment(difference);
|
||||
|
||||
ShiftNextSegment(SegmentLength - oldSegmentLength);
|
||||
Update();
|
||||
}
|
||||
|
||||
@ -323,7 +320,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
ProfileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
|
||||
if (SelectedProfileElement != null)
|
||||
SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
SelectedProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -333,20 +330,20 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
private void ProfileEditorServiceOnProfileElementSelected(object? sender, RenderProfileElementEventArgs e)
|
||||
{
|
||||
if (e.PreviousRenderProfileElement != null)
|
||||
e.PreviousRenderProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
e.PreviousRenderProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
|
||||
if (e.RenderProfileElement != null)
|
||||
e.RenderProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
e.RenderProfileElement.Timeline.PropertyChanged += TimelineOnPropertyChanged;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
private void SelectedProfileElementOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
private void TimelineOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(RenderProfileElement.StartSegmentLength) ||
|
||||
e.PropertyName == nameof(RenderProfileElement.MainSegmentLength) ||
|
||||
e.PropertyName == nameof(RenderProfileElement.EndSegmentLength))
|
||||
if (e.PropertyName == nameof(Core.Timeline.StartSegmentLength) ||
|
||||
e.PropertyName == nameof(Core.Timeline.MainSegmentLength) ||
|
||||
e.PropertyName == nameof(Core.Timeline.EndSegmentLength))
|
||||
Update();
|
||||
else if (e.PropertyName == nameof(RenderProfileElement.PlayMode))
|
||||
else if (e.PropertyName == nameof(Core.Timeline.PlayMode))
|
||||
NotifyOfPropertyChange(nameof(RepeatSegment));
|
||||
}
|
||||
|
||||
@ -359,15 +356,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
|
||||
public void ShiftNextSegment(TimeSpan amount)
|
||||
{
|
||||
TimeSpan segmentEnd = TimeSpan.Zero;
|
||||
if (Segment == SegmentViewModelType.Start)
|
||||
segmentEnd = SelectedProfileElement.StartSegmentLength;
|
||||
ShiftKeyframes(SelectedProfileElement.Timeline.StartSegmentEndPosition, null, amount);
|
||||
else if (Segment == SegmentViewModelType.Main)
|
||||
segmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength;
|
||||
ShiftKeyframes(SelectedProfileElement.Timeline.MainSegmentEndPosition, null, amount);
|
||||
else if (Segment == SegmentViewModelType.End)
|
||||
segmentEnd = SelectedProfileElement.TimelineLength;
|
||||
|
||||
ShiftKeyframes(segmentEnd, null, amount);
|
||||
ShiftKeyframes(SelectedProfileElement.Timeline.EndSegmentEndPosition, null, amount);
|
||||
}
|
||||
|
||||
private void WipeKeyframes(TimeSpan? start, TimeSpan? end)
|
||||
|
||||
@ -29,7 +29,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
_profileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
_profileEditorService.ProfileElementSelected += ProfileEditorServiceOnProfileElementSelected;
|
||||
if (_profileEditorService.SelectedProfileElement != null)
|
||||
_profileEditorService.SelectedProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
_profileEditorService.SelectedProfileElement.Timeline.PropertyChanged += TimelineOnPropertyChanged;
|
||||
Update();
|
||||
}
|
||||
|
||||
@ -75,9 +75,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
TotalTimelineWidth = EndSegmentEndPosition;
|
||||
}
|
||||
|
||||
private void SelectedProfileElementOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
private void TimelineOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
Update();
|
||||
if (e.PropertyName == nameof(Core.Timeline.StartSegmentLength) ||
|
||||
e.PropertyName == nameof(Core.Timeline.MainSegmentLength) ||
|
||||
e.PropertyName == nameof(Core.Timeline.EndSegmentLength))
|
||||
Update();
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnPixelsPerSecondChanged(object sender, EventArgs e)
|
||||
@ -88,9 +91,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
private void ProfileEditorServiceOnProfileElementSelected(object? sender, RenderProfileElementEventArgs e)
|
||||
{
|
||||
if (e.PreviousRenderProfileElement != null)
|
||||
e.PreviousRenderProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
e.PreviousRenderProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
|
||||
if (e.RenderProfileElement != null)
|
||||
e.RenderProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
|
||||
e.RenderProfileElement.Timeline.PropertyChanged += TimelineOnPropertyChanged;
|
||||
|
||||
Update();
|
||||
}
|
||||
@ -184,13 +187,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
// If shift is held, snap to the current time
|
||||
// Take a tolerance of 5 pixels (half a keyframe width)
|
||||
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
|
||||
{
|
||||
float tolerance = 1000f / _profileEditorService.PixelsPerSecond * 5;
|
||||
if (Math.Abs(_profileEditorService.CurrentTime.TotalMilliseconds - time.TotalMilliseconds) < tolerance)
|
||||
time = _profileEditorService.CurrentTime;
|
||||
else if (Math.Abs(_profileEditorService.SelectedProfileElement.StartSegmentLength.TotalMilliseconds - time.TotalMilliseconds) < tolerance)
|
||||
time = _profileEditorService.SelectedProfileElement.StartSegmentLength;
|
||||
}
|
||||
time = _profileEditorService.SnapToTimeline(time, TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 5), false, true);
|
||||
|
||||
return time;
|
||||
}
|
||||
@ -351,7 +348,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
|
||||
_profileEditorService.ProfileElementSelected -= ProfileEditorServiceOnProfileElementSelected;
|
||||
if (_profileEditorService.SelectedProfileElement != null)
|
||||
_profileEditorService.SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
|
||||
_profileEditorService.SelectedProfileElement.Timeline.PropertyChanged -= TimelineOnPropertyChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user