using System; using System.Collections.Generic; using System.Linq; using Artemis.Core.Exceptions; using Artemis.Core.Models.Profile.LayerProperties; using Artemis.Core.Utilities; namespace Artemis.Core.Models.Profile.KeyframeEngines { public abstract class KeyframeEngine { /// /// Indicates whether has been called. /// public bool Initialized { get; private set; } /// /// The layer property this keyframe engine applies to. /// public BaseLayerProperty LayerProperty { get; private set; } /// /// The total progress /// public TimeSpan Progress { get; private set; } /// /// The progress from the current keyframe to the next. /// Range 0.0 to 1.0. /// public float KeyframeProgress { get; private set; } /// /// The progress from the current keyframe to the next with the current keyframes easing function applied. /// Range 0.0 to 1.0 but can be higher than 1.0 depending on easing function. /// public float KeyframeProgressEased { get; set; } /// /// The current keyframe /// public BaseKeyframe CurrentKeyframe { get; private set; } /// /// The next keyframe /// public BaseKeyframe NextKeyframe { get; private set; } /// /// The types this keyframe engine supports. /// public abstract List CompatibleTypes { get; } /// /// Associates the keyframe engine with the provided layer property. /// /// public void Initialize(BaseLayerProperty layerProperty) { if (Initialized) throw new ArtemisCoreException("Cannot initialize the same keyframe engine twice"); if (!CompatibleTypes.Contains(layerProperty.Type)) throw new ArtemisCoreException($"This property engine does not support the provided type {layerProperty.Type.Name}"); LayerProperty = layerProperty; LayerProperty.KeyframeEngine = this; Initialized = true; } /// /// Updates the engine's progress /// /// public void Update(double deltaTime) { if (!Initialized) return; var keyframes = LayerProperty.UntypedKeyframes.ToList(); Progress = Progress.Add(TimeSpan.FromSeconds(deltaTime)); // The current keyframe is the last keyframe before the current time CurrentKeyframe = keyframes.LastOrDefault(k => k.Position <= Progress); // The next keyframe is the first keyframe that's after the current time NextKeyframe = keyframes.FirstOrDefault(k => k.Position > Progress); if (CurrentKeyframe == null) { KeyframeProgress = 0; KeyframeProgressEased = 0; } else if (NextKeyframe == null) { KeyframeProgress = 1; KeyframeProgressEased = 1; } else { var timeDiff = NextKeyframe.Position - CurrentKeyframe.Position; KeyframeProgress = (float) ((Progress - CurrentKeyframe.Position).TotalMilliseconds / timeDiff.TotalMilliseconds); KeyframeProgressEased = (float) Easings.Interpolate(KeyframeProgress, CurrentKeyframe.EasingFunction); } // LayerProperty determines what's next: reset, stop, continue } /// /// Overrides the engine's progress to the provided value /// /// public void OverrideProgress(TimeSpan progress) { Progress = TimeSpan.Zero; Update(progress.TotalSeconds); } /// /// Gets the current value, if the progress is in between two keyframes the value will be interpolated /// /// public object GetCurrentValue() { if (CurrentKeyframe == null && LayerProperty.UntypedKeyframes.Any()) return LayerProperty.UntypedKeyframes.First().BaseValue; if (CurrentKeyframe == null) return LayerProperty.BaseValue; if (NextKeyframe == null) return CurrentKeyframe.BaseValue; return GetInterpolatedValue(); } protected abstract object GetInterpolatedValue(); } }