using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Syntriax.Engine.Physics2D.Primitives; namespace Syntriax.Engine.Physics2D; public static class PhysicsMath { public static Vector2 Scale(this Vector2 original, Vector2 scale) => new Vector2(original.X * scale.X, original.Y * scale.Y); public static Triangle ToSuperTriangle(this IList vertices) { float minX = float.MaxValue, minY = float.MaxValue; float maxX = float.MinValue, maxY = float.MinValue; foreach (Vector2 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; 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 static IList ToLines(this IList vertices) { List lines = new List(vertices.Count - 1); ToLines(vertices, lines); return lines; } public static void ToLines(this IList vertices, IList 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 static bool LaysOn(this Vector2 point, Line line) => line.Resolve(point.X).ApproximatelyEquals(point); // Given three collinear points p, q, r, the function checks if // point q lies on line segment 'pr' public static bool OnSegment(Vector2 p, Vector2 q, Vector2 r) { if (q.X <= MathF.Max(p.X, r.X) && q.X >= MathF.Min(p.X, r.X) && q.Y <= MathF.Max(p.Y, r.Y) && q.Y >= MathF.Min(p.Y, r.Y)) return true; return false; } // To find orientation of ordered triplet (p, q, r). // The function returns following values // 0 --> p, q and r are collinear // 1 --> Clockwise // 2 --> Counterclockwise public static int Orientation(Vector2 p, Vector2 q, Vector2 r) { // See https://www.geeksforgeeks.org/orientation-3-ordered-points/ // for details of below formula. float val = (q.Y - p.Y) * (r.X - q.X) - (q.X - p.X) * (r.Y - q.Y); if (val == 0) return 0; // collinear return (val > 0) ? 1 : 2; // clock or counterclock wise } public static float IntersectionParameterT(Vector2 p0, Vector2 p1, Vector2 q0, Vector2 q1) => ((q0.X - p0.X) * (p1.Y - p0.Y) - (q0.Y - p0.Y) * (p1.X - p0.X)) / ((q1.Y - q0.Y) * (p1.X - p0.X) - (q1.X - q0.X) * (p1.Y - p0.Y)); public static bool ApproximatelyEquals(this float a, float b) => ApproximatelyEquals(a, b, float.Epsilon); public static bool ApproximatelyEquals(this Vector2 a, Vector2 b) => ApproximatelyEquals(a, b, float.Epsilon); public static bool ApproximatelyEquals(this Vector2 a, Vector2 b, float epsilon) => ApproximatelyEquals(a.X, b.X, epsilon) && ApproximatelyEquals(a.Y, b.Y, epsilon); public static bool ApproximatelyEquals(this float a, float b, float epsilon) { if (a == b) return true; const float floatNormal = (1 << 23) * float.Epsilon; float absA = MathF.Abs(a); float absB = MathF.Abs(b); float diff = MathF.Abs(a - b); if (a == 0.0f || b == 0.0f || diff < floatNormal) return diff < (epsilon * floatNormal); return diff / MathF.Min(absA + absB, float.MaxValue) < epsilon; } }