/// <summary> /// Gets the animation composition chain of the given animatable property and type. /// </summary> /// <typeparam name="T">The type of the property.</typeparam> /// <param name="property"> /// The animatable property. Can be <see langword="null"/>. /// </param> /// <param name="traits"> /// The animation value traits. /// </param> /// <param name="createIfNotFound"> /// If set to <see langword="true"/> a new animation composition chain will be created /// automatically when necessary. (<paramref name="property"/> must not be /// <see langword="null"/>.) /// </param> /// <returns> /// The <see cref="IAnimationCompositionChain"/> of the animated property. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="createIfNotFound"/> is set, but <paramref name="property"/> or /// <paramref name="traits"/> is <see langword="null"/>. /// </exception> internal AnimationCompositionChain<T> GetCompositionChain<T>(IAnimatableProperty<T> property, IAnimationValueTraits<T> traits, bool createIfNotFound) { if (property == null) { if (createIfNotFound) throw new ArgumentNullException("property", "The property must not be null if a new animation composition chain should be created."); return null; } int index; IAnimationCompositionChain untypedCompositionChain; _compositionChains.Get(property, out index, out untypedCompositionChain); AnimationCompositionChain<T> compositionChain = untypedCompositionChain as AnimationCompositionChain<T>; if (compositionChain == null && createIfNotFound) { if (traits == null) throw new ArgumentNullException("traits", "The animation value traits need to be set when a new animation composition chain should be created."); // Create new animation composition chain. compositionChain = AnimationCompositionChain<T>.Create(property, traits); _compositionChains.Insert(index, compositionChain); } return compositionChain; }
/// <summary> /// 在指定的属性上开始创建基于表达式的动画。 /// </summary> /// <param name="targetProperty"></param> /// <returns></returns> public ExpressionAnimationFluentContext AnimateWithExpression(IAnimatableProperty targetProperty) { var result = new ExpressionAnimationFluentContext(this, Target.Compositor.CreateExpressionAnimation(), targetProperty.PropertyPath); _animations.Add(result); return(result); }
/// <summary> /// Assigns this animation tree to the specified property. /// </summary> /// <param name="property">The animatable property.</param> internal virtual void AssignTo(IAnimatableProperty property) { foreach (var child in Children) { child.AssignTo(property); } }
/// <inheritdoc/> public void Apply() { IAnimatableProperty <T> property = Property; if (property == null) { // The animated object has been garbage collected. return; } if (property is ImmediateAnimatableProperty <T> ) { // Special: ImmediateAnimatableProperty<T> are set in Update(). // All other properties are set in Apply(); return; } if (Items.Count > 0) { // Apply output of chain to property. SetProperty(property); } else { // All animations have stopped or were removed. ResetProperty(property); } }
/// <inheritdoc/> /// <exception cref="ArgumentNullException"> /// <paramref name="value"/> or <paramref name="property"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// The value of <paramref name="property"/> must not be <see langword="null"/>. /// </exception> public void Set(ref SkeletonPose value, IAnimatableProperty <SkeletonPose> property) { if (value == null) { throw new ArgumentNullException("value"); } if (property == null) { throw new ArgumentNullException("property"); } var animationValue = property.AnimationValue; if (animationValue == null) { if (property.HasBaseValue) { // The SkeletonPose will be recycled in Reset(). Create(ref value, out animationValue); property.AnimationValue = animationValue; } else { throw new ArgumentException("The value of the property must not be null. This exception usually occurs when an IAnimatableProperty<SkeletonPose> is being animated, but the value of the property is null. A SkeletonPose must be set before the property can be animated."); } } SkeletonHelper.Copy(value, animationValue); }
/// <summary> /// Creates a new instance of the <see cref="AnimationCompositionChain{T}"/> class. /// </summary> /// <param name="property">The property that should be animated.</param> /// <param name="traits">The animation value traits.</param> /// <exception cref="ArgumentNullException"> /// Either <paramref name="property"/> or <paramref name="traits"/> is <see langword="null"/>. /// </exception> public static AnimationCompositionChain <T> Create(IAnimatableProperty <T> property, IAnimationValueTraits <T> traits) { var compositionChain = Pool.Obtain(); compositionChain.Initialize(property, traits); return(compositionChain); }
/// <inheritdoc/> internal override bool IsAssignableTo(IAnimatableProperty property) { // Note: In this case we do not check whether Animation.TargetProperty matches property.Name. // This method should only be called to explicitly assign the animation to the given property. return(property is IAnimatableProperty <T>); }
/// <summary> /// Writes the property value. /// </summary> /// <param name="property">The property.</param> private void SetProperty(IAnimatableProperty <T> property) { // Important: AnimationValue should be set BEFORE IsAnimated. // This is relevant because a property (such as a DigitalRune.Game.GameProperty<T>) // could raise Changed-events. If we call the setters in the wrong order the // property might raise too many Changed-events. Traits.Set(ref _value, property); property.IsAnimated = true; }
/// <inheritdoc/> public bool IsAnimated(IAnimatableProperty animatableProperty) { if (animatableProperty == null) throw new ArgumentNullException("animatableProperty"); int index; IAnimationCompositionChain compositionChain; _compositionChains.Get(animatableProperty, out index, out compositionChain); return compositionChain != null; }
/// <inheritdoc/> public AnimationController CreateController(ITimeline animation, IAnimatableProperty targetProperty) { if (targetProperty == null) throw new ArgumentNullException("targetProperty"); if (animation == null) throw new ArgumentNullException("animation"); var animationInstance = animation.CreateInstance(); animationInstance.AssignTo(targetProperty); return new AnimationController(this, animationInstance); }
/// <summary> /// Resets the property. /// </summary> /// <param name="property">The property.</param> private void ResetProperty(IAnimatableProperty <T> property) { // Important: AnimationValue should be set AFTER IsAnimated. // Again this is relevant to avoid too many Changed-events. property.IsAnimated = false; if (property.HasBaseValue) { // If the property has a base value we should reset the animation value // to avoid memory leaks. Traits.Reset(property); } }
/// <inheritdoc/> internal override void AssignTo(IAnimatableProperty property) { // Note: In this case we do not check whether Animation.TargetProperty matches property.Name. // This method should only be called to explicitly assign the animation to the given property. var typedProperty = property as IAnimatableProperty <T>; if (typedProperty != null) { Property = typedProperty; } }
/// <summary> /// Determines whether this animation tree is assigned to the specified property. /// </summary> /// <param name="property">The animatable property.</param> /// <returns> /// <see langword="true"/> if this animation instance (or one of its children) is assigned to /// <paramref name="property"/>; otherwise, <see langword="false"/>. /// </returns> internal virtual bool IsAssignedTo(IAnimatableProperty property) { foreach (var child in Children) { if (child.IsAssignedTo(property)) { return(true); } } return(false); }
///// <summary> ///// Unassigns this animation trees. (Removes all references to the currently assigned ///// properties.) ///// </summary> //internal virtual void Unassign() //{ // foreach (var child in Children) // child.Unassign(); //} /// <summary> /// Checks whether this animation tree is assigned to a certain animated property and returns /// the animation instance. /// </summary> /// <param name="property">The animatable property.</param> /// <returns> /// The animation instance that is assigned to <paramref name="property"/>. /// <see langword="null"/> if neither this instance nor its children are assigned to the /// property. If multiple children are assigned to the property the last found child is /// returned. /// </returns> internal virtual AnimationInstance GetAssignedInstance(IAnimatableProperty property) { for (int i = Children.Count - 1; i >= 0; i--) { var child = Children[i]; var assignedInstance = child.GetAssignedInstance(property); if (assignedInstance != null) { return(assignedInstance); } } return(null); }
/// <inheritdoc/> public void UpdateAndApplyAnimation(IAnimatableProperty property) { int index; IAnimationCompositionChain compositionChain; _compositionChains.Get(property, out index, out compositionChain); if (compositionChain != null) { compositionChain.Update(this); compositionChain.Apply(); // Remove empty animation composition chains. if (compositionChain.IsEmpty) _compositionChains.RemoveAt(index); } }
/// <inheritdoc/> public AnimationController StartAnimation(ITimeline animation, IAnimatableProperty targetProperty, AnimationTransition transition) { if (targetProperty == null) { throw new ArgumentNullException("targetProperty"); } if (animation == null) { throw new ArgumentNullException("animation"); } var animationInstance = animation.CreateInstance(); animationInstance.AssignTo(targetProperty); StartAnimation(animationInstance, transition); return(new AnimationController(this, animationInstance)); }
/// <summary> /// Takes a snapshot of the current animation value. /// </summary> /// <remarks> /// The first animation instance in the composition chain will receive snapshot as its source /// value instead of the properties' base value. /// </remarks> /// <seealso cref="ClearSnapshot"/> public void TakeSnapshot() { IAnimatableProperty <T> property = Property; if (property != null) { if (Items.Count > 0) { // Take snapshot of last animation value. if (!_hasSnapshot) { // Allocate object, if necessary. _traits.Create(ref _value, out _snapshot); _hasSnapshot = true; } _traits.Copy(ref _value, ref _snapshot); } else if (property.HasBaseValue) { // No animations: Take snapshot of base value. if (!_hasSnapshot) { // Allocate object, if necessary. _traits.Create(ref _value, out _snapshot); _hasSnapshot = true; } var baseValue = property.BaseValue; _traits.Copy(ref baseValue, ref _snapshot); } else { // The property does not have animations nor a base value. // Clear the snapshot. The animations will use the default // value (AnimationValueTraits.Identity). ClearSnapshot(); } } else { // The animatable property is recycled while the animation is started. // Clear snapshot just to be safe. (Should never occur in practice. ClearSnapshot(); } }
/// <inheritdoc/> public void Reset(IAnimatableProperty <SkeletonPose> property) { // ReSharper disable once RedundantIfElseBlock if (property.HasBaseValue) { // Recycle SkeletonPose which has been allocated in Set(). var animationValue = property.AnimationValue; property.AnimationValue = null; Recycle(ref animationValue); } else { // IAnimatableProperty<SkeletonPose> does not have a base value. The animation value // does not have to be set to null. // --> Do nothing. } }
/// <summary> /// Initializes the <see cref="AnimationCompositionChain{T}"/>. /// </summary> /// <param name="property">The property that should be animated.</param> /// <param name="traits">The animation value traits.</param> /// <exception cref="ArgumentNullException"> /// Either <paramref name="property"/> or <paramref name="traits"/> is <see langword="null"/>. /// </exception> public void Initialize(IAnimatableProperty<T> property, IAnimationValueTraits<T> traits) { Debug.Assert(Count == 0, "Animation composition chain has not been reset properly."); Debug.Assert(_weakReference.Target == null, "Animation composition chain has not been reset properly."); Debug.Assert(_hasSnapshot == false, "Animation composition chain has not been reset properly."); Debug.Assert(_traits == null, "Animation composition chain has not been reset properly."); if (property == null) throw new ArgumentNullException("property"); if (traits == null) throw new ArgumentNullException("traits"); _weakReference.Target = property; _traits = traits; var reference = (property.HasBaseValue) ? property.BaseValue : property.AnimationValue; traits.Create(ref reference, out _value); }
protected EasyTransitionAnimationFluentContext <T> Animate <T>(IAnimatableProperty <T> targetProperty) { AnimationFluentContext result; switch (targetProperty.AnimationType) { case AnimationTypes.Single: result = new ScalarEasyTransitionAnimationFluentContext(this, Target.Compositor.CreateScalarKeyFrameAnimation(), targetProperty.PropertyPath); _animations.Add(result); return((EasyTransitionAnimationFluentContext <T>)result); case AnimationTypes.Vector2: result = new Vector2EasyTransitionAnimationFluentContext(this, Target.Compositor.CreateVector2KeyFrameAnimation(), targetProperty.PropertyPath); _animations.Add(result); return((EasyTransitionAnimationFluentContext <T>)result); case AnimationTypes.Vector3: result = new Vector3EasyTransitionAnimationFluentContext(this, Target.Compositor.CreateVector3KeyFrameAnimation(), targetProperty.PropertyPath); _animations.Add(result); return((EasyTransitionAnimationFluentContext <T>)result); case AnimationTypes.Vector4: result = new Vector4EasyTransitionAnimationFluentContext(this, Target.Compositor.CreateVector4KeyFrameAnimation(), targetProperty.PropertyPath); _animations.Add(result); return((EasyTransitionAnimationFluentContext <T>)result); case AnimationTypes.Color: result = new ColorEasyTransitionAnimationFluentContext(this, Target.Compositor.CreateColorKeyFrameAnimation(), targetProperty.PropertyPath); _animations.Add(result); return((EasyTransitionAnimationFluentContext <T>)result); case AnimationTypes.Quaternion: result = new QuaternionEasyTransitionAnimationFluentContext(this, Target.Compositor.CreateQuaternionKeyFrameAnimation(), targetProperty.PropertyPath); _animations.Add(result); return((EasyTransitionAnimationFluentContext <T>)result); case AnimationTypes.NotSupport: default: throw new InvalidOperationException(""); } }
/// <inheritdoc/> public void StopAnimation(IAnimatableProperty animatedProperty) { int index; IAnimationCompositionChain compositionChain; _compositionChains.Get(animatedProperty, out index, out compositionChain); if (compositionChain != null) { for (int i = compositionChain.Count - 1; i >= 0; i--) { if (i >= compositionChain.Count) { // Multiple instances were removed in the previous iteration. // --> Skip index. continue; } var animationInstance = compositionChain[i].GetRoot(); Remove(animationInstance); } } }
///// <inheritdoc/> //internal override void Unassign() //{ // Property = null; //} /// <inheritdoc/> internal override AnimationInstance GetAssignedInstance(IAnimatableProperty property) { return(IsAssignedTo(property) ? this : null); }
/// <inheritdoc/> internal override bool IsAssignedTo(IAnimatableProperty property) { var assignedProperty = Property; return(assignedProperty != null && assignedProperty == property); }
/// <inheritdoc/> public void Reset(IAnimatableProperty <Color> property) { }
/// <inheritdoc/> public void Set(ref Vector2 value, IAnimatableProperty <Vector2> property) { property.AnimationValue = value; }
/// <inheritdoc/> public void Set(ref Color value, IAnimatableProperty <Color> property) { property.AnimationValue = value; }
// Reset the animation value of the given property. public void Reset(IAnimatableProperty <string> property) { }
// Set an animatable property to the given value. public void Set(ref string value, IAnimatableProperty <string> property) { property.AnimationValue = value; }
/// <summary> /// Gets the composition chain for the specified <paramref name="property"/>. /// </summary> /// <param name="property">The animatable property.</param> /// <param name="index"> /// The index of the composition chain in this collection. If no suitable composition chain is /// found, this is the index where a new composition chain should be inserted. /// </param> /// <param name="chain"> /// The found composition chain. <see langword="null"/> if no suitable chain was found. /// </param> public void Get(IAnimatableProperty property, out int index, out IAnimationCompositionChain chain) { // Abort if property is null. if (property == null) { index = -1; chain = null; return; } // Composition chains for immediate animatable properties are stored at the front - unsorted. if (property is IImmediateAnimatableProperty) { // Search immediates in sequential order. for (int i = 0; i < _numberOfImmediates; i++) { var candidate = _compositionChains[i]; if (candidate.Property == property) { index = i; chain = candidate; return; } } index = 0; chain = null; return; } // The other chains are stored after the immediates, sorted by the hash codes of the // properties. uint hashCode = (uint)property.GetHashCode(); // Search for non-immediates using binary search. int start = _numberOfImmediates; var numberOfCompositionChains = _compositionChains.Count; int end = numberOfCompositionChains - 1; while (start <= end) { index = start + (end - start >> 1); long comparison = (long)_hashCodes[index] - (long)hashCode; if (comparison == 0) { chain = _compositionChains[index]; if (chain.Property == property) { return; } // Found the correct hash value. Must search vicinity because there could be more // properties with this hash code. // Search forward. int savedIndex = index; index++; while (index < numberOfCompositionChains && _hashCodes[index] == hashCode) { chain = _compositionChains[index]; if (chain.Property == property) { return; } index++; } // Search backward. index = savedIndex - 1; while (index >= _numberOfImmediates && _hashCodes[index] == hashCode) { chain = _compositionChains[index]; if (chain.Property == property) { return; } index--; } index = savedIndex; chain = null; return; } if (comparison < 0) { Debug.Assert(hashCode > _hashCodes[index]); start = index + 1; } else { Debug.Assert(hashCode < _hashCodes[index]); end = index - 1; } } index = start; chain = null; return; }
/// <inheritdoc/> public void Reset(IAnimatableProperty <Vector2> property) { }