示例#1
0
        /// <summary>
        /// Calculates the value this animation believes should be the current value for the property.
        /// </summary>
        /// <param name="defaultOriginValue">
        /// This value is the suggested origin value provided to the animation
        /// to be used if the animation does not have its own concept of a
        /// start value. If this animation is the first in a composition chain
        /// this value will be the snapshot value if one is available or the
        /// base property value if it is not; otherise this value will be the
        /// value returned by the previous animation in the chain with an
        /// animationClock that is not Stopped.
        /// </param>
        /// <param name="defaultDestinationValue">
        /// This value is the suggested destination value provided to the animation
        /// to be used if the animation does not have its own concept of an
        /// end value. This value will be the base value if the animation is
        /// in the first composition layer of animations on a property;
        /// otherwise this value will be the output value from the previous
        /// composition layer of animations for the property.
        /// </param>
        /// <param name="animationClock">
        /// This is the animationClock which can generate the CurrentTime or
        /// CurrentProgress value to be used by the animation to generate its
        /// output value.
        /// </param>
        /// <returns>
        /// The value this animation believes should be the current value for the property.
        /// </returns>
        protected sealed override Byte GetCurrentValueCore(
            Byte defaultOriginValue,
            Byte defaultDestinationValue,
            AnimationClock animationClock)
        {
            Debug.Assert(animationClock.CurrentState != ClockState.Stopped);

            if (_keyFrames == null)
            {
                return(defaultDestinationValue);
            }

            // We resolved our KeyTimes when we froze, but also got notified
            // of the frozen state and therefore invalidated ourselves.
            if (!_areKeyTimesValid)
            {
                ResolveKeyTimes();
            }

            if (_sortedResolvedKeyFrames == null)
            {
                return(defaultDestinationValue);
            }

            TimeSpan currentTime      = animationClock.CurrentTime.Value;
            Int32    keyFrameCount    = _sortedResolvedKeyFrames.Length;
            Int32    maxKeyFrameIndex = keyFrameCount - 1;

            Byte currentIterationValue;

            Debug.Assert(maxKeyFrameIndex >= 0, "maxKeyFrameIndex is less than zero which means we don't actually have any key frames.");

            Int32 currentResolvedKeyFrameIndex = 0;

            // Skip all the key frames with key times lower than the current time.
            // currentResolvedKeyFrameIndex will be greater than maxKeyFrameIndex
            // if we are past the last key frame.
            while (currentResolvedKeyFrameIndex < keyFrameCount &&
                   currentTime > _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime)
            {
                currentResolvedKeyFrameIndex++;
            }

            // If there are multiple key frames at the same key time, be sure to go to the last one.
            while (currentResolvedKeyFrameIndex < maxKeyFrameIndex &&
                   currentTime == _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex + 1]._resolvedKeyTime)
            {
                currentResolvedKeyFrameIndex++;
            }

            if (currentResolvedKeyFrameIndex == keyFrameCount)
            {
                // Past the last key frame.
                currentIterationValue = GetResolvedKeyFrameValue(maxKeyFrameIndex);
            }
            else if (currentTime == _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime)
            {
                // Exactly on a key frame.
                currentIterationValue = GetResolvedKeyFrameValue(currentResolvedKeyFrameIndex);
            }
            else
            {
                // Between two key frames.
                Double currentSegmentProgress = 0.0;
                Byte   fromValue;

                if (currentResolvedKeyFrameIndex == 0)
                {
                    // The current key frame is the first key frame so we have
                    // some special rules for determining the fromValue and an
                    // optimized method of calculating the currentSegmentProgress.

                    // If we're additive we want the base value to be a zero value
                    // so that if there isn't a key frame at time 0.0, we'll use
                    // the zero value for the time 0.0 value and then add that
                    // later to the base value.
                    if (IsAdditive)
                    {
                        fromValue = AnimatedTypeHelpers.GetZeroValueByte(defaultOriginValue);
                    }
                    else
                    {
                        fromValue = defaultOriginValue;
                    }

                    // Current segment time divided by the segment duration.
                    // Note: the reason this works is that we know that we're in
                    // the first segment, so we can assume:
                    //
                    // currentTime.TotalMilliseconds                                  = current segment time
                    // _sortedResolvedKeyFrames[0]._resolvedKeyTime.TotalMilliseconds = current segment duration

                    currentSegmentProgress = currentTime.TotalMilliseconds
                                             / _sortedResolvedKeyFrames[0]._resolvedKeyTime.TotalMilliseconds;
                }
                else
                {
                    Int32    previousResolvedKeyFrameIndex = currentResolvedKeyFrameIndex - 1;
                    TimeSpan previousResolvedKeyTime       = _sortedResolvedKeyFrames[previousResolvedKeyFrameIndex]._resolvedKeyTime;

                    fromValue = GetResolvedKeyFrameValue(previousResolvedKeyFrameIndex);

                    TimeSpan segmentCurrentTime = currentTime - previousResolvedKeyTime;
                    TimeSpan segmentDuration    = _sortedResolvedKeyFrames[currentResolvedKeyFrameIndex]._resolvedKeyTime - previousResolvedKeyTime;

                    currentSegmentProgress = segmentCurrentTime.TotalMilliseconds
                                             / segmentDuration.TotalMilliseconds;
                }

                currentIterationValue = GetResolvedKeyFrame(currentResolvedKeyFrameIndex).InterpolateValue(fromValue, currentSegmentProgress);
            }



            // If we're cumulative, we need to multiply the final key frame
            // value by the current repeat count and add this to the return
            // value.
            if (IsCumulative)
            {
                Double currentRepeat = (Double)(animationClock.CurrentIteration - 1);

                if (currentRepeat > 0.0)
                {
                    currentIterationValue = AnimatedTypeHelpers.AddByte(
                        currentIterationValue,
                        AnimatedTypeHelpers.ScaleByte(GetResolvedKeyFrameValue(maxKeyFrameIndex), currentRepeat));
                }
            }

            // If we're additive we need to add the base value to the return value.
            if (IsAdditive)
            {
                return(AnimatedTypeHelpers.AddByte(defaultOriginValue, currentIterationValue));
            }


            return(currentIterationValue);
        }
示例#2
0
        /// <summary>
        /// Calculates the value this animation believes should be the current value for the property.
        /// </summary>
        /// <param name="defaultOriginValue">
        /// This value is the suggested origin value provided to the animation
        /// to be used if the animation does not have its own concept of a
        /// start value. If this animation is the first in a composition chain
        /// this value will be the snapshot value if one is available or the
        /// base property value if it is not; otherise this value will be the
        /// value returned by the previous animation in the chain with an
        /// animationClock that is not Stopped.
        /// </param>
        /// <param name="defaultDestinationValue">
        /// This value is the suggested destination value provided to the animation
        /// to be used if the animation does not have its own concept of an
        /// end value. This value will be the base value if the animation is
        /// in the first composition layer of animations on a property;
        /// otherwise this value will be the output value from the previous
        /// composition layer of animations for the property.
        /// </param>
        /// <param name="animationClock">
        /// This is the animationClock which can generate the CurrentTime or
        /// CurrentProgress value to be used by the animation to generate its
        /// output value.
        /// </param>
        /// <returns>
        /// The value this animation believes should be the current value for the property.
        /// </returns>
        protected override Byte GetCurrentValueCore(Byte defaultOriginValue, Byte defaultDestinationValue, AnimationClock animationClock)
        {
            Debug.Assert(animationClock.CurrentState != ClockState.Stopped);

            if (!_isAnimationFunctionValid)
            {
                ValidateAnimationFunction();
            }

            double progress = animationClock.CurrentProgress.Value;

            IEasingFunction easingFunction = EasingFunction;

            if (easingFunction != null)
            {
                progress = easingFunction.Ease(progress);
            }

            Byte from        = new Byte();
            Byte to          = new Byte();
            Byte accumulated = new Byte();
            Byte foundation  = new Byte();

            // need to validate the default origin and destination values if
            // the animation uses them as the from, to, or foundation values
            bool validateOrigin      = false;
            bool validateDestination = false;

            switch (_animationType)
            {
            case AnimationType.Automatic:

                from = defaultOriginValue;
                to   = defaultDestinationValue;

                validateOrigin      = true;
                validateDestination = true;

                break;

            case AnimationType.From:

                from = _keyValues[0];
                to   = defaultDestinationValue;

                validateDestination = true;

                break;

            case AnimationType.To:

                from = defaultOriginValue;
                to   = _keyValues[0];

                validateOrigin = true;

                break;

            case AnimationType.By:

                // According to the SMIL specification, a By animation is
                // always additive.  But we don't force this so that a
                // user can re-use a By animation and have it replace the
                // animations that precede it in the list without having
                // to manually set the From value to the base value.

                to         = _keyValues[0];
                foundation = defaultOriginValue;

                validateOrigin = true;

                break;

            case AnimationType.FromTo:

                from = _keyValues[0];
                to   = _keyValues[1];

                if (IsAdditive)
                {
                    foundation     = defaultOriginValue;
                    validateOrigin = true;
                }

                break;

            case AnimationType.FromBy:

                from = _keyValues[0];
                to   = AnimatedTypeHelpers.AddByte(_keyValues[0], _keyValues[1]);

                if (IsAdditive)
                {
                    foundation     = defaultOriginValue;
                    validateOrigin = true;
                }

                break;

            default:

                Debug.Fail("Unknown animation type.");

                break;
            }

            if (validateOrigin &&
                !AnimatedTypeHelpers.IsValidAnimationValueByte(defaultOriginValue))
            {
                throw new InvalidOperationException(
                          SR.Get(
                              SRID.Animation_Invalid_DefaultValue,
                              this.GetType(),
                              "origin",
                              defaultOriginValue.ToString(CultureInfo.InvariantCulture)));
            }

            if (validateDestination &&
                !AnimatedTypeHelpers.IsValidAnimationValueByte(defaultDestinationValue))
            {
                throw new InvalidOperationException(
                          SR.Get(
                              SRID.Animation_Invalid_DefaultValue,
                              this.GetType(),
                              "destination",
                              defaultDestinationValue.ToString(CultureInfo.InvariantCulture)));
            }


            if (IsCumulative)
            {
                double currentRepeat = (double)(animationClock.CurrentIteration - 1);

                if (currentRepeat > 0.0)
                {
                    Byte accumulator = AnimatedTypeHelpers.SubtractByte(to, from);

                    accumulated = AnimatedTypeHelpers.ScaleByte(accumulator, currentRepeat);
                }
            }

            // return foundation + accumulated + from + ((to - from) * progress)

            return(AnimatedTypeHelpers.AddByte(
                       foundation,
                       AnimatedTypeHelpers.AddByte(
                           accumulated,
                           AnimatedTypeHelpers.InterpolateByte(from, to, progress))));
        }