using System.Collections; using System.Collections.Generic; using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core; /// /// Represents a shape defined by a collection of vertices. /// /// The vertices of the shape. /// /// Initializes a new instance of a struct with the specified vertices. /// [System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")] public readonly struct Shape2D(List vertices) : IEnumerable { public static readonly Shape2D Triangle = CreateNgon(3, Vector2D.Up); public static readonly Shape2D Square = CreateNgon(4, Vector2D.One); public static readonly Shape2D Pentagon = CreateNgon(5, Vector2D.Up); public static readonly Shape2D Hexagon = CreateNgon(6, Vector2D.Right); private readonly List _verticesList = vertices; /// /// Gets the vertices of the . /// public IReadOnlyList Vertices => _verticesList; /// /// The vertex at the specified index. /// /// The zero-based index of the vertex to get or set. /// The vertex at the specified index. public Vector2D this[System.Index index] => Vertices[index]; /// /// Returns a copy of the current . /// /// The to copy. /// A copy of the input . public static Shape2D CreateCopy(Shape2D shape) => new(new List(shape.Vertices)); /// /// Creates a regular polygon (ngon) with the specified number of vertices. /// /// The number of vertices in the polygon. /// A regular polygon with the specified number of vertices. public static Shape2D CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up); /// /// Creates a regular polygon (ngon) with the specified number of vertices and a rotation position. /// /// The number of vertices in the polygon. /// The position to use for rotation. /// A regular polygon with the specified number of vertices and rotation position. public static Shape2D CreateNgon(int vertexCount, Vector2D positionToRotate) { if (vertexCount < 3) throw new System.ArgumentException($"{nameof(vertexCount)} must have a value of more than 2."); List vertices = new(vertexCount); float radiansPerVertex = 360f / vertexCount * Math.DegreeToRadian; for (int i = 0; i < vertexCount; i++) vertices.Add(positionToRotate.Rotate(i * radiansPerVertex)); return new(vertices); } /// /// Gets the super triangle that encloses the given . /// /// The to enclose. /// The super triangle that encloses the given . public static Triangle GetSuperTriangle(Shape2D shape) { float minX = float.MaxValue, minY = float.MaxValue; float maxX = float.MinValue, maxY = float.MinValue; foreach (Vector2D point in shape.Vertices) { minX = Math.Min(minX, point.X); minY = Math.Min(minY, point.Y); maxX = Math.Max(maxX, point.X); maxY = Math.Max(maxY, point.Y); } float dx = maxX - minX; float dy = maxY - minY; float deltaMax = Math.Max(dx, dy); float midX = (minX + maxX) / 2; float midY = (minY + maxY) / 2; Vector2D p1 = new((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax); Vector2D p2 = new((float)midX, (float)midY + 20 * (float)deltaMax); Vector2D p3 = new((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax); return new Triangle(p1, p2, p3); } /// /// Triangulates the given convex . /// /// The to triangulate. /// The list to populate with triangles. public static void TriangulateConvex(Shape2D shape, IList triangles) { triangles.Clear(); for (int i = 2; i < shape.Vertices.Count; i++) triangles.Add(new Triangle(shape[0], shape[i - 1], shape[i])); } /// /// Triangulates the given convex . /// /// The to triangulate. /// A list of s that makes up the given convex . public static List TriangulateConvex(Shape2D shape) { List triangles = new(shape.Vertices.Count - 2); TriangulateConvex(shape, triangles); return triangles; } /// /// Gets the s that form the edges of the . /// /// The to get s from. /// The list to populate with . public static void GetLines(Shape2D shape, IList lines) { lines.Clear(); for (int i = 0; i < shape.Vertices.Count - 1; i++) lines.Add(new(shape.Vertices[i], shape.Vertices[i + 1])); lines.Add(new(shape.Vertices[^1], shape.Vertices[0])); } /// /// Gets a list of s that form the edges of the . /// /// The shape to get s from. /// A list of s that form the edges of the . public static List GetLines(Shape2D shape) { List lines = new(shape.Vertices.Count - 1); GetLines(shape, lines); return lines; } /// /// Projects the onto a 1D plane. /// /// The shape to project. /// The vector to project onto. /// The list to populate with projected values. public static void Project(Shape2D shape, Vector2D projectionVector, IList list) { list.Clear(); int count = shape.Vertices.Count; for (int i = 0; i < count; i++) list.Add(projectionVector.Dot(shape[i])); } /// /// Projects the onto a vector. /// /// The to project. /// The vector to project onto. /// The projection of the onto the vector. public static Projection1D Project(Shape2D shape, Vector2D projectionVector) { float min = float.MaxValue; float max = float.MinValue; for (int i = 0; i < shape.Vertices.Count; i++) { float projectedLength = projectionVector.Dot(shape.Vertices[i]); min = Math.Min(projectedLength, min); max = Math.Max(projectedLength, max); } return new(min, max); } /// /// Transforms the using the specified . /// /// The to transform. /// The to apply. /// The transformed . public static Shape2D Transform(Shape2D shape, ITransform2D transform) { List vertices = new(shape.Vertices.Count); int count = shape.Vertices.Count; for (int i = 0; i < count; i++) vertices.Add(transform.Transform(shape[i])); return new Shape2D(vertices); } /// /// Transforms the using the specified . /// /// The to transform. /// The to apply. /// The transformed . public static void Transform(Shape2D from, ITransform2D transform, ref Shape2D to) { to._verticesList.Clear(); int count = from._verticesList.Count; for (int i = 0; i < count; i++) to._verticesList.Add(transform.Transform(from[i])); } /// /// Determines whether two s are approximately equal. /// /// The first to compare. /// The second to compare. /// The epsilon range. /// true if the s are approximately equal; otherwise, false. public static bool ApproximatelyEquals(Shape2D left, Shape2D right, float epsilon = float.Epsilon) { if (left.Vertices.Count != right.Vertices.Count) return false; for (int i = 0; i < left.Vertices.Count; i++) if (!left.Vertices[i].ApproximatelyEquals(right.Vertices[i], epsilon)) return false; return true; } /// public IEnumerator GetEnumerator() => Vertices.GetEnumerator(); /// IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator(); } /// /// Provides extension methods for the struct. /// public static class Shape2DExtensions { /// public static Shape2D CreateCopy(this Shape2D shape) => Shape2D.CreateCopy(shape); /// public static Triangle ToSuperTriangle(this Shape2D shape) => Shape2D.GetSuperTriangle(shape); /// public static void ToTrianglesConvex(this Shape2D shape, IList lines) => Shape2D.TriangulateConvex(shape, lines); /// public static List ToTrianglesConvex(this Shape2D shape) => Shape2D.TriangulateConvex(shape); /// public static void ToLines(this Shape2D shape, IList lines) => Shape2D.GetLines(shape, lines); /// public static List ToLines(this Shape2D shape) => Shape2D.GetLines(shape); /// public static void ToProjection(this Shape2D shape, Vector2D projectionVector, IList list) => Shape2D.Project(shape, projectionVector, list); /// public static Projection1D ToProjection(this Shape2D shape, Vector2D projectionVector) => Shape2D.Project(shape, projectionVector); /// public static Shape2D Transform(this ITransform2D transform, Shape2D shape) => Shape2D.Transform(shape, transform); /// public static void Transform(this ITransform2D transform, Shape2D from, ref Shape2D to) => Shape2D.Transform(from, transform, ref to); /// public static Shape2D Transform(this Shape2D shape, ITransform2D transform) => Shape2D.Transform(shape, transform); /// public static void Transform(this Shape2D from, ITransform2D transform, ref Shape2D to) => Shape2D.Transform(from, transform, ref to); /// public static bool ApproximatelyEquals(this Shape2D left, Shape2D right, float epsilon = float.Epsilon) => Shape2D.ApproximatelyEquals(left, right, epsilon); }