diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index 07733c0c4..5e489b190 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -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
///
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
///
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))
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 30c3aae39..f767ba514 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -138,7 +138,7 @@ namespace Artemis.Core
///
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
///
public override void Update(double deltaTime)
{
- if (_disposed)
+ if (Disposed)
throw new ObjectDisposedException("Layer");
if (!Enabled)
@@ -285,7 +285,7 @@ namespace Artemis.Core
///
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
///
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
/// The transformation matrix containing the current transformation settings
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
///
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
/// The number of transformations applied
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
/// The LED to add
public void AddLed(ArtemisLed led)
{
- if (_disposed)
+ if (Disposed)
throw new ObjectDisposedException("Layer");
_leds.Add(led);
@@ -658,7 +658,7 @@ namespace Artemis.Core
/// The LEDs to add
public void AddLeds(IEnumerable leds)
{
- if (_disposed)
+ if (Disposed)
throw new ObjectDisposedException("Layer");
_leds.AddRange(leds);
@@ -671,7 +671,7 @@ namespace Artemis.Core
/// The LED to remove
public void RemoveLed(ArtemisLed led)
{
- if (_disposed)
+ if (Disposed)
throw new ObjectDisposedException("Layer");
_leds.Remove(led);
@@ -683,7 +683,7 @@ namespace Artemis.Core
///
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 leds = new List();
diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs
index d02ad8689..786ed8369 100644
--- a/src/Artemis.Core/Models/Profile/Profile.cs
+++ b/src/Artemis.Core/Models/Profile/Profile.cs
@@ -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())
diff --git a/src/Artemis.Core/Models/Profile/ProfileElement.cs b/src/Artemis.Core/Models/Profile/ProfileElement.cs
index b7bd5f56d..56b86b240 100644
--- a/src/Artemis.Core/Models/Profile/ProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/ProfileElement.cs
@@ -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 ChildrenList;
protected ProfileElement()
@@ -113,7 +113,7 @@ namespace Artemis.Core
/// The order where to place the child (1-based), defaults to the end of the collection
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
/// The profile element to remove
public virtual void RemoveChild(ProfileElement child)
{
- if (_disposed)
+ if (Disposed)
throw new ObjectDisposedException(GetType().Name);
lock (ChildrenList)
@@ -180,7 +180,7 @@ namespace Artemis.Core
///
public List GetAllFolders()
{
- if (_disposed)
+ if (Disposed)
throw new ObjectDisposedException(GetType().Name);
List folders = new List();
@@ -201,7 +201,7 @@ namespace Artemis.Core
///
public List GetAllLayers()
{
- if (_disposed)
+ if (Disposed)
throw new ObjectDisposedException(GetType().Name);
List layers = new List();
diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
index 99fefe907..dd530719f 100644
--- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
@@ -17,7 +17,6 @@ namespace Artemis.Core
{
ApplyDataBindingsEnabled = true;
ApplyKeyframesEnabled = true;
- ExtraTimeLines = new List();
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
@@ -25,51 +24,21 @@ namespace Artemis.Core
public abstract List 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
+
+ ///
+ /// Gets the timeline associated with this render element
+ ///
+ 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;
-
- ///
- /// Gets or sets the length of the start segment
- ///
- public TimeSpan StartSegmentLength
- {
- get => _startSegmentLength;
- set
- {
- if (!SetAndNotify(ref _startSegmentLength, value)) return;
- UpdateTimeLineLength();
- if (Parent is RenderProfileElement renderElement)
- renderElement.UpdateTimeLineLength();
- }
- }
-
- ///
- /// Gets or sets the length of the main segment
- ///
- public TimeSpan MainSegmentLength
- {
- get => _mainSegmentLength;
- set
- {
- if (!SetAndNotify(ref _mainSegmentLength, value)) return;
- UpdateTimeLineLength();
- if (Parent is RenderProfileElement renderElement)
- renderElement.UpdateTimeLineLength();
- }
- }
-
- ///
- /// Gets or sets the length of the end segment
- ///
- public TimeSpan EndSegmentLength
- {
- get => _endSegmentLength;
- set
- {
- if (!SetAndNotify(ref _endSegmentLength, value)) return;
- UpdateTimeLineLength();
- if (Parent is RenderProfileElement renderElement)
- renderElement.UpdateTimeLineLength();
- }
- }
-
- ///
- /// Gets the current time line position
- ///
- public TimeSpan TimeLine
- {
- get => _timeLine;
- protected set => SetAndNotify(ref _timeLine, value);
- }
-
- ///
- /// Gets a list of extra time lines to render the element at each frame. Extra time lines are removed when they reach
- /// their end
- ///
- public List ExtraTimeLines { get; }
-
- ///
- /// Gets or sets the mode in which the render element starts its timeline when display conditions are met
- ///
- public TimelinePlayMode PlayMode
- {
- get => _playMode;
- set => SetAndNotify(ref _playMode, value);
- }
-
- ///
- /// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
- ///
- public TimelineStopMode StopMode
- {
- get => _stopMode;
- set => SetAndNotify(ref _stopMode, value);
- }
-
- ///
- /// Gets or sets the mode in which the render element responds to display condition events being fired before the
- /// timeline finished
- ///
- public TimeLineEventOverlapMode EventOverlapMode
- {
- get => _eventOverlapMode;
- set => SetAndNotify(ref _eventOverlapMode, value);
- }
-
- ///
- /// Gets the max length of this element and any of its children
- ///
- public TimeSpan TimelineLength { get; protected set; }
-
- ///
- /// Updates the time line and any extra time lines present in
- ///
- /// The delta with which to move the time lines
- 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;
- }
- }
-
- ///
- /// Overrides the time line to the specified time and clears any extra time lines
- ///
- /// The time to override to
- /// Whether to stick to the main segment, wrapping around if needed
- 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 _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
}
-
- ///
- /// Represents a mode for render elements to start their timeline when display conditions are met
- ///
- public enum TimelinePlayMode
- {
- ///
- /// Continue repeating the main segment of the timeline while the condition is met
- ///
- Repeat,
-
- ///
- /// Only play the timeline once when the condition is met
- ///
- Once
- }
-
- ///
- /// Represents a mode for render elements to stop their timeline when display conditions are no longer met
- ///
- public enum TimelineStopMode
- {
- ///
- /// When conditions are no longer met, finish the the current run of the main timeline
- ///
- Finish,
-
- ///
- /// When conditions are no longer met, skip to the end segment of the timeline
- ///
- SkipToEnd
- }
-
- ///
- /// Represents a mode for render elements to start their timeline when display conditions events are fired
- ///
- public enum TimeLineEventOverlapMode
- {
- ///
- /// Stop the current run and restart the timeline
- ///
- Restart,
-
- ///
- /// Ignore subsequent event fires until the timeline finishes
- ///
- Ignore,
-
- ///
- /// Play another copy of the timeline on top of the current run
- ///
- Copy
- }
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Timeline.cs b/src/Artemis.Core/Models/Profile/Timeline.cs
new file mode 100644
index 000000000..dec48c34c
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/Timeline.cs
@@ -0,0 +1,350 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Artemis.Storage.Entities.Profile;
+using Stylet;
+
+namespace Artemis.Core
+{
+ ///
+ /// Represents a timeline used by profile elements
+ ///
+ public class Timeline : PropertyChangedBase, IStorageModel
+ {
+ ///
+ /// Creates a new instance of the class
+ ///
+ public Timeline()
+ {
+ Entity = new TimelineEntity();
+ _extraTimelines = new List();
+ MainSegmentLength = TimeSpan.FromSeconds(5);
+
+ Save();
+ }
+
+ internal Timeline(TimelineEntity entity)
+ {
+ Entity = entity;
+ _extraTimelines = new List();
+
+ Load();
+ }
+
+ private Timeline(Timeline parent)
+ {
+ Parent = parent;
+ }
+
+ #region Extra timelines
+
+ ///
+ /// Adds an extra timeline to this timeline
+ ///
+ public void AddExtraTimeline()
+ {
+ _extraTimelines.Add(new Timeline(this));
+ }
+
+ ///
+ /// Removes all extra timelines from this timeline
+ ///
+ 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 _extraTimelines;
+
+ ///
+ /// Gets the parent this timeline is an extra timeline of
+ ///
+ public Timeline Parent { get; }
+
+ ///
+ /// Gets the current position of the timeline
+ ///
+ public TimeSpan Position
+ {
+ get => _position;
+ private set => SetAndNotify(ref _position, value);
+ }
+
+ ///
+ /// Gets the delta that was applied during the last call to
+ ///
+ public TimeSpan LastDelta
+ {
+ get => _lastDelta;
+ private set => SetAndNotify(ref _lastDelta, value);
+ }
+
+ ///
+ /// Gets or sets the mode in which the render element starts its timeline when display conditions are met
+ ///
+ public TimelinePlayMode PlayMode
+ {
+ get => _playMode;
+ set => SetAndNotify(ref _playMode, value);
+ }
+
+ ///
+ /// Gets or sets the mode in which the render element stops its timeline when display conditions are no longer met
+ ///
+ public TimelineStopMode StopMode
+ {
+ get => _stopMode;
+ set => SetAndNotify(ref _stopMode, value);
+ }
+
+ ///
+ /// Gets or sets the mode in which the render element responds to display condition events being fired before the
+ /// timeline finished
+ ///
+ public TimeLineEventOverlapMode EventOverlapMode
+ {
+ get => _eventOverlapMode;
+ set => SetAndNotify(ref _eventOverlapMode, value);
+ }
+
+ ///
+ /// Gets a list of extra copies of the timeline applied to this timeline
+ ///
+ public ReadOnlyCollection ExtraTimelines => _extraTimelines.AsReadOnly();
+
+ ///
+ /// Gets a boolean indicating whether the timeline has finished its run
+ ///
+ public bool IsFinished => Position > Length;
+
+ #region Segments
+
+ ///
+ /// Gets the total length of this timeline
+ ///
+ public TimeSpan Length => StartSegmentLength + MainSegmentLength + EndSegmentLength;
+
+ ///
+ /// Gets or sets the length of the start segment
+ ///
+ public TimeSpan StartSegmentLength { get; set; }
+
+ ///
+ /// Gets or sets the length of the main segment
+ ///
+ public TimeSpan MainSegmentLength { get; set; }
+
+ ///
+ /// Gets or sets the length of the end segment
+ ///
+ public TimeSpan EndSegmentLength { get; set; }
+
+ ///
+ /// Gets or sets the start position of the main segment
+ ///
+ public TimeSpan MainSegmentStartPosition
+ {
+ get => StartSegmentEndPosition;
+ set => StartSegmentEndPosition = value;
+ }
+
+ ///
+ /// Gets or sets the end position of the end segment
+ ///
+ public TimeSpan EndSegmentStartPosition
+ {
+ get => MainSegmentEndPosition;
+ set => MainSegmentEndPosition = value;
+ }
+
+ ///
+ /// Gets or sets the end position of the start segment
+ ///
+ public TimeSpan StartSegmentEndPosition
+ {
+ get => StartSegmentLength;
+ set => StartSegmentLength = value;
+ }
+
+ ///
+ /// Gets or sets the end position of the main segment
+ ///
+ public TimeSpan MainSegmentEndPosition
+ {
+ get => StartSegmentEndPosition + MainSegmentLength;
+ set => MainSegmentLength = value - StartSegmentEndPosition >= TimeSpan.Zero ? value - StartSegmentEndPosition : TimeSpan.Zero;
+ }
+
+ ///
+ /// Gets or sets the end position of the end segment
+ ///
+ 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
+
+ ///
+ /// Updates the timeline, applying the provided to the
+ ///
+ /// The amount of time to apply to the position
+ /// Whether to stick to the main segment, wrapping around if needed
+ public void Update(TimeSpan delta, bool stickToMainSegment)
+ {
+ LastDelta = delta;
+ Position += delta;
+
+ if (stickToMainSegment && Position >= MainSegmentStartPosition)
+ Position = MainSegmentStartPosition + TimeSpan.FromMilliseconds(Position.TotalMilliseconds % MainSegmentLength.TotalMilliseconds);
+ }
+
+ ///
+ /// Moves the position of the timeline backwards to the very start of the timeline
+ ///
+ public void JumpToStart()
+ {
+ if (Position == TimeSpan.Zero)
+ return;
+
+ LastDelta = TimeSpan.Zero - Position;
+ Position = TimeSpan.Zero;
+ }
+
+ ///
+ /// Moves the position of the timeline forwards to the beginning of the end segment
+ ///
+ public void JumpToEndSegment()
+ {
+ if (Position >= EndSegmentStartPosition)
+ return;
+
+ LastDelta = EndSegmentStartPosition - Position;
+ Position = EndSegmentStartPosition;
+ }
+
+ ///
+ /// Moves the position of the timeline forwards to the very end of the timeline
+ ///
+ public void JumpToEnd()
+ {
+ if (Position >= EndSegmentEndPosition)
+ return;
+
+ LastDelta = EndSegmentEndPosition - Position;
+ Position = EndSegmentEndPosition;
+ }
+
+ ///
+ /// Overrides the to the specified time and clears any extra time lines
+ ///
+ /// The position to set the timeline to
+ /// Whether to stick to the main segment, wrapping around if needed
+ 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
+
+ ///
+ public void Load()
+ {
+ StartSegmentLength = Entity.StartSegmentLength;
+ MainSegmentLength = Entity.MainSegmentLength;
+ EndSegmentLength = Entity.EndSegmentLength;
+ PlayMode = (TimelinePlayMode) Entity.PlayMode;
+ StopMode = (TimelineStopMode) Entity.StopMode;
+ EventOverlapMode = (TimeLineEventOverlapMode) Entity.EventOverlapMode;
+ }
+
+ ///
+ 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
+ }
+
+ ///
+ /// Represents a mode for render elements to start their timeline when display conditions are met
+ ///
+ public enum TimelinePlayMode
+ {
+ ///
+ /// Continue repeating the main segment of the timeline while the condition is met
+ ///
+ Repeat,
+
+ ///
+ /// Only play the timeline once when the condition is met
+ ///
+ Once
+ }
+
+ ///
+ /// Represents a mode for render elements to stop their timeline when display conditions are no longer met
+ ///
+ public enum TimelineStopMode
+ {
+ ///
+ /// When conditions are no longer met, finish the the current run of the main timeline
+ ///
+ Finish,
+
+ ///
+ /// When conditions are no longer met, skip to the end segment of the timeline
+ ///
+ SkipToEnd
+ }
+
+ ///
+ /// Represents a mode for render elements to start their timeline when display conditions events are fired
+ ///
+ public enum TimeLineEventOverlapMode
+ {
+ ///
+ /// Stop the current run and restart the timeline
+ ///
+ Restart,
+
+ ///
+ /// Ignore subsequent event fires until the timeline finishes
+ ///
+ Ignore,
+
+ ///
+ /// Play another copy of the timeline on top of the current run
+ ///
+ Copy
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
index e538642e3..d8f7ded16 100644
--- a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
@@ -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 LayerEffects { get; set; }
public List PropertyEntities { get; set; }
public List ExpandedPropertyGroups { get; set; }
public DataModelConditionGroupEntity DisplayCondition { get; set; }
+ public TimelineEntity Timeline { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/TimelineEntity.cs b/src/Artemis.Storage/Entities/Profile/TimelineEntity.cs
new file mode 100644
index 000000000..6d17188c6
--- /dev/null
+++ b/src/Artemis.Storage/Entities/Profile/TimelineEntity.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.sln.DotSettings b/src/Artemis.sln.DotSettings
index 0549ae7d5..d11049d68 100644
--- a/src/Artemis.sln.DotSettings
+++ b/src/Artemis.sln.DotSettings
@@ -221,4 +221,5 @@
True
True
True
- True
\ No newline at end of file
+ True
+ True
\ No newline at end of file