private void ResolveKeyTimes() { Debug.Assert(!_areKeyTimesValid, "KeyFrameCharAnimaton.ResolveKeyTimes() shouldn't be called if the key times are already valid."); int keyFrameCount = 0; if (_keyFrames != null) { keyFrameCount = _keyFrames.Count; } if (keyFrameCount == 0) { _sortedResolvedKeyFrames = null; _areKeyTimesValid = true; return; } _sortedResolvedKeyFrames = new ResolvedKeyFrameEntry[keyFrameCount]; int index = 0; // Initialize the _originalKeyFrameIndex. for ( ; index < keyFrameCount; index++) { _sortedResolvedKeyFrames[index]._originalKeyFrameIndex = index; } // calculationDuration represents the time span we will use to resolve // percent key times. This is defined as the value in the following // precedence order: // 1. The animation's duration, but only if it is a time span, not auto or forever. // 2. The largest time span specified key time of all the key frames. // 3. 1 second, to match the From/To/By animations. TimeSpan calculationDuration = TimeSpan.Zero; Duration duration = Duration; if (duration.HasTimeSpan) { calculationDuration = duration.TimeSpan; } else { calculationDuration = LargestTimeSpanKeyTime; } int maxKeyFrameIndex = keyFrameCount - 1; ArrayList unspecifiedBlocks = new ArrayList(); bool hasPacedKeyTimes = false; // // Pass 1: Resolve Percent and Time key times. // index = 0; while (index < keyFrameCount) { KeyTime keyTime = _keyFrames[index].KeyTime; switch (keyTime.Type) { case KeyTimeType.Percent: _sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.FromMilliseconds( keyTime.Percent * calculationDuration.TotalMilliseconds); index++; break; case KeyTimeType.TimeSpan: _sortedResolvedKeyFrames[index]._resolvedKeyTime = keyTime.TimeSpan; index++; break; case KeyTimeType.Paced: case KeyTimeType.Uniform: if (index == maxKeyFrameIndex) { // If the last key frame doesn't have a specific time // associated with it its resolved key time will be // set to the calculationDuration, which is the // defined in the comments above where it is set. // Reason: We only want extra time at the end of the // key frames if the user specifically states that // the last key frame ends before the animation ends. _sortedResolvedKeyFrames[index]._resolvedKeyTime = calculationDuration; index++; } else if ( index == 0 && keyTime.Type == KeyTimeType.Paced) { // Note: It's important that this block come after // the previous if block because of rule precendence. // If the first key frame in a multi-frame key frame // collection is paced, we set its resolved key time // to 0.0 for performance reasons. If we didn't, the // resolved key time list would be dependent on the // base value which can change every animation frame // in many cases. _sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.Zero; index++; } else { if (keyTime.Type == KeyTimeType.Paced) { hasPacedKeyTimes = true; } KeyTimeBlock block = new KeyTimeBlock(); block.BeginIndex = index; // NOTE: We don't want to go all the way up to the // last frame because if it is Uniform or Paced its // resolved key time will be set to the calculation // duration using the logic above. // // This is why the logic is: // ((++index) < maxKeyFrameIndex) // instead of: // ((++index) < keyFrameCount) while ((++index) < maxKeyFrameIndex) { KeyTimeType type = _keyFrames[index].KeyTime.Type; if ( type == KeyTimeType.Percent || type == KeyTimeType.TimeSpan) { break; } else if (type == KeyTimeType.Paced) { hasPacedKeyTimes = true; } } Debug.Assert(index < keyFrameCount, "The end index for a block of unspecified key frames is out of bounds."); block.EndIndex = index; unspecifiedBlocks.Add(block); } break; } } // // Pass 2: Resolve Uniform key times. // for (int j = 0; j < unspecifiedBlocks.Count; j++) { KeyTimeBlock block = (KeyTimeBlock)unspecifiedBlocks[j]; TimeSpan blockBeginTime = TimeSpan.Zero; if (block.BeginIndex > 0) { blockBeginTime = _sortedResolvedKeyFrames[block.BeginIndex - 1]._resolvedKeyTime; } // The number of segments is equal to the number of key // frames we're working on plus 1. Think about the case // where we're working on a single key frame. There's a // segment before it and a segment after it. // // Time known Uniform Time known // ^ ^ ^ // | | | // | (segment 1) | (segment 2) | Int64 segmentCount = (block.EndIndex - block.BeginIndex) + 1; TimeSpan uniformTimeStep = TimeSpan.FromTicks((_sortedResolvedKeyFrames[block.EndIndex]._resolvedKeyTime - blockBeginTime).Ticks / segmentCount); index = block.BeginIndex; TimeSpan resolvedTime = blockBeginTime + uniformTimeStep; while (index < block.EndIndex) { _sortedResolvedKeyFrames[index]._resolvedKeyTime = resolvedTime; resolvedTime += uniformTimeStep; index++; } } // // Pass 3: Resolve Paced key times. // if (hasPacedKeyTimes) { ResolvePacedKeyTimes(); } // // Sort resolved key frame entries. // Array.Sort(_sortedResolvedKeyFrames); _areKeyTimesValid = true; return; }
private void ResolveKeyTimes() { Debug.Assert(!_areKeyTimesValid, "KeyFrameByteAnimaton.ResolveKeyTimes() shouldn't be called if the key times are already valid."); int keyFrameCount = 0; if (_keyFrames != null) { keyFrameCount = _keyFrames.Count; } if (keyFrameCount == 0) { _sortedResolvedKeyFrames = null; _areKeyTimesValid = true; return; } _sortedResolvedKeyFrames = new ResolvedKeyFrameEntry[keyFrameCount]; int index = 0; // Initialize the _originalKeyFrameIndex. for ( ; index < keyFrameCount; index++) { _sortedResolvedKeyFrames[index]._originalKeyFrameIndex = index; } // calculationDuration represents the time span we will use to resolve // percent key times. This is defined as the value in the following // precedence order: // 1. The animation's duration, but only if it is a time span, not auto or forever. // 2. The largest time span specified key time of all the key frames. // 3. 1 second, to match the From/To/By animations. TimeSpan calculationDuration = TimeSpan.Zero; Duration duration = Duration; if (duration.HasTimeSpan) { calculationDuration = duration.TimeSpan; } else { calculationDuration = LargestTimeSpanKeyTime; } int maxKeyFrameIndex = keyFrameCount - 1; List <KeyTimeBlock> unspecifiedBlocks = new List <KeyTimeBlock>(); bool hasPacedKeyTimes = false; // // Pass 1: Resolve Percent and Time key times. // index = 0; while (index < keyFrameCount) { KeyTime keyTime = _keyFrames[index].KeyTime; switch (keyTime.Type) { case KeyTimeType.Percent: _sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.FromMilliseconds( keyTime.Percent * calculationDuration.TotalMilliseconds); index++; break; case KeyTimeType.TimeSpan: _sortedResolvedKeyFrames[index]._resolvedKeyTime = keyTime.TimeSpan; index++; break; case KeyTimeType.Paced: case KeyTimeType.Uniform: if (index == maxKeyFrameIndex) { // If the last key frame doesn't have a specific time // associated with it its resolved key time will be // set to the calculationDuration, which is the // defined in the comments above where it is set. // Reason: We only want extra time at the end of the // key frames if the user specifically states that // the last key frame ends before the animation ends. _sortedResolvedKeyFrames[index]._resolvedKeyTime = calculationDuration; index++; } else if (index == 0 && keyTime.Type == KeyTimeType.Paced) { // Note: It's important that this block come after // the previous if block because of rule precendence. // If the first key frame in a multi-frame key frame // collection is paced, we set its resolved key time // to 0.0 for performance reasons. If we didn't, the // resolved key time list would be dependent on the // base value which can change every animation frame // in many cases. _sortedResolvedKeyFrames[index]._resolvedKeyTime = TimeSpan.Zero; index++; } else { if (keyTime.Type == KeyTimeType.Paced) { hasPacedKeyTimes = true; } KeyTimeBlock block = new KeyTimeBlock(); block.BeginIndex = index; // NOTE: We don't want to go all the way up to the // last frame because if it is Uniform or Paced its // resolved key time will be set to the calculation // duration using the logic above. // // This is why the logic is: // ((++index) < maxKeyFrameIndex) // instead of: // ((++index) < keyFrameCount) while ((++index) < maxKeyFrameIndex) { KeyTimeType type = _keyFrames[index].KeyTime.Type; if (type == KeyTimeType.Percent || type == KeyTimeType.TimeSpan) { break; } else if (type == KeyTimeType.Paced) { hasPacedKeyTimes = true; } } Debug.Assert(index < keyFrameCount, "The end index for a block of unspecified key frames is out of bounds."); block.EndIndex = index; unspecifiedBlocks.Add(block); } break; } } // // Pass 2: Resolve Uniform key times. // for (int j = 0; j < unspecifiedBlocks.Count; j++) { KeyTimeBlock block = unspecifiedBlocks[j]; TimeSpan blockBeginTime = TimeSpan.Zero; if (block.BeginIndex > 0) { blockBeginTime = _sortedResolvedKeyFrames[block.BeginIndex - 1]._resolvedKeyTime; } // The number of segments is equal to the number of key // frames we're working on plus 1. Think about the case // where we're working on a single key frame. There's a // segment before it and a segment after it. // // Time known Uniform Time known // ^ ^ ^ // | | | // | (segment 1) | (segment 2) | Int64 segmentCount = (block.EndIndex - block.BeginIndex) + 1; TimeSpan uniformTimeStep = TimeSpan.FromTicks((_sortedResolvedKeyFrames[block.EndIndex]._resolvedKeyTime - blockBeginTime).Ticks / segmentCount); index = block.BeginIndex; TimeSpan resolvedTime = blockBeginTime + uniformTimeStep; while (index < block.EndIndex) { _sortedResolvedKeyFrames[index]._resolvedKeyTime = resolvedTime; resolvedTime += uniformTimeStep; index++; } } // // Pass 3: Resolve Paced key times. // if (hasPacedKeyTimes) { ResolvePacedKeyTimes(); } // // Sort resolved key frame entries. // Array.Sort(_sortedResolvedKeyFrames); _areKeyTimesValid = true; return; }