// ------------------------------------------------------ // Using Directives // ------------------------------------------------------ using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using System; // ------------------------------------------------------ // XY - A double precision point // ------------------------------------------------------ public class XY { // ------------------------------------------------------ // XY - Construct from 'x' and 'y' // ------------------------------------------------------ public XY(double x, double y) { X = x; Y = y; } // ------------------------------------------------------ // XY - Construct from another XY object, 'xy' // ------------------------------------------------------ public XY(XY xy) : this(xy.X, xy.Y) { } // ------------------------------------------------------ // Distance - Return the distance to 'other' // ------------------------------------------------------ public double Distance(XY other) { double xDelta = other.X - X; double yDelta = other.Y - Y; return(Math.Sqrt(xDelta * xDelta + yDelta * yDelta)); } // ------------------------------------------------------ // GetAngle - Return the angle between our point // and 'other' // ------------------------------------------------------ public double GetAngle(XY other) { return(Math.Atan2(other.Y - Y, other.X - X)); } public double X { get; set; } public double Y { get; set; } } // ------------------------------------------------------ // Dot // ------------------------------------------------------ public class Dot { // ------------------------------------------------------ // Dot - Create a Dot using 'speed' and 'point' // ------------------------------------------------------ public Dot(double speed, List points) { mSpeed = speed; mPoints = points; mIndex = 0; mCurrent = new XY(points[mIndex]); mPen = new Pen(Color.FromArgb(0, 0, 255), 2); } // ------------------------------------------------------ // Update - Update the dot's (x, y) location // ------------------------------------------------------ public void Update() { int indexNext = GetIndexNext(mIndex); double distance = mCurrent.Distance(mPoints[indexNext]); double moveBy = mSpeed; if(mSpeed >= distance) { mCurrent.X = mPoints[indexNext].X; mCurrent.Y = mPoints[indexNext].Y; mIndex = indexNext; indexNext = GetIndexNext(mIndex); moveBy = mSpeed - distance; } if(moveBy > 0.0) { double angle = mCurrent.GetAngle(mPoints[indexNext]); mCurrent.X += moveBy * Math.Cos(angle); mCurrent.Y += moveBy * Math.Sin(angle); } } // ------------------------------------------------------ // Render - Draw the dot at its current location // ------------------------------------------------------ public void Render(Graphics g) { for(int i = 0; i < mPoints.Count; i ++) { int x0 = R(mPoints[i].X); int y0 = R(mPoints[i].Y); int x1 = R(mPoints[(i + 1) % mPoints.Count].X); int y1 = R(mPoints[(i + 1) % mPoints.Count].Y); g.DrawLine(mPen, x0, y0, x1, y1); } g.FillEllipse( Brushes.Black, R(mCurrent.X) - 5, R(mCurrent.Y) - 5, 10, 10 ); } // ------------------------------------------------------ // R - Round 'value' to the nearest integer // ------------------------------------------------------ public static int R(double value) { return((int)Math.Round(value)); } // ------------------------------------------------------ // GetIndexNext - Find the next index in 'mPoints' // after 'theIndex' // ------------------------------------------------------ private int GetIndexNext(int theIndex) { // Move towards our next point (clockwise or counterclockwise // depending upon whether mPoints.Count is odd or even) int add = (mPoints.Count % 2 == 0 ? 1 : mPoints.Count - 1); int indexNext = (theIndex + add) % mPoints.Count; return(indexNext); } private XY mCurrent; private double mSpeed; private int mIndex; private List mPoints; private Pen mPen; } // ------------------------------------------------------ // DotsForm // ------------------------------------------------------ public class DotsForm : Form { public DotsForm() { // ------------------------------------------------------ // Size our form // ------------------------------------------------------ Size = new Size(FORM_WIDTH, FORM_HEIGHT); // ------------------------------------------------------ // Create our dots // ------------------------------------------------------ mDots = new List(); int xc = ClientSize.Width / 2; int yc = ClientSize.Height / 2; const int SIDE_LENGTH = 75; const int NUMBER = 16; for(int i = 3; i <= 15; i ++) { double radians = D2R(270); double angleOne = (360.0 / i); double angleTwo = (180 - angleOne) / 2.0; double radius = (SIDE_LENGTH * Math.Sin(D2R(angleTwo))) / Math.Sin(D2R(angleOne)); var points = new List(); for(int j = 0; j < i; j ++) { double x = xc + radius * Math.Cos(radians); double y = yc + radius * Math.Sin(radians); points.Add(new XY(x, y)); radians += D2R(angleOne); } int divisor = GetNearestDivisor(i, NUMBER); double factor = 4.0; if(divisor != i) factor *= (double)i / divisor; mDots.Add(new Dot(factor, points)); } // ------------------------------------------------------ // Set up our timer // ------------------------------------------------------ mMoveDotsTimer.Enabled = true; mMoveDotsTimer.Interval = 10; mMoveDotsTimer.Tick += new System.EventHandler(MoveDotsTick); // ------------------------------------------------------ // And our form, use double buffering to reduce flicker // ------------------------------------------------------ FormBorderStyle = FormBorderStyle.Fixed3D; Text = "Dots"; Paint += new PaintEventHandler(RenderDots); SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true ); UpdateStyles(); } // ------------------------------------------------------ // MoveDotsTick - Update the location of each dot // ------------------------------------------------------ private void MoveDotsTick(object sender, EventArgs e) { foreach(Dot d in mDots) d.Update(); Refresh(); } // ------------------------------------------------------ // RenderDots - Draw each dot at its current location // ------------------------------------------------------ private void RenderDots(object sender, PaintEventArgs e) { e.Graphics.Clear(BackColor); mDots.ForEach(delegate(Dot d) { d.Render(e.Graphics); }); } // ------------------------------------------------------ // D2R - Convert 'degrees' to radians // ------------------------------------------------------ public static double D2R(double degrees) { return(degrees * Math.PI / 180.0); } // ------------------------------------------------------ // GetNearestDivisor - Return the nearest number to // 'value' that divides evenly // into 'number' // ------------------------------------------------------ public static int GetNearestDivisor(int value, int number) { int divisor = 0; int v1 = value; int v2 = value; while(divisor == 0) { if(number % v1 == 0) { divisor = v1; } else { v1++; if(number % v2 == 0) { divisor = v2; } else v2--; } } return(divisor); } // ------------------------------------------------------ // Settings // ------------------------------------------------------ private const int FORM_WIDTH = 400; private const int FORM_HEIGHT = 400; // ------------------------------------------------------ // Member variables // ------------------------------------------------------ private Timer mMoveDotsTimer = new System.Windows.Forms.Timer(); private List mDots = new List(); // ------------------------------------------------------ // Main // ------------------------------------------------------ [STAThread] static void Main() { Application.Run(new DotsForm()); } }