Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

Tergiver - Animation System

By: a guest on Oct 22nd, 2010  |  syntax: C#  |  size: 16.24 KB  |  views: 46  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Diagnostics;
  5. using System.Reflection;
  6.  
  7. #region Animation Management
  8.  
  9. public enum AnimationTerminate
  10. {
  11.     Cancel,
  12.     End,
  13. }
  14.  
  15. public class AnimationManager : IDisposable
  16. {
  17.     protected List<Animation> activeAnimations = new List<Animation>();
  18.     protected System.Windows.Forms.Timer timer1;
  19.  
  20.     public AnimationManager()
  21.     {
  22.         this.timer1 = new System.Windows.Forms.Timer();
  23.         this.timer1.Enabled = false;
  24.         this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
  25.         this.timer1.Interval = 50;
  26.     }
  27.  
  28.     // This is the time interval (in ms) of the animation pulses. It has no effect on
  29.     //  the timing of animations, but can be increased or decreased to adjust how
  30.     //  course or fine the animations run.
  31.     [Description("The time interval of the animation pulses in milliseconds.")]
  32.     [DefaultValue(50)]
  33.     public int PulseInterval
  34.     {
  35.         get { return timer1.Interval; }
  36.         set { timer1.Interval = value; }
  37.     }
  38.  
  39.     // Returns true if there are currently any animations playing.
  40.     [Browsable(false)]
  41.     public bool Animating { get { return activeAnimations.Count > 0; } }
  42.  
  43.     // Adding an animation will begin it immediately.
  44.     public void Add(Animation animation)
  45.     {
  46.         if (animation == null)
  47.             throw new ArgumentNullException("animation");
  48.         animation.StartTime = DateTime.Now;
  49.         animation.Begin();
  50.         if (animation.IsComplete)
  51.             animation.End();
  52.         else
  53.         {
  54.             activeAnimations.Add(animation);
  55.             timer1.Enabled = true;
  56.         }
  57.     }
  58.  
  59.     // The only way to abort an individual animation is to call the Remove method here.
  60.     public void Remove(Animation animation, AnimationTerminate terminate)
  61.     {
  62.         if (animation == null)
  63.             throw new ArgumentNullException("animation");
  64.         if (activeAnimations.Contains(animation))
  65.         {
  66.             activeAnimations.Remove(animation);
  67.             if (terminate == AnimationTerminate.End)
  68.                 animation.End();
  69.         }
  70.         if (activeAnimations.Count == 0)
  71.             timer1.Enabled = false;
  72.     }
  73.  
  74.     // Stops all animations.
  75.     public void Clear()
  76.     {
  77.         foreach (Animation animation in activeAnimations)
  78.             animation.End();
  79.         activeAnimations.Clear();
  80.         timer1.Enabled = false;
  81.     }
  82.  
  83.     private void timer1_Tick(object sender, EventArgs e)
  84.     {
  85.         if (activeAnimations.Count > 0)
  86.         {
  87.             // Enumerate all active animations and invoke their Pulse method
  88.             for (int n = 0; n < activeAnimations.Count; n++)
  89.             {
  90.                 Animation animation = activeAnimations[n];
  91.  
  92.                 TimeSpan elapsed = DateTime.Now - animation.StartTime;
  93.                 animation.Pulse(elapsed);
  94.                 // If the animation has run its course, end it and add it to the deletion list
  95.                 if (animation.IsComplete)
  96.                 {
  97.                     animation.End();
  98.                     activeAnimations.RemoveAt(n--);
  99.                 }
  100.             }
  101.         }
  102.  
  103.         // Redundant Count test required as Count can be changed above
  104.         if (activeAnimations.Count == 0)
  105.             timer1.Enabled = false;
  106.     }
  107.  
  108.     #region IDisposable Members
  109.  
  110.     public void Dispose()
  111.     {
  112.         if (timer1 != null)
  113.             timer1.Dispose();
  114.     }
  115.  
  116.     #endregion
  117. }
  118.  
  119.  
  120. // An animation is anything that has a beginning, "runs" for a period of time (receiving periodic
  121. //  pulses), and then ends.
  122.  
  123. public abstract class Animation
  124. {
  125.     // Time the animation began. This is set by the AnimationManager.
  126.     public DateTime StartTime { get; internal set; }
  127.  
  128.     // Has the animation run its course? The animation itself is responsible for setting this to true.
  129.     public bool IsComplete { get; protected set; }
  130.  
  131.     // Called by the AnimationManager when the animation is started. If IsComplete is set to true here
  132.     //  the End method will be called and the Pulse method will never be called. Handy if your "animation"
  133.     //  has a time duration of zero.
  134.     public abstract void Begin();
  135.  
  136.     // Called by the AnimationManager periodically during the animation.
  137.     public abstract void Pulse(TimeSpan elapsed);
  138.  
  139.     // Called by the AnimationManager to signal the animation is done. It is important that
  140.     //  the animation make a final state change to the object it is animating here. That is to say, set
  141.     //  the object's state to be what it should be at the end of the animation.
  142.     public abstract void End();
  143. }
  144.  
  145. // An AnimationGroup is a group of animations that run concurrently. The grouping
  146. //  is just a conveinence to keep them together
  147.  
  148. public sealed class AnimationGroup : Animation
  149. {
  150.     Animation[] animations;
  151.  
  152.     public AnimationGroup(params Animation[] animations)
  153.     {
  154.         if (animations == null)
  155.             throw new ArgumentNullException("animations");
  156.         this.animations = animations;
  157.     }
  158.  
  159.     public override void Begin()
  160.     {
  161.         DateTime now = DateTime.Now;
  162.         foreach (Animation animation in animations)
  163.         {
  164.             animation.StartTime = now;
  165.             animation.Begin();
  166.             if (animation.IsComplete)
  167.                 animation.End();
  168.         }
  169.     }
  170.  
  171.     public override void Pulse(TimeSpan elapsed)
  172.     {
  173.         foreach (Animation animation in animations)
  174.         {
  175.             if (!animation.IsComplete)
  176.             {
  177.                 animation.Pulse(elapsed);
  178.                 if (animation.IsComplete)
  179.                     animation.End();
  180.             }
  181.         }
  182.     }
  183.  
  184.     public override void End()
  185.     {
  186.         foreach (Animation animation in animations)
  187.         {
  188.             if (!animation.IsComplete)
  189.                 animation.End();
  190.         }
  191.     }
  192. }
  193.  
  194. // An AnimationSequence is an ordered list of animations. Each executes completely, one after the other.
  195.  
  196. public sealed class AnimationSequence : Animation
  197. {
  198.     private Queue<Animation> queue;
  199.     private Animation currentAnimation = null;
  200.  
  201.     public AnimationSequence(params Animation[] animations)
  202.     {
  203.         if (animations == null)
  204.             throw new ArgumentNullException("animations");
  205.         queue = new Queue<Animation>(animations);
  206.     }
  207.  
  208.     public AnimationSequence(IEnumerable<Animation> animations)
  209.     {
  210.         if (animations == null)
  211.             throw new ArgumentNullException("animations");
  212.         queue = new Queue<Animation>(animations);
  213.     }
  214.  
  215.     public override void Begin()
  216.     {
  217.     // This "animation" is complete, only after all of the animations in the list complete.
  218.     // The odd logic here is in the case of an animation calling itself complete after
  219.     //  we call Begin (a zero-length animation), so we have to return to the queue for
  220.     //  the next one.
  221.  
  222.     TryNext:
  223.         if (queue.Count == 0)
  224.             IsComplete = true;
  225.         else
  226.         {
  227.             currentAnimation = queue.Dequeue();
  228.             currentAnimation.StartTime = DateTime.Now;
  229.             currentAnimation.Begin();
  230.             if (currentAnimation.IsComplete)
  231.             {
  232.                 currentAnimation.End();
  233.                 goto TryNext;
  234.             }
  235.         }
  236.     }
  237.  
  238.     public override void Pulse(TimeSpan elapsed)
  239.     {
  240.         // This should not fire because we should not get a Pulse call if we have no running animation.
  241.         Debug.Assert(currentAnimation != null, "currentAnimation != null");
  242.  
  243.         // Fire off the pulse for the current animation. Note that we pass an elapsed
  244.         //  time of Now minus the animation's start time, not the elapsed value passed
  245.         // to us which is the amount of time elapsed for the entire sequence (so far).
  246.         elapsed = DateTime.Now - currentAnimation.StartTime;
  247.         currentAnimation.Pulse(elapsed);
  248.  
  249.         // If the current animation has completed
  250.         if (currentAnimation.IsComplete)
  251.         {
  252.             // Fire off its End
  253.             currentAnimation.End();
  254.             currentAnimation = null;
  255.             // Queue up next animation
  256.             Begin();
  257.         }
  258.     }
  259.  
  260.     public override void End()
  261.     {
  262.         // If End is called (on us) before all of our animations have ended,
  263.         //  we need to call End on each of the remaining ones so that they have the
  264.         //  opportunity to set their animation objects to their final state.
  265.         while (currentAnimation != null)
  266.         {
  267.             currentAnimation.End();
  268.             currentAnimation = queue.Count > 0 ? queue.Dequeue() : null;
  269.         }
  270.     }
  271. }
  272.  
  273. #endregion
  274.  
  275. #region Animations
  276.  
  277.  
  278. // DelayAnimation is an animation that does nothing but delay for the specified amount of time.
  279.  
  280. public class DelayAnimation : Animation
  281. {
  282.     TimeSpan delay;
  283.     public DelayAnimation(TimeSpan delay)
  284.     {
  285.         this.delay = delay;
  286.     }
  287.  
  288.     public override void Begin()
  289.     {
  290.         if (delay.TotalMilliseconds <= 0)
  291.             IsComplete = true;
  292.     }
  293.  
  294.     public override void Pulse(TimeSpan elapsed)
  295.     {
  296.         if (elapsed >= delay)
  297.             IsComplete = true;
  298.     }
  299.  
  300.     public override void End()
  301.     {
  302.         IsComplete = true;
  303.     }
  304. }
  305.  
  306.  
  307. // A PropertyAnimation allows any (non-indexed.. for now) object property to be animated
  308. // using an IAnimator object
  309.  
  310. public class PropertyAnimation : Animation
  311. {
  312.     protected object instance;
  313.     protected object startValue, endValue;
  314.     protected string propertyName;
  315.     protected PropertyInfo propertyInfo;
  316.     protected IAnimator animator;
  317.     protected TimeSpan duration;
  318.  
  319.     // This ctor creates a zero-length animation of the property change (i.e. it'll change
  320.     //  immediately). This is useful if you want to change a property during an AnimationSequence.
  321.     public PropertyAnimation(string propertyName, object instance, object endValue)
  322.         : this(propertyName, instance, endValue, TimeSpan.Zero, null)
  323.     {
  324.     }
  325.  
  326.     // Create an animation on an object's property. The animator determains how the property
  327.     //  morphs over time.
  328.     public PropertyAnimation(string propertyName, object instance, object endValue, TimeSpan duration, IAnimator animator)
  329.     {
  330.         if (String.IsNullOrEmpty(propertyName))
  331.             throw new ArgumentNullException("propertyName");
  332.         if (instance == null)
  333.             throw new ArgumentNullException("instance");
  334.         this.propertyName = propertyName;
  335.         this.instance = instance;
  336.         this.propertyInfo = instance.GetType().GetProperty(propertyName);
  337.         // Assure that the endValue passed is of the correct type to be set to the target property
  338.         this.endValue = Convert.ChangeType(endValue, propertyInfo.PropertyType);
  339.         this.animator = animator;
  340.         this.duration = duration;
  341.     }
  342.  
  343.     public override void Begin()
  344.     {
  345.         // If no animator is specified or the duration is zero, flag ourselves complete so
  346.         //  that Pulse is not called.
  347.         if (animator == null || duration == TimeSpan.Zero)
  348.             IsComplete = true;
  349.         // Otherwise we cache the initial value of the property for use during Pulse
  350.         else
  351.             this.startValue = propertyInfo.GetValue(instance, null);
  352.     }
  353.  
  354.     public override void Pulse(TimeSpan elapsed)
  355.     {
  356.         // If time has expired, we are done
  357.         if (elapsed >= duration)
  358.             IsComplete = true;
  359.         // Otherwise set the property to an intermediate value, determined by the Animator
  360.         else
  361.         {
  362.             // Calculate the "percentage done" for this pulse
  363.             float fraction = (float)elapsed.Ticks / (float)duration.Ticks;
  364.  
  365.             // Let the Animator do the work of generating an intermediate value
  366.             object temp = animator.ComputeValue(startValue, endValue, fraction);
  367.  
  368.             // Assure that the Animator's return value is of a compatible type
  369.             temp = Convert.ChangeType(temp, propertyInfo.PropertyType);
  370.  
  371.             // Change the property
  372.             propertyInfo.SetValue(instance, temp, null);
  373.         }
  374.     }
  375.  
  376.     public override void End()
  377.     {
  378.         // Change the property to its final value
  379.         propertyInfo.SetValue(instance, endValue, null);
  380.     }
  381. }
  382. #endregion
  383.  
  384.  
  385. #region Animators
  386.  
  387. // IAnimator is the workhorse for animating values
  388.  
  389. public interface IAnimator
  390. {
  391.     // ComputeValue will receive a starting value, ending value, and a fraction
  392.     //  representing a point between the two.
  393.     // If given an amount of 0.0, this function is expected to return the startValue.
  394.     // If given an amount of 1.0, this function is expected to return the endValue.
  395.     // If given an amount between 0.0 and 1.0, this function should return a value that
  396.     //  represents some appropriate "distance" between the two.
  397.     object ComputeValue(object startValue, object endValue, float amount);
  398. }
  399.  
  400.  
  401. // The ColorAnimator returns a linear interpolation between two color values representing
  402. //  the distance between the two.
  403.  
  404. public class ColorAnimator : IAnimator
  405. {
  406.     public object ComputeValue(object startValue, object endValue, float amount)
  407.     {
  408.         System.Drawing.Color color1 = (System.Drawing.Color)startValue;
  409.         System.Drawing.Color color2 = (System.Drawing.Color)endValue;
  410.         int r1 = color1.R;
  411.         int g1 = color1.G;
  412.         int b1 = color1.B;
  413.         int r2 = color2.R;
  414.         int g2 = color2.G;
  415.         int b2 = color2.B;
  416.         int n = (int)MathHelper.ClampAndRound(65536f * amount, 0f, 65536f);
  417.         int r3 = r1 + (((r2 - r1) * n) >> 16);
  418.         int g3 = g1 + (((g2 - g1) * n) >> 16);
  419.         int b3 = b1 + (((b2 - b1) * n) >> 16);
  420.  
  421.         return System.Drawing.Color.FromArgb(r3, g3, b3);
  422.     }
  423. }
  424.  
  425. // The DoubleAnimator returns a linear interpolation between two double values.
  426. public class DoubleAnimator : IAnimator
  427. {
  428.     public object ComputeValue(object startValue, object endValue, float amount)
  429.     {
  430.         double s = Convert.ToDouble(startValue);
  431.         double e = Convert.ToDouble(endValue);
  432.         int n = (int)MathHelper.ClampAndRound(65536f * amount, 0f, 65536f);
  433.         double r = s + (((e - s) * n) / 65536);
  434.         return r;
  435.     }
  436. }
  437.  
  438. // The IntegerAnimator returns a linear interpolation between two integer values.
  439. public class IntegerAnimator : IAnimator
  440. {
  441.     public object ComputeValue(object startValue, object endValue, float amount)
  442.     {
  443.         int s = Convert.ToInt32(startValue);
  444.         int e = Convert.ToInt32(endValue);
  445.         int n = (int)MathHelper.ClampAndRound(65536f * amount, 0f, 65536f);
  446.         int r = s + (((e - s) * n) >> 16);
  447.         return r;
  448.     }
  449. }
  450.  
  451. public class SizeAnimator : IAnimator
  452. {
  453.     public object ComputeValue(object startValue, object endValue, float amount)
  454.     {
  455.         System.Drawing.Size s = (System.Drawing.Size)startValue;
  456.         System.Drawing.Size e = (System.Drawing.Size)endValue;
  457.         int n = (int)MathHelper.ClampAndRound(65536f * amount, 0f, 65536f);
  458.         int w = s.Width + (((e.Width - s.Width) * n) >> 16);
  459.         int h = s.Height + (((e.Height - s.Height) * n) >> 16);
  460.         return new System.Drawing.Size(w, h);
  461.     }
  462. }
  463.  
  464. public class PointAnimator : IAnimator
  465. {
  466.     public object ComputeValue(object startValue, object endValue, float amount)
  467.     {
  468.         System.Drawing.Point s = (System.Drawing.Point)startValue;
  469.         System.Drawing.Point e = (System.Drawing.Point)endValue;
  470.         int n = (int)MathHelper.ClampAndRound(65536f * amount, 0f, 65536f);
  471.         int x = s.X + (((e.X - s.X) * n) >> 16);
  472.         int y = s.Y + (((e.Y - s.Y) * n) >> 16);
  473.         return new System.Drawing.Point(x, y);
  474.     }
  475. }
  476.  
  477. #endregion
  478.  
  479. #region Math Helper
  480.  
  481. internal static class MathHelper
  482. {
  483.     // Clamps the value to min and max while also rounding the value.
  484.     internal static double ClampAndRound(float value, float min, float max)
  485.     {
  486.         if (float.IsNaN(value))
  487.             return 0.0;
  488.         if (float.IsInfinity(value))
  489.             return (float.IsNegativeInfinity(value) ? ((double)min) : ((double)max));
  490.         if (value < min)
  491.             return (double)min;
  492.         if (value > max)
  493.             return (double)max;
  494.         return Math.Round((double)value);
  495.     }
  496. }
  497.  
  498. #endregion