feat: Initial Physics Code From Previous Repo

This commit is contained in:
2024-01-23 18:39:25 +03:00
parent d08495afbb
commit a9485475c7
22 changed files with 1184 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Primitives;
public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary)
{
public bool Overlaps(Vector2D point)
=> point.X >= LowerBoundary.X && point.X <= UpperBoundary.X &&
point.Y >= LowerBoundary.Y && point.Y <= UpperBoundary.Y;
public bool Overlaps(AABB other)
=> LowerBoundary.X <= other.UpperBoundary.X && UpperBoundary.X >= other.LowerBoundary.X &&
LowerBoundary.Y <= other.UpperBoundary.Y && UpperBoundary.Y >= other.LowerBoundary.Y;
public bool ApproximatelyEquals(AABB other)
=> LowerBoundary.ApproximatelyEquals(other.LowerBoundary) && UpperBoundary.ApproximatelyEquals(other.UpperBoundary);
}

View File

@@ -0,0 +1,18 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Primitives;
public record Circle(Vector2D Position, float Radius)
{
public bool Intersects(Circle other)
{
float distanceSquared = (Position - other.Position).LengthSquared();
float radiusSumSquared = Radius * Radius + other.Radius * other.Radius;
return distanceSquared < radiusSumSquared;
}
public bool Overlaps(Vector2D point) => (Position - point).LengthSquared() <= Radius * Radius;
public bool ApproximatelyEquals(Circle other)
=> Position.ApproximatelyEquals(other.Position) && Radius.ApproximatelyEquals(other.Radius);
}

View File

@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Primitives;
public record Line(Vector2D From, Vector2D To)
{
public Line Reversed => new(To, From);
public Vector2D Direction => Vector2D.Normalize(To - From);
public float Length => (From - To).Length();
public float LengthSquared => (From - To).LengthSquared();
public LineEquation LineEquation
{
get
{
Vector2D slopeVector = To - From;
float slope = slopeVector.Y / slopeVector.X;
float yOffset = From.Y - (slope * From.X);
return new LineEquation(slope, yOffset);
}
}
public bool Intersects(Vector2D point)
=> Resolve(point.X).ApproximatelyEquals(point);
public float GetT(Vector2D 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;
float t = pointX / max;
// FIXME
// I don't even know, apparently whatever I wrote up there doesn't take into account of the direction of the line
// Which... I can see how, but I am also not sure how I can make it take into account. Or actually I'm for some reason
// too unmotivated to find a solution. Future me, find a better way if possible, please.
if (!Lerp(t).ApproximatelyEquals(point))
return 1f - t;
return t;
}
public bool Exist(List<Vector2D> vertices)
{
for (int i = 0; i < vertices.Count - 1; i++)
{
Vector2D vertexCurrent = vertices[i];
Vector2D vertexNext = vertices[i];
if (From == vertexCurrent && To == vertexNext) return true;
if (From == vertexNext && To == vertexCurrent) return true;
}
Vector2D vertexFirst = vertices[0];
Vector2D vertexLast = vertices[^1];
if (From == vertexFirst && To == vertexLast) return true;
if (From == vertexLast && To == vertexFirst) return true;
return false;
}
public float IntersectionParameterT(Line other)
{
float numerator = (From.X - other.From.X) * (other.From.Y - other.To.Y) - (From.Y - other.From.Y) * (other.From.X - other.To.X);
float denominator = (From.X - To.X) * (other.From.Y - other.To.Y) - (From.Y - To.Y) * (other.From.X - other.To.X);
// Lines are parallel
if (denominator == 0)
return float.NaN;
return numerator / denominator;
}
public Vector2D Lerp(float t)
=> new Vector2D(
From.X + (To.X - From.X) * t,
From.Y + (To.Y - From.Y) * t
);
public Vector2D Resolve(float x)
=> new Vector2D(x, LineEquation.Resolve(x));
public Vector2D ClosestPointTo(Vector2D point)
{
// Convert edge points to vectors
var edgeVector = new Vector2D(To.X - From.X, To.Y - From.Y);
var pointVector = new Vector2D(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 = MathF.Max(0, MathF.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 Vector2D((float)closestX, (float)closestY);
}
public Vector2D IntersectionPoint(Line other)
=> Vector2D.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 Vector2D? point)
{
point = null;
bool result = Intersects(other);
if (result)
point = IntersectionPoint(other);
return result;
}
public bool ApproximatelyEquals(Line other)
=> From.ApproximatelyEquals(other.From) && To.ApproximatelyEquals(other.To);
}

View File

@@ -0,0 +1,8 @@
namespace Syntriax.Engine.Physics2D.Primitives;
public record LineEquation(float Slope, float OffsetY)
{
public float Resolve(float x) => Slope * x + OffsetY; // y = mx + b
public bool ApproximatelyEquals(LineEquation other)
=> Slope.ApproximatelyEquals(other.Slope) && OffsetY.ApproximatelyEquals(other.OffsetY);
}

View File

@@ -0,0 +1,9 @@
namespace Syntriax.Engine.Physics2D.Primitives;
public static class Math
{
public const float RadianToDegree = 57.29577866666166f;
public const float DegreeToRadian = 0.01745329277777778f;
public static float Clamp(float value, float min, float max) => (value < min) ? min : (value > max) ? max : value;
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Primitives;
public record Shape(IList<Vector2D> Vertices)
{
public Triangle SuperTriangle
{
get
{
float minX = float.MaxValue, minY = float.MaxValue;
float maxX = float.MinValue, maxY = float.MinValue;
foreach (Vector2D point in Vertices)
{
minX = MathF.Min(minX, point.X);
minY = MathF.Min(minY, point.Y);
maxX = MathF.Max(maxX, point.X);
maxY = MathF.Max(maxY, point.Y);
}
float dx = maxX - minX;
float dy = maxY - minY;
float deltaMax = MathF.Max(dx, dy);
float midX = (minX + maxX) / 2;
float midY = (minY + maxY) / 2;
Vector2D p1 = new Vector2D((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax);
Vector2D p2 = new Vector2D((float)midX, (float)midY + 20 * (float)deltaMax);
Vector2D p3 = new Vector2D((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax);
return new Triangle(p1, p2, p3);
}
}
public List<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]));
}
public bool ApproximatelyEquals(Shape other)
{
if (Vertices.Count != other.Vertices.Count)
return false;
for (int i = 0; i < Vertices.Count; i++)
if (!Vertices[i].ApproximatelyEquals(other.Vertices[i]))
return false;
return true;
}
}

View File

@@ -0,0 +1,54 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Primitives;
public record Triangle(Vector2D A, Vector2D B, Vector2D C)
{
public float Area => MathF.Abs((
A.X * (B.Y - C.Y) +
B.X * (C.Y - A.Y) +
C.X * (A.Y - B.Y)
) * .5f);
public Circle CircumCircle
{
get
{
Vector2D midAB = (A + B) / 2;
Vector2D midBC = (B + C) / 2;
float slopeAB = (B.Y - A.Y) / (B.X - A.X);
float slopeBC = (C.Y - B.Y) / (C.X - B.X);
Vector2D center;
if (MathF.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 Vector2D(x, y);
}
else
center = (midAB + midBC) * .5f;
return new(center, Vector2D.Distance(center, A));
}
}
public bool Overlaps(Vector2D 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.ApproximatelyEquals(pointTriangleAreasSum, float.Epsilon * 3f);
}
public bool ApproximatelyEquals(Triangle other)
=> A.ApproximatelyEquals(other.A) && B.ApproximatelyEquals(other.B) && C.ApproximatelyEquals(other.C);
}