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();
}
}