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