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