Refactor
This commit is contained in:
10
Game/Physics2D/Primitives/AABB.cs
Normal file
10
Game/Physics2D/Primitives/AABB.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record AABB(Vector2 LowerBoundary, Vector2 UpperBoundary)
|
||||
{
|
||||
public bool Overlaps(Vector2 point)
|
||||
=> point.X >= LowerBoundary.X && point.X <= UpperBoundary.X &&
|
||||
point.Y >= LowerBoundary.Y && point.Y <= UpperBoundary.Y;
|
||||
}
|
16
Game/Physics2D/Primitives/Circle.cs
Normal file
16
Game/Physics2D/Primitives/Circle.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Circle(Vector2 Position, float Radius)
|
||||
{
|
||||
public bool Intersects(Circle circleOther)
|
||||
{
|
||||
float distanceSquared = (Position - circleOther.Position).LengthSquared();
|
||||
float radiusSumSquared = Radius * Radius + circleOther.Radius * circleOther.Radius;
|
||||
|
||||
return distanceSquared < radiusSumSquared;
|
||||
}
|
||||
|
||||
public bool Overlaps(Vector2 point) => (Position - point).LengthSquared() <= Radius * Radius;
|
||||
}
|
121
Game/Physics2D/Primitives/Line.cs
Normal file
121
Game/Physics2D/Primitives/Line.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Line(Vector2 From, Vector2 To)
|
||||
{
|
||||
public Vector2 Direction => Vector2.Normalize(To - From);
|
||||
public float Length => (From - To).Length();
|
||||
public float LengthSquared => (From - To).LengthSquared();
|
||||
|
||||
public LineEquation LineEquation
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector2 slopeVector = To - From;
|
||||
float slope = slopeVector.Y / slopeVector.X;
|
||||
|
||||
float yOffset = From.Y - (slope * From.X);
|
||||
|
||||
return new LineEquation(slope, yOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Intersects(Vector2 point)
|
||||
=> Resolve(point.X).ApproximatelyEquals(point);
|
||||
|
||||
public float GetT(Vector2 point)
|
||||
{
|
||||
float fromX = MathF.Abs(From.X);
|
||||
float toX = MathF.Abs(To.X);
|
||||
float pointX = MathF.Abs(point.X);
|
||||
|
||||
float min = MathF.Min(fromX, toX);
|
||||
float max = MathF.Max(fromX, toX) - min;
|
||||
|
||||
pointX -= min;
|
||||
|
||||
return pointX / max;
|
||||
}
|
||||
|
||||
public bool Exist(List<Vector2> vertices)
|
||||
{
|
||||
for (int i = 0; i < vertices.Count - 1; i++)
|
||||
{
|
||||
Vector2 vertexCurrent = vertices[i];
|
||||
Vector2 vertexNext = vertices[i];
|
||||
if (From == vertexCurrent && To == vertexNext) return true;
|
||||
if (From == vertexNext && To == vertexCurrent) return true;
|
||||
}
|
||||
|
||||
Vector2 vertexFirst = vertices[0];
|
||||
Vector2 vertexLast = vertices[^1];
|
||||
if (From == vertexFirst && To == vertexLast) return true;
|
||||
if (From == vertexLast && To == vertexFirst) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public float IntersectionParameterT(Line other)
|
||||
=> ((other.From.X - From.X) * (To.Y - From.Y) - (other.From.Y - From.Y) * (To.X - From.X)) /
|
||||
((other.To.Y - other.From.Y) * (To.X - From.X) - (other.To.X - other.From.X) * (To.Y - From.Y));
|
||||
|
||||
|
||||
public Vector2 Resolve(float x)
|
||||
=> new Vector2(x, LineEquation.Resolve(x));
|
||||
|
||||
public Vector2 ClosestPointTo(Vector2 point)
|
||||
{
|
||||
// Convert edge points to vectors
|
||||
var edgeVector = new Vector2(To.X - From.X, To.Y - From.Y);
|
||||
var pointVector = new Vector2(point.X - From.X, point.Y - From.Y);
|
||||
|
||||
// Calculate the projection of pointVector onto edgeVector
|
||||
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
|
||||
|
||||
// Clamp t to the range [0, 1] to ensure the closest point is on the edge
|
||||
t = Math.Max(0, Math.Min(1, t));
|
||||
|
||||
// Calculate the closest point on the edge
|
||||
float closestX = From.X + t * edgeVector.X;
|
||||
float closestY = From.Y + t * edgeVector.Y;
|
||||
|
||||
return new Vector2((float)closestX, (float)closestY);
|
||||
}
|
||||
|
||||
public Vector2 IntersectionPoint(Line other)
|
||||
=> Vector2.Lerp(From, To, IntersectionParameterT(other));
|
||||
|
||||
public bool Intersects(Line other)
|
||||
{
|
||||
int o1 = PhysicsMath.Orientation(From, To, other.From);
|
||||
int o2 = PhysicsMath.Orientation(From, To, other.To);
|
||||
int o3 = PhysicsMath.Orientation(other.From, other.To, From);
|
||||
int o4 = PhysicsMath.Orientation(other.From, other.To, To);
|
||||
|
||||
if (o1 != o2 && o3 != o4)
|
||||
return true;
|
||||
|
||||
if (o1 == 0 && PhysicsMath.OnSegment(From, other.From, To)) return true;
|
||||
if (o2 == 0 && PhysicsMath.OnSegment(From, other.To, To)) return true;
|
||||
if (o3 == 0 && PhysicsMath.OnSegment(other.From, From, other.To)) return true;
|
||||
if (o4 == 0 && PhysicsMath.OnSegment(other.From, To, other.To)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Intersects(Line other, [NotNullWhen(returnValue: true)] out Vector2? point)
|
||||
{
|
||||
point = null;
|
||||
|
||||
bool result = Intersects(other);
|
||||
|
||||
if (result)
|
||||
point = IntersectionPoint(other);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
6
Game/Physics2D/Primitives/LineEquation.cs
Normal file
6
Game/Physics2D/Primitives/LineEquation.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record LineEquation(float Slope, float OffsetY)
|
||||
{
|
||||
public float Resolve(float x) => Slope * x + OffsetY; // y = mx + b
|
||||
}
|
56
Game/Physics2D/Primitives/Shape.cs
Normal file
56
Game/Physics2D/Primitives/Shape.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Shape(IList<Vector2> Vertices)
|
||||
{
|
||||
public Triangle SuperTriangle
|
||||
{
|
||||
get
|
||||
{
|
||||
float minX = float.MaxValue, minY = float.MaxValue;
|
||||
float maxX = float.MinValue, maxY = float.MinValue;
|
||||
|
||||
foreach (Vector2 point in 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;
|
||||
|
||||
Vector2 p1 = new Vector2((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
Vector2 p2 = new Vector2((float)midX, (float)midY + 20 * (float)deltaMax);
|
||||
Vector2 p3 = new Vector2((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
|
||||
return new Triangle(p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
public IList<Line> Lines
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Line> lines = new List<Line>(Vertices.Count - 1);
|
||||
GetLinesNonAlloc(lines);
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetLinesNonAlloc(IList<Line> lines)
|
||||
{
|
||||
lines.Clear();
|
||||
for (int i = 0; i < Vertices.Count - 1; i++)
|
||||
lines.Add(new(Vertices[i], Vertices[i + 1]));
|
||||
lines.Add(new(Vertices[^1], Vertices[0]));
|
||||
}
|
||||
}
|
51
Game/Physics2D/Primitives/Triangle.cs
Normal file
51
Game/Physics2D/Primitives/Triangle.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Triangle(Vector2 A, Vector2 B, Vector2 C)
|
||||
{
|
||||
public float Area => Math.Abs((
|
||||
A.X * (B.Y - C.Y) +
|
||||
B.X * (C.Y - A.Y) +
|
||||
C.X * (A.Y - B.Y)
|
||||
) * .5f);
|
||||
|
||||
public Circle CircumCircle
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector2 midAB = (A + B) / 2;
|
||||
Vector2 midBC = (B + C) / 2;
|
||||
|
||||
float slopeAB = (B.Y - A.Y) / (B.X - A.X);
|
||||
float slopeBC = (C.Y - B.Y) / (C.X - B.X);
|
||||
|
||||
Vector2 center;
|
||||
if (Math.Abs(slopeAB - slopeBC) > float.Epsilon)
|
||||
{
|
||||
float x = (slopeAB * slopeBC * (A.Y - C.Y) + slopeBC * (A.X + B.X) - slopeAB * (B.X + C.X)) / (2 * (slopeBC - slopeAB));
|
||||
float y = -(x - (A.X + B.X) / 2) / slopeAB + (A.Y + B.Y) / 2;
|
||||
center = new Vector2((float)x, (float)y);
|
||||
}
|
||||
else
|
||||
center = (midAB + midBC) * .5f;
|
||||
|
||||
return new(center, Vector2.Distance(center, A));
|
||||
}
|
||||
}
|
||||
|
||||
public bool Overlaps(Vector2 point)
|
||||
{
|
||||
float originalTriangleArea = Area;
|
||||
|
||||
float pointTriangleArea1 = new Triangle(point, B, C).Area;
|
||||
float pointTriangleArea2 = new Triangle(A, point, C).Area;
|
||||
float pointTriangleArea3 = new Triangle(A, B, point).Area;
|
||||
|
||||
float pointTriangleAreasSum = pointTriangleArea1 + pointTriangleArea2 + pointTriangleArea3;
|
||||
|
||||
return originalTriangleArea >= pointTriangleAreasSum;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user