mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-01-02 10:43:31 +00:00
Profiles - Moved timeline logic to separate class (WIP)
This commit is contained in:
parent
df2f364cbe
commit
4ede3876d4
@ -74,7 +74,7 @@ namespace Artemis.Core
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Folder");
|
||||
|
||||
if (!Enabled)
|
||||
@ -104,7 +104,7 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public override void AddChild(ProfileElement child, int? order = null)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Folder");
|
||||
|
||||
base.AddChild(child, order);
|
||||
@ -115,7 +115,7 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public override void RemoveChild(ProfileElement child)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Folder");
|
||||
|
||||
base.RemoveChild(child);
|
||||
@ -130,7 +130,7 @@ namespace Artemis.Core
|
||||
|
||||
public void CalculateRenderProperties()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Folder");
|
||||
|
||||
SKPath path = new SKPath {FillType = SKPathFillType.Winding};
|
||||
@ -159,7 +159,7 @@ namespace Artemis.Core
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
Disposed = true;
|
||||
|
||||
foreach (ProfileElement profileElement in Children)
|
||||
profileElement.Dispose();
|
||||
@ -189,7 +189,7 @@ namespace Artemis.Core
|
||||
|
||||
internal override void Save()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Folder");
|
||||
|
||||
FolderEntity.Id = EntityId;
|
||||
@ -212,7 +212,7 @@ namespace Artemis.Core
|
||||
|
||||
public override void Render(SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Folder");
|
||||
|
||||
if (!Enabled || !Children.Any(c => c.Enabled))
|
||||
|
||||
@ -138,7 +138,7 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
Disposed = true;
|
||||
|
||||
// Brush first in case it depends on any of the other disposables during it's own disposal
|
||||
_layerBrush?.Dispose();
|
||||
@ -199,7 +199,7 @@ namespace Artemis.Core
|
||||
|
||||
internal override void Save()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
// Properties
|
||||
@ -264,7 +264,7 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
if (!Enabled)
|
||||
@ -285,7 +285,7 @@ namespace Artemis.Core
|
||||
/// <inheritdoc />
|
||||
public override void Render(SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
if (!Enabled)
|
||||
@ -446,7 +446,7 @@ namespace Artemis.Core
|
||||
|
||||
internal void CalculateRenderProperties()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
if (!Leds.Any())
|
||||
@ -475,7 +475,7 @@ namespace Artemis.Core
|
||||
|
||||
internal SKPoint GetLayerAnchorPosition(SKPath layerPath, bool zeroBased = false)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
SKPoint positionProperty = Transform.Position.CurrentValue;
|
||||
@ -499,7 +499,7 @@ namespace Artemis.Core
|
||||
/// <param name="path"></param>
|
||||
public void IncludePathInTranslation(SKPath path, bool zeroBased)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
SKSize sizeProperty = Transform.Scale.CurrentValue;
|
||||
@ -535,7 +535,7 @@ namespace Artemis.Core
|
||||
/// <returns>The transformation matrix containing the current transformation settings</returns>
|
||||
public SKMatrix GetTransformMatrix(bool zeroBased)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
SKSize sizeProperty = Transform.Scale.CurrentValue;
|
||||
@ -569,7 +569,7 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public void ExcludePathFromTranslation(SKPath path, bool zeroBased)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
SKSize sizeProperty = Transform.Scale.CurrentValue;
|
||||
@ -605,7 +605,7 @@ namespace Artemis.Core
|
||||
/// <returns>The number of transformations applied</returns>
|
||||
public int ExcludeCanvasFromTranslation(SKCanvas canvas, bool zeroBased)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
SKSize sizeProperty = Transform.Scale.CurrentValue;
|
||||
@ -645,7 +645,7 @@ namespace Artemis.Core
|
||||
/// <param name="led">The LED to add</param>
|
||||
public void AddLed(ArtemisLed led)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
_leds.Add(led);
|
||||
@ -658,7 +658,7 @@ namespace Artemis.Core
|
||||
/// <param name="leds">The LEDs to add</param>
|
||||
public void AddLeds(IEnumerable<ArtemisLed> leds)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
_leds.AddRange(leds);
|
||||
@ -671,7 +671,7 @@ namespace Artemis.Core
|
||||
/// <param name="led">The LED to remove</param>
|
||||
public void RemoveLed(ArtemisLed led)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
_leds.Remove(led);
|
||||
@ -683,7 +683,7 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public void ClearLeds()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
_leds.Clear();
|
||||
@ -692,7 +692,7 @@ namespace Artemis.Core
|
||||
|
||||
internal void PopulateLeds(ArtemisSurface surface)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
List<ArtemisLed> leds = new List<ArtemisLed>();
|
||||
|
||||
@ -56,7 +56,7 @@ namespace Artemis.Core
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
if (!IsActivated)
|
||||
throw new ArtemisCoreException($"Cannot update inactive profile: {this}");
|
||||
@ -70,7 +70,7 @@ namespace Artemis.Core
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
if (!IsActivated)
|
||||
throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
|
||||
@ -89,7 +89,7 @@ namespace Artemis.Core
|
||||
|
||||
public Folder GetRootFolder()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
|
||||
return (Folder) Children.Single();
|
||||
@ -97,7 +97,7 @@ namespace Artemis.Core
|
||||
|
||||
internal override void Load()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
|
||||
Name = ProfileEntity.Name;
|
||||
@ -137,12 +137,12 @@ namespace Artemis.Core
|
||||
ChildrenList.Clear();
|
||||
|
||||
IsActivated = false;
|
||||
_disposed = true;
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
internal override void Save()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
|
||||
ProfileEntity.Id = EntityId;
|
||||
@ -164,7 +164,7 @@ namespace Artemis.Core
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
if (IsActivated)
|
||||
return;
|
||||
@ -177,7 +177,7 @@ namespace Artemis.Core
|
||||
|
||||
internal void PopulateLeds(ArtemisSurface surface)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Profile");
|
||||
|
||||
foreach (Layer layer in GetAllLayers())
|
||||
|
||||
@ -9,13 +9,13 @@ namespace Artemis.Core
|
||||
{
|
||||
public abstract class ProfileElement : PropertyChangedBase, IDisposable
|
||||
{
|
||||
protected bool _disposed;
|
||||
private bool _enabled;
|
||||
private Guid _entityId;
|
||||
private string _name;
|
||||
private int _order;
|
||||
private ProfileElement _parent;
|
||||
private Profile _profile;
|
||||
protected bool Disposed;
|
||||
protected List<ProfileElement> ChildrenList;
|
||||
|
||||
protected ProfileElement()
|
||||
@ -113,7 +113,7 @@ namespace Artemis.Core
|
||||
/// <param name="order">The order where to place the child (1-based), defaults to the end of the collection</param>
|
||||
public virtual void AddChild(ProfileElement child, int? order = null)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
|
||||
lock (ChildrenList)
|
||||
@ -157,7 +157,7 @@ namespace Artemis.Core
|
||||
/// <param name="child">The profile element to remove</param>
|
||||
public virtual void RemoveChild(ProfileElement child)
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
|
||||
lock (ChildrenList)
|
||||
@ -180,7 +180,7 @@ namespace Artemis.Core
|
||||
/// <returns></returns>
|
||||
public List<Folder> GetAllFolders()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
|
||||
List<Folder> folders = new List<Folder>();
|
||||
@ -201,7 +201,7 @@ namespace Artemis.Core
|
||||
/// <returns></returns>
|
||||
public List<Layer> GetAllLayers()
|
||||
{
|
||||
if (_disposed)
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
|
||||
List<Layer> layers = new List<Layer>();
|
||||
|
||||
@ -17,7 +17,6 @@ namespace Artemis.Core
|
||||
{
|
||||
ApplyDataBindingsEnabled = true;
|
||||
ApplyKeyframesEnabled = true;
|
||||
ExtraTimeLines = new List<TimeSpan>();
|
||||
|
||||
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
|
||||
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
|
||||
@ -25,51 +24,21 @@ namespace Artemis.Core
|
||||
|
||||
public abstract List<ILayerProperty> GetAllLayerProperties();
|
||||
|
||||
#region IDisposable
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
LayerEffectStore.LayerEffectAdded -= LayerEffectStoreOnLayerEffectAdded;
|
||||
LayerEffectStore.LayerEffectRemoved -= LayerEffectStoreOnLayerEffectRemoved;
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal void ApplyRenderElementDefaults()
|
||||
{
|
||||
MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||
}
|
||||
|
||||
internal void LoadRenderElement()
|
||||
{
|
||||
StartSegmentLength = RenderElementEntity.StartSegmentLength;
|
||||
MainSegmentLength = RenderElementEntity.MainSegmentLength;
|
||||
EndSegmentLength = RenderElementEntity.EndSegmentLength;
|
||||
PlayMode = (TimelinePlayMode) RenderElementEntity.PlayMode;
|
||||
StopMode = (TimelineStopMode) RenderElementEntity.StopMode;
|
||||
EventOverlapMode = (TimeLineEventOverlapMode) RenderElementEntity.EventOverlapMode;
|
||||
|
||||
DisplayCondition = RenderElementEntity.DisplayCondition != null
|
||||
? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
|
||||
: new DataModelConditionGroup(null);
|
||||
|
||||
Timeline = RenderElementEntity.Timeline != null
|
||||
? new Timeline(RenderElementEntity.Timeline)
|
||||
: new Timeline();
|
||||
|
||||
ActivateEffects();
|
||||
}
|
||||
|
||||
internal void SaveRenderElement()
|
||||
{
|
||||
RenderElementEntity.StartSegmentLength = StartSegmentLength;
|
||||
RenderElementEntity.MainSegmentLength = MainSegmentLength;
|
||||
RenderElementEntity.EndSegmentLength = EndSegmentLength;
|
||||
RenderElementEntity.PlayMode = (int) PlayMode;
|
||||
RenderElementEntity.StopMode = (int) StopMode;
|
||||
RenderElementEntity.EventOverlapMode = (int) EventOverlapMode;
|
||||
|
||||
RenderElementEntity.LayerEffects.Clear();
|
||||
foreach (BaseLayerEffect layerEffect in LayerEffects)
|
||||
{
|
||||
@ -90,8 +59,36 @@ namespace Artemis.Core
|
||||
// Conditions
|
||||
RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
|
||||
DisplayCondition?.Save();
|
||||
|
||||
// Timeline
|
||||
RenderElementEntity.Timeline = Timeline?.Entity;
|
||||
Timeline?.Save();
|
||||
}
|
||||
|
||||
#region Timeline
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timeline associated with this render element
|
||||
/// </summary>
|
||||
public Timeline? Timeline { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
LayerEffectStore.LayerEffectAdded -= LayerEffectStoreOnLayerEffectAdded;
|
||||
LayerEffectStore.LayerEffectRemoved -= LayerEffectStoreOnLayerEffectRemoved;
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
private SKPath _path;
|
||||
@ -144,168 +141,6 @@ namespace Artemis.Core
|
||||
|
||||
#endregion
|
||||
|
||||
#region Timeline
|
||||
|
||||
private TimeSpan _startSegmentLength;
|
||||
private TimeSpan _mainSegmentLength;
|
||||
private TimeSpan _endSegmentLength;
|
||||
private TimeSpan _timeLine;
|
||||
private TimelinePlayMode _playMode;
|
||||
private TimelineStopMode _stopMode;
|
||||
private TimeLineEventOverlapMode _eventOverlapMode;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the start segment
|
||||
/// </summary>
|
||||
public TimeSpan StartSegmentLength
|
||||
{
|
||||
get => _startSegmentLength;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _startSegmentLength, value)) return;
|
||||
UpdateTimeLineLength();
|
||||
if (Parent is RenderProfileElement renderElement)
|
||||
renderElement.UpdateTimeLineLength();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the main segment
|
||||
/// </summary>
|
||||
public TimeSpan MainSegmentLength
|
||||
{
|
||||
get => _mainSegmentLength;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _mainSegmentLength, value)) return;
|
||||
UpdateTimeLineLength();
|
||||
if (Parent is RenderProfileElement renderElement)
|
||||
renderElement.UpdateTimeLineLength();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the end segment
|
||||
/// </summary>
|
||||
public TimeSpan EndSegmentLength
|
||||
{
|
||||
get => _endSegmentLength;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _endSegmentLength, value)) return;
|
||||
UpdateTimeLineLength();
|
||||
if (Parent is RenderProfileElement renderElement)
|
||||
renderElement.UpdateTimeLineLength();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current time line position
|
||||
/// </summary>
|
||||
public TimeSpan TimeLine
|
||||
{
|
||||
get => _timeLine;
|
||||
protected set => SetAndNotify(ref _timeLine, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of extra time lines to render the element at each frame. Extra time lines are removed when they reach
|
||||
/// their end
|
||||
/// </summary>
|
||||
public List<TimeSpan> ExtraTimeLines { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element starts its timeline when display conditions are met
|
||||
/// </summary>
|
||||
public TimelinePlayMode PlayMode
|
||||
{
|
||||
get => _playMode;
|
||||
set => SetAndNotify(ref _playMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
|
||||
/// </summary>
|
||||
public TimelineStopMode StopMode
|
||||
{
|
||||
get => _stopMode;
|
||||
set => SetAndNotify(ref _stopMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element responds to display condition events being fired before the
|
||||
/// timeline finished
|
||||
/// </summary>
|
||||
public TimeLineEventOverlapMode EventOverlapMode
|
||||
{
|
||||
get => _eventOverlapMode;
|
||||
set => SetAndNotify(ref _eventOverlapMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max length of this element and any of its children
|
||||
/// </summary>
|
||||
public TimeSpan TimelineLength { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the time line and any extra time lines present in <see cref="ExtraTimeLines" />
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The delta with which to move the time lines</param>
|
||||
protected void UpdateTimeLines(double deltaTime)
|
||||
{
|
||||
bool repeatMainSegment = PlayMode == TimelinePlayMode.Repeat;
|
||||
bool finishMainSegment = StopMode == TimelineStopMode.Finish;
|
||||
|
||||
// If the condition is event-based, never display continuously and always finish the timeline
|
||||
if (DisplayCondition != null && DisplayCondition.ContainsEvents)
|
||||
{
|
||||
repeatMainSegment = false;
|
||||
finishMainSegment = true;
|
||||
}
|
||||
|
||||
TimeSpan deltaTimeSpan = TimeSpan.FromSeconds(deltaTime);
|
||||
TimeSpan mainSegmentEnd = StartSegmentLength + MainSegmentLength;
|
||||
|
||||
// Update the main time line
|
||||
if (TimeLine != TimeSpan.Zero || DisplayConditionMet)
|
||||
{
|
||||
TimeLine += deltaTimeSpan;
|
||||
|
||||
// Apply play and stop mode
|
||||
if (DisplayConditionMet && repeatMainSegment && TimeLine >= mainSegmentEnd)
|
||||
TimeLine = StartSegmentLength;
|
||||
else if (!DisplayConditionMet && !finishMainSegment)
|
||||
TimeLine = mainSegmentEnd.Add(new TimeSpan(1));
|
||||
}
|
||||
|
||||
lock (ExtraTimeLines)
|
||||
{
|
||||
// Remove extra time lines that have finished
|
||||
ExtraTimeLines.RemoveAll(t => t >= mainSegmentEnd);
|
||||
// Update remaining extra time lines
|
||||
for (int index = 0; index < ExtraTimeLines.Count; index++)
|
||||
ExtraTimeLines[index] += deltaTimeSpan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the time line to the specified time and clears any extra time lines
|
||||
/// </summary>
|
||||
/// <param name="time">The time to override to</param>
|
||||
/// <param name="stickToMainSegment">Whether to stick to the main segment, wrapping around if needed</param>
|
||||
public void OverrideTimeLines(TimeSpan time, bool stickToMainSegment)
|
||||
{
|
||||
ExtraTimeLines.Clear();
|
||||
TimeLine = time;
|
||||
|
||||
if (stickToMainSegment && TimeLine > StartSegmentLength)
|
||||
TimeLine = StartSegmentLength + TimeSpan.FromMilliseconds(time.TotalMilliseconds % MainSegmentLength.TotalMilliseconds);
|
||||
}
|
||||
|
||||
protected internal abstract void UpdateTimeLineLength();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Effect management
|
||||
|
||||
protected List<BaseLayerEffect> _layerEffects;
|
||||
@ -475,20 +310,22 @@ namespace Artemis.Core
|
||||
// Regular conditions reset the timeline whenever their condition is met and was not met before that
|
||||
if (!DisplayCondition.ContainsEvents)
|
||||
{
|
||||
if (conditionMet && !DisplayConditionMet && TimeLine > TimelineLength)
|
||||
TimeLine = TimeSpan.Zero;
|
||||
if (conditionMet && !DisplayConditionMet && Timeline.IsFinished)
|
||||
Timeline.JumpToStart();
|
||||
}
|
||||
// Event conditions reset if the timeline finished and otherwise apply their overlap mode
|
||||
else if (conditionMet)
|
||||
{
|
||||
if (TimeLine > TimelineLength)
|
||||
TimeLine = TimeSpan.Zero;
|
||||
if (Timeline.IsFinished)
|
||||
{
|
||||
Timeline.JumpToStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EventOverlapMode == TimeLineEventOverlapMode.Restart)
|
||||
TimeLine = TimeSpan.Zero;
|
||||
else if (EventOverlapMode == TimeLineEventOverlapMode.Copy)
|
||||
ExtraTimeLines.Add(new TimeSpan());
|
||||
if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Restart)
|
||||
Timeline.JumpToStart();
|
||||
else if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Copy)
|
||||
Timeline.AddExtraTimeline();
|
||||
}
|
||||
}
|
||||
|
||||
@ -508,57 +345,4 @@ namespace Artemis.Core
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to start their timeline when display conditions are met
|
||||
/// </summary>
|
||||
public enum TimelinePlayMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Continue repeating the main segment of the timeline while the condition is met
|
||||
/// </summary>
|
||||
Repeat,
|
||||
|
||||
/// <summary>
|
||||
/// Only play the timeline once when the condition is met
|
||||
/// </summary>
|
||||
Once
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to stop their timeline when display conditions are no longer met
|
||||
/// </summary>
|
||||
public enum TimelineStopMode
|
||||
{
|
||||
/// <summary>
|
||||
/// When conditions are no longer met, finish the the current run of the main timeline
|
||||
/// </summary>
|
||||
Finish,
|
||||
|
||||
/// <summary>
|
||||
/// When conditions are no longer met, skip to the end segment of the timeline
|
||||
/// </summary>
|
||||
SkipToEnd
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to start their timeline when display conditions events are fired
|
||||
/// </summary>
|
||||
public enum TimeLineEventOverlapMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Stop the current run and restart the timeline
|
||||
/// </summary>
|
||||
Restart,
|
||||
|
||||
/// <summary>
|
||||
/// Ignore subsequent event fires until the timeline finishes
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
/// <summary>
|
||||
/// Play another copy of the timeline on top of the current run
|
||||
/// </summary>
|
||||
Copy
|
||||
}
|
||||
}
|
||||
350
src/Artemis.Core/Models/Profile/Timeline.cs
Normal file
350
src/Artemis.Core/Models/Profile/Timeline.cs
Normal file
@ -0,0 +1,350 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a timeline used by profile elements
|
||||
/// </summary>
|
||||
public class Timeline : PropertyChangedBase, IStorageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="Timeline" /> class
|
||||
/// </summary>
|
||||
public Timeline()
|
||||
{
|
||||
Entity = new TimelineEntity();
|
||||
_extraTimelines = new List<Timeline>();
|
||||
MainSegmentLength = TimeSpan.FromSeconds(5);
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
internal Timeline(TimelineEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
_extraTimelines = new List<Timeline>();
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
private Timeline(Timeline parent)
|
||||
{
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
#region Extra timelines
|
||||
|
||||
/// <summary>
|
||||
/// Adds an extra timeline to this timeline
|
||||
/// </summary>
|
||||
public void AddExtraTimeline()
|
||||
{
|
||||
_extraTimelines.Add(new Timeline(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all extra timelines from this timeline
|
||||
/// </summary>
|
||||
public void ClearExtraTimelines()
|
||||
{
|
||||
_extraTimelines.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
private TimeSpan _position;
|
||||
private TimeSpan _lastDelta;
|
||||
private TimeLineEventOverlapMode _eventOverlapMode;
|
||||
private TimelinePlayMode _playMode;
|
||||
private TimelineStopMode _stopMode;
|
||||
private readonly List<Timeline> _extraTimelines;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent this timeline is an extra timeline of
|
||||
/// </summary>
|
||||
public Timeline Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current position of the timeline
|
||||
/// </summary>
|
||||
public TimeSpan Position
|
||||
{
|
||||
get => _position;
|
||||
private set => SetAndNotify(ref _position, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delta that was applied during the last call to <see cref="Update" />
|
||||
/// </summary>
|
||||
public TimeSpan LastDelta
|
||||
{
|
||||
get => _lastDelta;
|
||||
private set => SetAndNotify(ref _lastDelta, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element starts its timeline when display conditions are met
|
||||
/// </summary>
|
||||
public TimelinePlayMode PlayMode
|
||||
{
|
||||
get => _playMode;
|
||||
set => SetAndNotify(ref _playMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
|
||||
/// </summary>
|
||||
public TimelineStopMode StopMode
|
||||
{
|
||||
get => _stopMode;
|
||||
set => SetAndNotify(ref _stopMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode in which the render element responds to display condition events being fired before the
|
||||
/// timeline finished
|
||||
/// </summary>
|
||||
public TimeLineEventOverlapMode EventOverlapMode
|
||||
{
|
||||
get => _eventOverlapMode;
|
||||
set => SetAndNotify(ref _eventOverlapMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of extra copies of the timeline applied to this timeline
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<Timeline> ExtraTimelines => _extraTimelines.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the timeline has finished its run
|
||||
/// </summary>
|
||||
public bool IsFinished => Position > Length;
|
||||
|
||||
#region Segments
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total length of this timeline
|
||||
/// </summary>
|
||||
public TimeSpan Length => StartSegmentLength + MainSegmentLength + EndSegmentLength;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the start segment
|
||||
/// </summary>
|
||||
public TimeSpan StartSegmentLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the main segment
|
||||
/// </summary>
|
||||
public TimeSpan MainSegmentLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the end segment
|
||||
/// </summary>
|
||||
public TimeSpan EndSegmentLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start position of the main segment
|
||||
/// </summary>
|
||||
public TimeSpan MainSegmentStartPosition
|
||||
{
|
||||
get => StartSegmentEndPosition;
|
||||
set => StartSegmentEndPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end position of the end segment
|
||||
/// </summary>
|
||||
public TimeSpan EndSegmentStartPosition
|
||||
{
|
||||
get => MainSegmentEndPosition;
|
||||
set => MainSegmentEndPosition = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end position of the start segment
|
||||
/// </summary>
|
||||
public TimeSpan StartSegmentEndPosition
|
||||
{
|
||||
get => StartSegmentLength;
|
||||
set => StartSegmentLength = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end position of the main segment
|
||||
/// </summary>
|
||||
public TimeSpan MainSegmentEndPosition
|
||||
{
|
||||
get => StartSegmentEndPosition + MainSegmentLength;
|
||||
set => MainSegmentLength = value - StartSegmentEndPosition >= TimeSpan.Zero ? value - StartSegmentEndPosition : TimeSpan.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end position of the end segment
|
||||
/// </summary>
|
||||
public TimeSpan EndSegmentEndPosition
|
||||
{
|
||||
get => MainSegmentEndPosition + EndSegmentLength;
|
||||
set => EndSegmentLength = value - MainSegmentEndPosition >= TimeSpan.Zero ? value - MainSegmentEndPosition : TimeSpan.Zero;
|
||||
}
|
||||
|
||||
internal TimelineEntity Entity { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Updating
|
||||
|
||||
/// <summary>
|
||||
/// Updates the timeline, applying the provided <paramref name="delta" /> to the <see cref="Position" />
|
||||
/// </summary>
|
||||
/// <param name="delta">The amount of time to apply to the position</param>
|
||||
/// <param name="stickToMainSegment">Whether to stick to the main segment, wrapping around if needed</param>
|
||||
public void Update(TimeSpan delta, bool stickToMainSegment)
|
||||
{
|
||||
LastDelta = delta;
|
||||
Position += delta;
|
||||
|
||||
if (stickToMainSegment && Position >= MainSegmentStartPosition)
|
||||
Position = MainSegmentStartPosition + TimeSpan.FromMilliseconds(Position.TotalMilliseconds % MainSegmentLength.TotalMilliseconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the position of the timeline backwards to the very start of the timeline
|
||||
/// </summary>
|
||||
public void JumpToStart()
|
||||
{
|
||||
if (Position == TimeSpan.Zero)
|
||||
return;
|
||||
|
||||
LastDelta = TimeSpan.Zero - Position;
|
||||
Position = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the position of the timeline forwards to the beginning of the end segment
|
||||
/// </summary>
|
||||
public void JumpToEndSegment()
|
||||
{
|
||||
if (Position >= EndSegmentStartPosition)
|
||||
return;
|
||||
|
||||
LastDelta = EndSegmentStartPosition - Position;
|
||||
Position = EndSegmentStartPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the position of the timeline forwards to the very end of the timeline
|
||||
/// </summary>
|
||||
public void JumpToEnd()
|
||||
{
|
||||
if (Position >= EndSegmentEndPosition)
|
||||
return;
|
||||
|
||||
LastDelta = EndSegmentEndPosition - Position;
|
||||
Position = EndSegmentEndPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the <see cref="Position" /> to the specified time and clears any extra time lines
|
||||
/// </summary>
|
||||
/// <param name="position">The position to set the timeline to</param>
|
||||
/// <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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Storage
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
StartSegmentLength = Entity.StartSegmentLength;
|
||||
MainSegmentLength = Entity.MainSegmentLength;
|
||||
EndSegmentLength = Entity.EndSegmentLength;
|
||||
PlayMode = (TimelinePlayMode) Entity.PlayMode;
|
||||
StopMode = (TimelineStopMode) Entity.StopMode;
|
||||
EventOverlapMode = (TimeLineEventOverlapMode) Entity.EventOverlapMode;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
Entity.StartSegmentLength = StartSegmentLength;
|
||||
Entity.MainSegmentLength = MainSegmentLength;
|
||||
Entity.EndSegmentLength = EndSegmentLength;
|
||||
Entity.PlayMode = (int) PlayMode;
|
||||
Entity.StopMode = (int) StopMode;
|
||||
Entity.EventOverlapMode = (int) EventOverlapMode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to start their timeline when display conditions are met
|
||||
/// </summary>
|
||||
public enum TimelinePlayMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Continue repeating the main segment of the timeline while the condition is met
|
||||
/// </summary>
|
||||
Repeat,
|
||||
|
||||
/// <summary>
|
||||
/// Only play the timeline once when the condition is met
|
||||
/// </summary>
|
||||
Once
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to stop their timeline when display conditions are no longer met
|
||||
/// </summary>
|
||||
public enum TimelineStopMode
|
||||
{
|
||||
/// <summary>
|
||||
/// When conditions are no longer met, finish the the current run of the main timeline
|
||||
/// </summary>
|
||||
Finish,
|
||||
|
||||
/// <summary>
|
||||
/// When conditions are no longer met, skip to the end segment of the timeline
|
||||
/// </summary>
|
||||
SkipToEnd
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode for render elements to start their timeline when display conditions events are fired
|
||||
/// </summary>
|
||||
public enum TimeLineEventOverlapMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Stop the current run and restart the timeline
|
||||
/// </summary>
|
||||
Restart,
|
||||
|
||||
/// <summary>
|
||||
/// Ignore subsequent event fires until the timeline finishes
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
/// <summary>
|
||||
/// Play another copy of the timeline on top of the current run
|
||||
/// </summary>
|
||||
Copy
|
||||
}
|
||||
}
|
||||
@ -6,18 +6,11 @@ namespace Artemis.Storage.Entities.Profile.Abstract
|
||||
{
|
||||
public abstract class RenderElementEntity
|
||||
{
|
||||
public TimeSpan StartSegmentLength { get; set; }
|
||||
public TimeSpan MainSegmentLength { get; set; }
|
||||
public TimeSpan EndSegmentLength { get; set; }
|
||||
|
||||
public int PlayMode { get; set; }
|
||||
public int StopMode { get; set; }
|
||||
public int EventOverlapMode { get; set; }
|
||||
|
||||
public List<LayerEffectEntity> LayerEffects { get; set; }
|
||||
public List<PropertyEntity> PropertyEntities { get; set; }
|
||||
public List<string> ExpandedPropertyGroups { get; set; }
|
||||
|
||||
public DataModelConditionGroupEntity DisplayCondition { get; set; }
|
||||
public TimelineEntity Timeline { get; set; }
|
||||
}
|
||||
}
|
||||
15
src/Artemis.Storage/Entities/Profile/TimelineEntity.cs
Normal file
15
src/Artemis.Storage/Entities/Profile/TimelineEntity.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Storage.Entities.Profile
|
||||
{
|
||||
public class TimelineEntity
|
||||
{
|
||||
public TimeSpan StartSegmentLength { get; set; }
|
||||
public TimeSpan MainSegmentLength { get; set; }
|
||||
public TimeSpan EndSegmentLength { get; set; }
|
||||
|
||||
public int PlayMode { get; set; }
|
||||
public int StopMode { get; set; }
|
||||
public int EventOverlapMode { get; set; }
|
||||
}
|
||||
}
|
||||
@ -221,4 +221,5 @@
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=luma/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=luma/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Timelines/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
Loading…
x
Reference in New Issue
Block a user