using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace SnakeMonoGame { public static class Primitives2D { #region Private Members private static readonly Dictionary> circleCache = new Dictionary>(); //private static readonly Dictionary> arcCache = new Dictionary>(); private static Texture2D pixel; #endregion #region Private Methods private static void CreateThePixel(SpriteBatch spriteBatch) { pixel = new Texture2D(spriteBatch.GraphicsDevice, 1, 1, false, SurfaceFormat.Color); pixel.SetData(new[] { Color.White }); } /// /// Draws a list of connecting points /// /// The destination drawing surface /// /// Where to position the points /// The points to connect with lines /// The color to use /// The thickness of the lines private static void DrawPoints(SpriteBatch spriteBatch, Vector2 position, List points, Color color, float thickness) { if (points.Count < 2) return; for (int i = 1; i < points.Count; i++) { DrawLine(spriteBatch, points[i - 1] + position, points[i] + position, color, thickness); } } /// /// Creates a list of vectors that represents a circle /// /// The radius of the circle /// The number of sides to generate /// A list of vectors that, if connected, will create a circle private static List CreateCircle(double radius, int sides) { // Look for a cached version of this circle String circleKey = radius + "x" + sides; if (circleCache.ContainsKey(circleKey)) { return circleCache[circleKey]; } List vectors = new List(); const double max = 2.0 * Math.PI; double step = max / sides; for (double theta = 0.0; theta < max; theta += step) { vectors.Add(new Vector2((float)(radius * Math.Cos(theta)), (float)(radius * Math.Sin(theta)))); } // then add the first vector again so it's a complete loop vectors.Add(new Vector2((float)(radius * Math.Cos(0)), (float)(radius * Math.Sin(0)))); // Cache this circle so that it can be quickly drawn next time circleCache.Add(circleKey, vectors); return vectors; } /// /// Creates a list of vectors that represents an arc /// /// The radius of the arc /// The number of sides to generate in the circle that this will cut out from /// The starting angle of arc, 0 being to the east, increasing as you go clockwise /// The radians to draw, clockwise from the starting angle /// A list of vectors that, if connected, will create an arc private static List CreateArc(float radius, int sides, float startingAngle, float radians) { List points = new List(); points.AddRange(CreateCircle(radius, sides)); points.RemoveAt(points.Count - 1); // remove the last point because it's a duplicate of the first // The circle starts at (radius, 0) double curAngle = 0.0; double anglePerSide = MathHelper.TwoPi / sides; // "Rotate" to the starting point while ((curAngle + (anglePerSide / 2.0)) < startingAngle) { curAngle += anglePerSide; // move the first point to the end points.Add(points[0]); points.RemoveAt(0); } // Add the first point, just in case we make a full circle points.Add(points[0]); // Now remove the points at the end of the circle to create the arc int sidesInArc = (int)((radians / anglePerSide) + 0.5); points.RemoveRange(sidesInArc + 1, points.Count - sidesInArc - 1); return points; } #endregion #region FillRectangle /// /// Draws a filled rectangle /// /// The destination drawing surface /// The rectangle to draw /// The color to draw the rectangle in public static void FillRectangle(this SpriteBatch spriteBatch, Rectangle rect, Color color) { if (pixel == null) { CreateThePixel(spriteBatch); } // Simply use the function already there spriteBatch.Draw(pixel, rect, color); } /// /// Draws a filled rectangle /// /// The destination drawing surface /// The rectangle to draw /// The color to draw the rectangle in /// The angle in radians to draw the rectangle at public static void FillRectangle(this SpriteBatch spriteBatch, Rectangle rect, Color color, float angle) { if (pixel == null) { CreateThePixel(spriteBatch); } spriteBatch.Draw(pixel, rect, null, color, angle, Vector2.Zero, SpriteEffects.None, 0); } /// /// Draws a filled rectangle /// /// The destination drawing surface /// Where to draw /// The size of the rectangle /// The color to draw the rectangle in public static void FillRectangle(this SpriteBatch spriteBatch, Vector2 location, Vector2 size, Color color) { FillRectangle(spriteBatch, location, size, color, 0.0f); } /// /// Draws a filled rectangle /// /// The destination drawing surface /// Where to draw /// The size of the rectangle /// The angle in radians to draw the rectangle at /// The color to draw the rectangle in public static void FillRectangle(this SpriteBatch spriteBatch, Vector2 location, Vector2 size, Color color, float angle) { if (pixel == null) { CreateThePixel(spriteBatch); } // stretch the pixel between the two vectors spriteBatch.Draw(pixel, location, null, color, angle, Vector2.Zero, size, SpriteEffects.None, 0); } /// /// Draws a filled rectangle /// /// The destination drawing surface /// The X coord of the left side /// The Y coord of the upper side /// Width /// Height /// The color to draw the rectangle in public static void FillRectangle(this SpriteBatch spriteBatch, float x, float y, float w, float h, Color color) { FillRectangle(spriteBatch, new Vector2(x, y), new Vector2(w, h), color, 0.0f); } /// /// Draws a filled rectangle /// /// The destination drawing surface /// The X coord of the left side /// The Y coord of the upper side /// Width /// Height /// The color to draw the rectangle in /// The angle of the rectangle in radians public static void FillRectangle(this SpriteBatch spriteBatch, float x, float y, float w, float h, Color color, float angle) { FillRectangle(spriteBatch, new Vector2(x, y), new Vector2(w, h), color, angle); } #endregion #region DrawRectangle /// /// Draws a rectangle with the thickness provided /// /// The destination drawing surface /// The rectangle to draw /// The color to draw the rectangle in public static void DrawRectangle(this SpriteBatch spriteBatch, Rectangle rect, Color color) { DrawRectangle(spriteBatch, rect, color, 1.0f); } /// /// Draws a rectangle with the thickness provided /// /// The destination drawing surface /// The rectangle to draw /// The color to draw the rectangle in /// The thickness of the lines public static void DrawRectangle(this SpriteBatch spriteBatch, Rectangle rect, Color color, float thickness) { // TODO: Handle rotations // TODO: Figure out the pattern for the offsets required and then handle it in the line instead of here DrawLine(spriteBatch, new Vector2(rect.X, rect.Y), new Vector2(rect.Right, rect.Y), color, thickness); // top DrawLine(spriteBatch, new Vector2(rect.X + 1f, rect.Y), new Vector2(rect.X + 1f, rect.Bottom + thickness), color, thickness); // left DrawLine(spriteBatch, new Vector2(rect.X, rect.Bottom), new Vector2(rect.Right, rect.Bottom), color, thickness); // bottom DrawLine(spriteBatch, new Vector2(rect.Right + 1f, rect.Y), new Vector2(rect.Right + 1f, rect.Bottom + thickness), color, thickness); // right } /// /// Draws a rectangle with the thickness provided /// /// The destination drawing surface /// Where to draw /// The size of the rectangle /// The color to draw the rectangle in public static void DrawRectangle(this SpriteBatch spriteBatch, Vector2 location, Vector2 size, Color color) { DrawRectangle(spriteBatch, new Rectangle((int)location.X, (int)location.Y, (int)size.X, (int)size.Y), color, 1.0f); } /// /// Draws a rectangle with the thickness provided /// /// The destination drawing surface /// Where to draw /// The size of the rectangle /// The color to draw the rectangle in /// The thickness of the line public static void DrawRectangle(this SpriteBatch spriteBatch, Vector2 location, Vector2 size, Color color, float thickness) { DrawRectangle(spriteBatch, new Rectangle((int)location.X, (int)location.Y, (int)size.X, (int)size.Y), color, thickness); } #endregion #region DrawLine /// /// Draws a line from point1 to point2 with an offset /// /// The destination drawing surface /// The X coord of the first point /// The Y coord of the first point /// The X coord of the second point /// The Y coord of the second point /// The color to use public static void DrawLine(this SpriteBatch spriteBatch, float x1, float y1, float x2, float y2, Color color) { DrawLine(spriteBatch, new Vector2(x1, y1), new Vector2(x2, y2), color, 1.0f); } /// /// Draws a line from point1 to point2 with an offset /// /// The destination drawing surface /// The X coord of the first point /// The Y coord of the first point /// The X coord of the second point /// The Y coord of the second point /// The color to use /// The thickness of the line public static void DrawLine(this SpriteBatch spriteBatch, float x1, float y1, float x2, float y2, Color color, float thickness) { DrawLine(spriteBatch, new Vector2(x1, y1), new Vector2(x2, y2), color, thickness); } /// /// Draws a line from point1 to point2 with an offset /// /// The destination drawing surface /// The first point /// The second point /// The color to use public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color) { DrawLine(spriteBatch, point1, point2, color, 1.0f); } /// /// Draws a line from point1 to point2 with an offset /// /// The destination drawing surface /// The first point /// The second point /// The color to use /// The thickness of the line public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, float thickness) { // calculate the distance between the two vectors float distance = Vector2.Distance(point1, point2); // calculate the angle between the two vectors float angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X); DrawLine(spriteBatch, point1, distance, angle, color, thickness); } /// /// Draws a line from point1 to point2 with an offset /// /// The destination drawing surface /// The starting point /// The length of the line /// The angle of this line from the starting point in radians /// The color to use public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point, float length, float angle, Color color) { DrawLine(spriteBatch, point, length, angle, color, 1.0f); } /// /// Draws a line from point1 to point2 with an offset /// /// The destination drawing surface /// The starting point /// The length of the line /// The angle of this line from the starting point /// The color to use /// The thickness of the line public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point, float length, float angle, Color color, float thickness) { if (pixel == null) { CreateThePixel(spriteBatch); } // stretch the pixel between the two vectors spriteBatch.Draw(pixel, point, null, color, angle, Vector2.Zero, new Vector2(length, thickness), SpriteEffects.None, 0); } #endregion #region PutPixel public static void PutPixel(this SpriteBatch spriteBatch, float x, float y, Color color) { PutPixel(spriteBatch, new Vector2(x, y), color); } public static void PutPixel(this SpriteBatch spriteBatch, Vector2 position, Color color) { if (pixel == null) { CreateThePixel(spriteBatch); } spriteBatch.Draw(pixel, position, color); } #endregion #region DrawCircle /// /// Draw a circle /// /// The destination drawing surface /// The center of the circle /// The radius of the circle /// The number of sides to generate /// The color of the circle public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color) { DrawPoints(spriteBatch, center, CreateCircle(radius, sides), color, 1.0f); } /// /// Draw a circle /// /// The destination drawing surface /// The center of the circle /// The radius of the circle /// The number of sides to generate /// The color of the circle /// The thickness of the lines used public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color, float thickness) { DrawPoints(spriteBatch, center, CreateCircle(radius, sides), color, thickness); } /// /// Draw a circle /// /// The destination drawing surface /// The center X of the circle /// The center Y of the circle /// The radius of the circle /// The number of sides to generate /// The color of the circle public static void DrawCircle(this SpriteBatch spriteBatch, float x, float y, float radius, int sides, Color color) { DrawPoints(spriteBatch, new Vector2(x, y), CreateCircle(radius, sides), color, 1.0f); } /// /// Draw a circle /// /// The destination drawing surface /// The center X of the circle /// The center Y of the circle /// The radius of the circle /// The number of sides to generate /// The color of the circle /// The thickness of the lines used public static void DrawCircle(this SpriteBatch spriteBatch, float x, float y, float radius, int sides, Color color, float thickness) { DrawPoints(spriteBatch, new Vector2(x, y), CreateCircle(radius, sides), color, thickness); } #endregion #region DrawArc /// /// Draw a arc /// /// The destination drawing surface /// The center of the arc /// The radius of the arc /// The number of sides to generate /// The starting angle of arc, 0 being to the east, increasing as you go clockwise /// The number of radians to draw, clockwise from the starting angle /// The color of the arc public static void DrawArc(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, float startingAngle, float radians, Color color) { DrawArc(spriteBatch, center, radius, sides, startingAngle, radians, color, 1.0f); } /// /// Draw a arc /// /// The destination drawing surface /// The center of the arc /// The radius of the arc /// The number of sides to generate /// The starting angle of arc, 0 being to the east, increasing as you go clockwise /// The number of radians to draw, clockwise from the starting angle /// The color of the arc /// The thickness of the arc public static void DrawArc(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, float startingAngle, float radians, Color color, float thickness) { List arc = CreateArc(radius, sides, startingAngle, radians); //List arc = CreateArc2(radius, sides, startingAngle, degrees); DrawPoints(spriteBatch, center, arc, color, thickness); } #endregion } }