using System; using System.Collections.Generic; namespace Otter { /// /// Class representing a Polygon. /// public class Polygon { #region Public Static Methods /// /// Creates a Polygon in the shape of a circle. /// /// The radius of the circle. /// How many steps to use to create the circle (higher is rounder.) /// A circle shaped Polygon. public static Polygon CreateCircle(float radius, int steps = 32) { var poly = new Polygon(); (steps).Times((i) => { var angle = 360f / steps * i; poly.Add(Util.PolarX(angle, radius) + radius, Util.PolarY(angle, radius) + radius); }); return poly; } /// /// Creates a Polygon in the shape of a rectangle. /// /// The width of the rectangle. /// The height of the rectangle. /// A rectangle shaped Polygon. public static Polygon CreateRectangle(float width, float height) { var poly = new Polygon(); poly.Add(0, 0); poly.Add(width, 0); poly.Add(width, height); poly.Add(0, height); return poly; } #endregion #region Public Constructors /// /// Create a new Polygon. /// /// The Vector2 points composing the Polygon. public Polygon(Vector2 firstPoint, params Vector2[] points) { Points = new List(); Points.Add(firstPoint); Points.AddRange(points); } /// /// Create a new Polygon. /// /// The source Polygon to copy. public Polygon(Polygon copy) { Points = new List(); Points.AddRange(copy.Points); } /// /// Create a new Polygon. /// /// A series of points to create the Polygon from (x1, y1, x2, y2, x3, y3...) public Polygon(params float[] points) { Points = new List(); int i = 0; float x = 0; foreach (var p in points) { if (i == 0) { x = p; i = 1; } else { Add(x, p); i = 0; } } if (i == 1) { Add(x, 0); } } #endregion Public Constructors #region Public Properties /// /// The list of Vector2 points. /// public List Points { get; private set; } /// /// The number of points in the Polygon. /// public int Count { get { return Points.Count; } } /// /// The Width of the polygon determined by the right most point minus the left most point. /// public float Width { get { float min = float.MaxValue; float max = float.MinValue; foreach (var p in Points) { min = Util.Min(min, p.X); max = Util.Max(max, p.X); } return Math.Abs(max - min); } } /// /// The Height of the polygon determined by the bottom most point minus the top most point. /// public float Height { get { float min = float.MaxValue; float max = float.MinValue; foreach (var p in Points) { min = Util.Min(min, p.Y); max = Util.Max(max, p.Y); } return Math.Abs(max - min); } } /// /// Half of the Width. /// public float HalfWidth { get { return Width / 2f; } } /// /// Half of the Height. /// public float HalfHeight { get { return Height / 2f; } } public float Left { get { float min = Points[0].X; foreach (var p in Points) { if (p.X > min) continue; min = p.X; } return min; } } public float Right { get { float max = Points[0].X; foreach (var p in Points) { if (p.X < max) continue; max = p.X; } return max; } } public float Top { get { float min = Points[0].Y; foreach (var p in Points) { if (p.Y > min) continue; min = p.Y; } return min; } } public float Bottom { get { float max = Points[0].Y; foreach (var p in Points) { if (p.Y < max) continue; max = p.Y; } return max; } } #endregion Public Properties #region Public Indexers /// /// The list of Vector2 points in the Polygon. /// /// The index of the point. /// The point at the specified index. public Vector2 this[int index] { get { return Points[index]; } set { Points[index] = value; } } #endregion Public Indexers #region Public Methods /// /// Get a list of all the edges of the Polygon as Line2 objects. /// /// A Line2 list of all edges. public List GetEdgesAsLines() { var list = new List(); for (var i = 0; i < Points.Count; i++) { Vector2 p1 = Points[i]; Vector2 p2 = Points[i + 1 == Points.Count ? 0 : i + 1]; // Clever! var line = new Line2(p1, p2); list.Add(line); } return list; } /// /// Convert to a string. /// /// String of data about the Polygon. public override string ToString() { var str = "Polygon "; foreach (var p in Points) { str += string.Format("{0} ", p); } return str; } /// /// Offset all the points by a Vector2 amount. /// /// The offset amount. public void OffsetPoints(Vector2 vector) { for (int i = 0; i < Count; i++) { Points[i] += vector; } } /// /// Rotate the polygon by a specified amount. /// /// The amount in degrees to rotate. /// The X position to rotate around. /// The Y position to rotate around. public void Rotate(float amount, float aroundX, float aroundY) { for (int i = 0; i < Count; i++) { var p = Points[i]; p = Util.RotateAround(p.X, p.Y, aroundX, aroundY, amount); Points[i] = p; } } /// /// Scale the polygon by a specified amount. /// /// The amount to scale horizontally. /// The amount to scale veritcally. /// The X position to scale around. /// The Y position to scale around. public void Scale(float amountX, float amountY, float aroundX, float aroundY) { for (int i = 0; i < Count; i++) { var p = Points[i]; p.X -= aroundX; p.Y -= aroundY; p.X *= amountX; p.Y *= amountY; p.X += aroundX; p.Y += aroundY; Points[i] = p; } } /// /// Clear all points. /// public void Clear() { Points.Clear(); } /// /// Offset all the points by a Vector2 amount. /// /// The offset amount. public void OffsetPoints(float x, float y) { OffsetPoints(new Vector2(x, y)); } /// /// Check to see if a Polygon contains a point. /// /// The point to check for. /// True if the polygon contains the point. public bool ContainsPoint(Vector2 point) { // I have no idea how this works http://stackoverflow.com/questions/11716268/point-in-polygon-algorithm int i, j, nvert = Points.Count; bool c = false; for(i = 0, j = nvert - 1; i < nvert; j = i++) { if(((Points[i].Y) >= point.Y ) != (Points[j].Y >= point.Y) && (point.X <= (Points[j].X - Points[i].X) * (point.Y - Points[i].Y) / (Points[j].Y - Points[i].Y) + Points[i].X)) c = !c; } return c; } /// /// Check to see if a Polygon contains a point. /// /// The X position of the point to check for. /// The Y position of the point to check for. /// True if the polygon contains the point. public bool ContainsPoint(float x, float y) { return ContainsPoint(new Vector2(x, y)); } /// /// Add a Vector2 to the list of points. /// /// The Vector2 to add the points. public void Add(Vector2 point) { Points.Add(point); } /// /// Add an X Y position to the list of points. /// /// The X position to add. /// The Y position to add. public void Add(float x, float y) { Points.Add(new Vector2(x, y)); } /// /// Project the polygon onto an axis. /// /// The axis to project on. /// The min and max values of the projection. public Range Projection(Vector2 axis) { if (Points.Count < 0) return new Range(0); float min = Vector2.Dot(axis, Points[0]); float max = min; for (var i = 0; i < Points.Count; i++) { float p = Vector2.Dot(axis, Points[i]); if (p < min) { min = p; } else if (p > max) { max = p; } } return new Range(min, max); } /// /// Get the axes to project on. /// /// A list of normals from the polygon. public List GetAxes() { var axes = new List(); for (var i = 0; i < Points.Count; i++) { Vector2 p1 = Points[i]; Vector2 p2 = Points[i + 1 == Points.Count ? 0 : i + 1]; // Clever! Vector2 edge = p1 - p2; Vector2 normal = new Vector2(-edge.Y, edge.X); axes.Add(normal); } return axes; } /// /// Test another Polygon for an overlap. Will not work if either Polygon is concave! /// /// The other polygon to check. /// True if this polygon overlaps the other polygon. public bool Overlap(Polygon other) { var axes = GetAxes(); axes.AddRange(other.GetAxes()); int i = 0; foreach (var axis in axes) { var p1 = Projection(axis); var p2 = other.Projection(axis); if (!p1.Overlap(p2)) { return false; } i++; } return true; } #endregion Public Methods } }