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
}
}