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