using System.Collections; using System.Collections.Generic; using Syntriax.Engine.Core; using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Physics2D.Primitives; /// /// Represents a shape defined by a collection of vertices. /// /// The vertices of the shape. /// /// Initializes a new instance of the struct with the specified vertices. /// [System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")] public readonly struct Shape(List vertices) : IEnumerable { public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up); public static readonly Shape Box = CreateNgon(4, Vector2D.One); public static readonly Shape Pentagon = CreateNgon(5, Vector2D.Up); public static readonly Shape Hexagon = CreateNgon(6, Vector2D.Right); private readonly List _verticesList = vertices; /// /// Gets the vertices of the shape. /// 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 shape. /// /// The shape to copy. /// A copy of the input shape. public static Shape CreateCopy(Shape 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 Shape 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 Shape 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 shape. /// /// The shape to enclose. /// The super triangle that encloses the given shape. public static Triangle GetSuperTriangle(Shape 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); } /// /// Gets the lines that form the edges of the shape. /// /// The shape to get lines from. /// The list to populate with lines. public static void GetLines(Shape 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 lines that form the edges of the shape. /// /// The shape to get lines from. /// A list of lines that form the edges of the shape. public static List GetLines(Shape shape) { List lines = new(shape.Vertices.Count - 1); GetLines(shape, lines); return lines; } /// /// Projects the shape onto a vector. /// /// The shape to project. /// The vector to project onto. /// The list to populate with projected values. public static void Project(Shape 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 shape onto a vector. /// /// The shape to project. /// The vector to project onto. /// The projection of the shape onto the vector. public static Projection Project(Shape 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 shape using the specified transform. /// /// The shape to transform. /// The transform to apply. /// The transformed shape. public static Shape TransformShape(Shape shape, ITransform transform) { List vertices = new(shape.Vertices.Count); int count = shape.Vertices.Count; for (int i = 0; i < count; i++) vertices.Add(transform.TransformVector2D(shape[i])); return new Shape(vertices); } /// /// Transforms the shape using the specified transform. /// /// The shape to transform. /// The transform to apply. /// The transformed shape. public static void TransformShape(Shape from, ITransform transform, ref Shape to) { to._verticesList.Clear(); int count = from._verticesList.Count; for (int i = 0; i < count; i++) to._verticesList.Add(transform.TransformVector2D(from[i])); } /// /// Determines whether two shapes are approximately equal. /// /// The first shape to compare. /// The second shape to compare. /// true if the shapes are approximately equal; otherwise, false. public static bool ApproximatelyEquals(Shape left, Shape right) { 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])) return false; return true; } /// public IEnumerator GetEnumerator() => Vertices.GetEnumerator(); /// IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator(); } /// /// Provides extension methods for the struct. /// public static class ShapeExtensions { /// /// Creates a copy of the shape. /// /// The shape to copy. /// A copy of the input shape. public static Shape CreateCopy(this Shape shape) => Shape.CreateCopy(shape); /// /// Gets the super triangle that encloses the shape. /// /// The shape to enclose. /// The super triangle that encloses the shape. public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape); /// /// Gets the lines that form the edges of the shape. /// /// The shape to get lines from. /// The list to populate with lines. public static void ToLines(this Shape shape, IList lines) => Shape.GetLines(shape, lines); /// /// Gets a list of lines that form the edges of the shape. /// /// The shape to get lines from. /// A list of lines that form the edges of the shape. public static List ToLines(this Shape shape) => Shape.GetLines(shape); /// /// Projects the shape onto a vector. /// /// The shape to project. /// The vector to project onto. /// The list to populate with projected values. public static void ToProjection(this Shape shape, Vector2D projectionVector, IList list) => Shape.Project(shape, projectionVector, list); /// /// Projects the shape onto a vector. /// /// The shape to project. /// The vector to project onto. /// The projection of the shape onto the vector. public static Projection ToProjection(this Shape shape, Vector2D projectionVector) => Shape.Project(shape, projectionVector); /// /// Transforms the shape using the specified transform. /// /// The transform to apply. /// The shape to transform. /// The transformed shape. public static Shape TransformShape(this ITransform transform, Shape shape) => Shape.TransformShape(shape, transform); /// /// Transforms the shape using the specified transform. /// /// The transform to apply. /// The shape to transform. /// The transformed shape. public static void TransformShape(this ITransform transform, Shape from, ref Shape to) => Shape.TransformShape(from, transform, ref to); /// /// Determines whether two shapes are approximately equal. /// /// The first shape to compare. /// The second shape to compare. /// true if the shapes are approximately equal; otherwise, false. public static bool ApproximatelyEquals(this Shape left, Shape right) => Shape.ApproximatelyEquals(left, right); }