Mega Code Archive

 
Categories / C# Tutorial / 2D Graphics
 

Clock animation

/* Quote from  Programming .NET Windows Applications By Jesse Liberty, Dan Hurwitz First Edition October 2003  Pages: 1246 (More details) */ using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Timers; using System.Windows.Forms; namespace Clock3CS {   // Rename the class   public class ClockFace : System.Windows.Forms.Form   {     // Required designer variable.     private System.ComponentModel.Container components = null;        private int FaceRadius = 450;    // size of the clock face     private bool b24Hours = false;    // 24 hour clock face?     private System.Windows.Forms.Button btnClockFormat;         private DateTime currentTime;    // used in more than one method     // new     private int xCenter;        // center of the clock     private int yCenter;     private static int DateRadius = 600; // outer circumference for date     private static int Offset = 0;    // for moving the text      Font font = new Font("Arial", 40);  // use the same font throughout     private StringDraw sdToday;      // the text to animate     public ClockFace()     {       // Required for Windows Form Designer support       InitializeComponent();       // use the user's choice of colors       BackColor = SystemColors.Window;       ForeColor = SystemColors.WindowText;            // *** begin new       string today = System.DateTime.Now.ToLongDateString();       today = " " + today.Replace(",","");              // create a new stringdraw object with today's date       sdToday = new StringDraw(today,this);       currentTime = DateTime.Now;       // set the current center based on the       // client area       xCenter = Width / 2;       yCenter = Height / 2;       // *** end new       // update the clock by timer       System.Timers.Timer timer = new System.Timers.Timer();       timer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimer);       timer.Interval = 20;  // shorter interval - more movement       timer.Enabled = true;     }     protected override void OnPaint ( PaintEventArgs e )     {       base.OnPaint(e);       Graphics g = e.Graphics;       SetScale(g);       DrawFace(g);       DrawTime(g,true);  // force an update     }     // every time the timer event fires, update the clock     public void OnTimer(Object source, ElapsedEventArgs e)     {       Graphics g = this.CreateGraphics();       SetScale(g);       DrawFace(g);       DrawTime(g,false);       DrawDate(g);       g.Dispose();                }     #region Windows Form Designer generated code     protected override void Dispose( bool disposing )     {       if( disposing )       {         if (components != null)          {           components.Dispose();         }       }       base.Dispose( disposing );     }          /// <summary>     /// Required method for Designer support - do not modify     /// the contents of this method with the code editor.     /// </summary>     private void InitializeComponent()     {       this.btnClockFormat = new System.Windows.Forms.Button();       this.SuspendLayout();       //        // btnClockFormat       //        this.btnClockFormat.Location = new System.Drawing.Point(8, 8);       this.btnClockFormat.Name = "btnClockFormat";       this.btnClockFormat.TabIndex = 1;       this.btnClockFormat.Text = "24 Hours";       this.btnClockFormat.Click += new System.EventHandler(this.btnClockFormat_Click);       //        // ClockFace       //        this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);       this.ClientSize = new System.Drawing.Size(292, 266);       this.Controls.AddRange(new System.Windows.Forms.Control[] {                                       this.btnClockFormat});       this.Name = "ClockFace";       this.Text = "Clock3CS";       this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ClockFace_MouseDown);       this.ResumeLayout(false);     }     #endregion     [STAThread]     static void Main()      {       Application.Run(new ClockFace());     }     private void SetScale(Graphics g)     {       // if the form is too small, do nothing       if ( Width == 0 || Height == 0 )         return;       // set the origin at the center       g.TranslateTransform(xCenter, yCenter);  // use the members vars       // set inches to the minimum of the width        // or height dividedby the dots per inch         float inches = Math.Min(Width / g.DpiX, Height / g.DpiX);       // set the scale to a grid of 2000 by 2000 units       g.ScaleTransform(         inches * g.DpiX / 2000, inches * g.DpiY / 2000);     }     private void DrawFace(Graphics g)     {       // numbers are in forecolor except flash number in green       // as the seconds go by.       Brush brush = new SolidBrush(ForeColor);       float x, y;       // new code       int numHours = b24Hours ? 24 : 12;       int deg = 360 / numHours;              // for each of the hours on the clock face       for (int i = 1; i <= numHours; i++)       {         // i = hour  30 degrees = offset per hour           // +90 to make 12 straight up         x = GetCos(i*deg + 90) * FaceRadius;         y = GetSin(i*deg + 90) * FaceRadius;         StringFormat format = new StringFormat();         format.Alignment = StringAlignment.Center;         format.LineAlignment = StringAlignment.Center;         g.DrawString(           i.ToString(), font, brush, -x, -y,format);              }  // end for loop     }    // end drawFace     private void DrawTime(Graphics g, bool forceDraw)     {       //  length of the hands       float hourLength = FaceRadius * 0.5f;       float minuteLength = FaceRadius * 0.7f;       float secondLength = FaceRadius * 0.9f;       // set to back color to erase old hands first       Pen hourPen = new Pen(BackColor);       Pen minutePen = new Pen(BackColor);       Pen secondPen = new Pen(BackColor);       // set the arrow heads       hourPen.EndCap = LineCap.ArrowAnchor;       minutePen.EndCap = LineCap.ArrowAnchor;       // hour hand is thicker       hourPen.Width = 30;       minutePen.Width = 20;       // second hand        Brush secondBrush = new SolidBrush(BackColor);       const int EllipseSize = 50;       GraphicsState state;  // to to protect and to serve       // 1 - delete the old time       // delete the old second hand       // figure out how far around to rotate to draw the second hand       // save the current state, rotate, draw and then restore the state       float rotation = GetSecondRotation();       state = g.Save();       g.RotateTransform(rotation);       g.FillEllipse(         secondBrush,         -(EllipseSize/2),         -secondLength,         EllipseSize,         EllipseSize);       g.Restore(state);       DateTime newTime = DateTime.Now;       bool newMin = false;  // has the minute changed?       // if the minute has changed, set the flag       if ( newTime.Minute != currentTime.Minute )         newMin = true;       // if the minute has changed or you must draw anyway then you        // must first delete the old minute and hour hand       if ( newMin  || forceDraw )       {         // figure out how far around to rotate to draw the minute hand         // save the current state, rotate, draw and then restore the state         rotation = GetMinuteRotation();         state = g.Save();         g.RotateTransform(rotation);         g.DrawLine(minutePen,0,0,0,-minuteLength);         g.Restore(state);         // figure out how far around to rotate to draw the hour hand         // save the current state, rotate, draw and then restore the state         rotation = GetHourRotation();         state = g.Save();         g.RotateTransform(rotation);         g.DrawLine(hourPen,0,0,0,-hourLength);         g.Restore(state);       }       // step 2 - draw the new time       currentTime = newTime;       hourPen.Color = Color.Red;       minutePen.Color = Color.Blue;       secondPen.Color = Color.Green;       secondBrush = new SolidBrush(Color.Green);       // draw the new second hand       // figure out how far around to rotate to draw the second hand       // save the current state, rotate, draw and then restore the state       state = g.Save();       rotation = GetSecondRotation();       g.RotateTransform(rotation);       g.FillEllipse(         secondBrush,         -(EllipseSize/2),         -secondLength,         EllipseSize,         EllipseSize);       g.Restore(state);       // if the minute has changed or you must draw anyway then you        // must draw the new minute and hour hand       if ( newMin || forceDraw )       {         // figure out how far around to rotate to draw the minute hand         // save the current state, rotate, draw and then restore the state         state = g.Save();         rotation = GetMinuteRotation();         g.RotateTransform(rotation);         g.DrawLine(minutePen,0,0,0,-minuteLength);         g.Restore(state);         // figure out how far around to rotate to draw the hour hand         // save the current state, rotate, draw and then restore the state         state = g.Save();         rotation = GetHourRotation();         g.RotateTransform(rotation);         g.DrawLine(hourPen,0,0,0,-hourLength);         g.Restore(state);       }     }     // determine the rotation to draw the hour hand     private float GetHourRotation()     {       // degrees depend on 24 vs. 12 hour clock       float deg = b24Hours ? 15 : 30;       float numHours = b24Hours ? 24 : 12;       return( 360f * currentTime.Hour / numHours +         deg * currentTime.Minute / 60f);     }     private float GetMinuteRotation()     {       return( 360f * currentTime.Minute / 60f );      }     private float GetSecondRotation()     {       return(360f * currentTime.Second / 60f);     }     private static float GetSin(float degAngle)     {       return (float) Math.Sin(Math.PI * degAngle / 180f);     }     private static float GetCos(float degAngle)     {       return (float) Math.Cos(Math.PI * degAngle / 180f);     }     private void btnClockFormat_Click(object sender, System.EventArgs e)     {       btnClockFormat.Text = b24Hours ? "24 Hour" : "12 Hour";       b24Hours = ! b24Hours;       this.Invalidate();     }     private void DrawDate(Graphics g)     {       Brush brush = new SolidBrush(ForeColor);       sdToday.DrawString(g,brush);     }     private void ClockFace_MouseDown(       object sender, System.Windows.Forms.MouseEventArgs e)     {       xCenter = e.X;       yCenter = e.Y;       this.Invalidate();          }     // each letter in the outer string knows how to draw itself     private class LtrDraw     {       char myChar;    // the actual letter i draw       float x;      // current x coordinate       float y;      // current y coordinate       float oldx;      // old x coordinate (to delete)       float oldy;      // old y coordinate (to delete)              // constructor       public LtrDraw(char c)       {         myChar = c;       }       // property for X coordinate       public float X       {         get { return x; }         set { oldx = x; x = value; }       }       // property for Y coordinate       public float Y       {         get { return y; }         set { oldy = y; y = value; }       }       // get total width of the string       public float GetWidth(Graphics g, Font font)       {         SizeF stringSize = g.MeasureString(myChar.ToString(),font);         return stringSize.Width;       }       // get total height of the string       public float GetHeight(Graphics g, Font font)       {         SizeF stringSize = g.MeasureString(myChar.ToString(),font);         return stringSize.Height;       }       // get the font from the control and draw the current character       // First delete the old and then draw the new       public void DrawString(Graphics g, Brush brush, ClockFace cf)       {         Font font = cf.font;         Brush blankBrush = new SolidBrush(cf.BackColor);         g.DrawString(myChar.ToString(),font,blankBrush,oldx,oldy);         g.DrawString(myChar.ToString(),font,brush,x,y);       }     }     // holds an array of LtrDraw objects     // and knows how to tell them to draw     private class StringDraw     {       ArrayList theString = new ArrayList();       LtrDraw l;       ClockFace theControl;       // constructor takes a string, populates the array       // and stashes away the calling control (ClockFace)       public StringDraw(string s, ClockFace theControl)       {         this.theControl = theControl;         foreach (char c in s)         {           l = new LtrDraw(c);           theString.Add(l);         }       }       // divide the circle by the number of letters       // and draw each letter in position       public void DrawString(Graphics g, Brush brush)       {         int angle = 360 / theString.Count;         int counter = 0;         foreach (LtrDraw theLtr in theString)         {           // 1. To find the X coordinate, take the Cosine of the angle           // and multiply by the radius.           // 2. To compute the angle, start with the base angle            // (360 divided by the number of letters)           // and multiply by letter position.           // Thus if each letter is 10 degrees, and this is the third           // letter, you get 30 degrees. Add 90 to start at 12 O'clock.           // Each time through, subtract the clockFace offset to move            // the entire string around the clock on each timer call           float newX = GetCos(angle  * counter + 90 - ClockFace.Offset) * ClockFace.DateRadius ;           float newY = GetSin(angle * counter + 90 - ClockFace.Offset) * ClockFace.DateRadius ;           theLtr.X =              newX - (theLtr.GetWidth(g,theControl.font) / 2);           theLtr.Y =              newY - (theLtr.GetHeight(g,theControl.font) / 2);           counter++;           theLtr.DrawString(g,brush,theControl);         }         ClockFace.Offset += 1;  // rotate the entire string       }     }   }  // end class }    // end namespace