refactor GJK again

generics
Evan Hemsley 2019-10-25 22:00:34 -07:00
parent 2151252d6e
commit f787a00a91
7 changed files with 285 additions and 190 deletions

View File

@ -24,7 +24,7 @@ namespace MoonTools.Core.Bonk
/// </summary>
/// <param name="simplex">A simplex returned by the GJK algorithm.</param>
/// <returns></returns>
public static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, Simplex simplex)
public static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, Simplex2D simplex)
{
var simplexVertices = new PooledList<Vector2>(36, ClearMode.Always);

View File

@ -11,73 +11,135 @@ namespace MoonTools.Core.Bonk
/// </summary>
public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)
{
var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var a = minkowskiDifference.Support(Vector2.UnitX);
var b = minkowskiDifference.Support(-a);
return Vector2.Dot(a, b) > 0 ? false : CheckSimplex(new Simplex(minkowskiDifference, a, b));
}
private static bool CheckSimplex(Simplex simplex)
{
var a = simplex.DirectionA;
var b = simplex.DirectionB;
var axb = a.Cross(b);
var c = simplex.Support((b - a).Perpendicular());
var axc = a.Cross(c);
var bxc = b.Cross(c);
var cxb = -bxc;
return (b - a) == Vector2.Zero || (axb.Y > 0 != bxc.Y > 0 ? CheckSimplex(simplex.WithDirections(b, c)) : (axc.Y > 0 != cxb.Y > 0 ? CheckSimplex(simplex.WithDirections(a, c)) : true));
return FindCollisionSimplex(shapeA, transformA, shapeB, transformB).Item1;
}
/// <summary>
/// Tests if the two shape-transform pairs are overlapping, and returns a simplex that can be used by the EPA algorithm to determine a miminum separating vector.
/// </summary>
public static (bool, Simplex) FindCollisionSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)
public static (bool, Simplex2D) FindCollisionSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)
{
var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var a = minkowskiDifference.Support(Vector2.UnitX);
var b = minkowskiDifference.Support(-a);
return Vector2.Dot(a, b) > 0 ? (false, default(Simplex)) : Simplex(new Simplex(minkowskiDifference, a, b));
var c = minkowskiDifference.Support(Vector2.UnitX);
var b = minkowskiDifference.Support(-Vector2.UnitX);
return Check(minkowskiDifference, c, b);
}
private static (bool, Simplex) Simplex(Simplex simplex)
private static (bool, Simplex2D) Check(MinkowskiDifference minkowskiDifference, Vector2 c, Vector2 b)
{
var a = simplex.DirectionA;
var b = simplex.DirectionB;
var cb = c - b;
var c0 = -c;
var d = Direction(cb, c0);
return DoSimplex(minkowskiDifference, new Simplex2D(b, c), d);
}
if ((b - a) == Vector2.Zero)
private static (bool, Simplex2D) DoSimplex(MinkowskiDifference minkowskiDifference, Simplex2D simplex, Vector2 direction)
{
return (false, simplex.WithDirections(a, b));
var a = minkowskiDifference.Support(direction);
var notPastOrigin = Vector2.Dot(a, direction) < 0;
var (intersects, newSimplex, newDirection) = EnclosesOrigin(a, simplex);
if (notPastOrigin)
{
return (false, default(Simplex2D));
}
else if (intersects)
{
return (true, new Simplex2D(simplex.A, simplex.B.Value, a));
}
else
{
var c = simplex.Support((b - a).Perpendicular());
var axb = a.Cross(b);
var bxc = b.Cross(c);
return DoSimplex(minkowskiDifference, newSimplex, newDirection);
}
}
if (axb.Y > 0 != bxc.Y > 0)
private static (bool, Simplex2D, Vector2) EnclosesOrigin(Vector2 a, Simplex2D simplex)
{
return Simplex(simplex.WithDirections(b, c));
if (simplex.ZeroSimplex)
{
return HandleZeroSimplex(a, simplex.A);
}
else if (simplex.OneSimplex)
{
return HandleOneSimplex(a, simplex.A, simplex.B.Value);
}
else
{
var axc = a.Cross(c);
var cxb = -bxc;
return (false, simplex, Vector2.Zero);
}
}
if (axc.Y > 0 != cxb.Y > 0)
private static (bool, Simplex2D, Vector2) HandleZeroSimplex(Vector2 a, Vector2 b)
{
return Simplex(simplex.WithDirections(a, b));
var ab = b - a;
var a0 = -a;
var (newSimplex, newDirection) = SameDirection(ab, a0) ? (new Simplex2D(a, b), Perpendicular(ab, a0)) : (new Simplex2D(a), a0);
return (false, newSimplex, newDirection);
}
private static (bool, Simplex2D, Vector2) HandleOneSimplex(Vector2 a, Vector2 b, Vector2 c)
{
var a0 = -a;
var ab = b - a;
var ac = c - a;
var abp = Perpendicular(ab, -ac);
var acp = Perpendicular(ac, -ab);
if (SameDirection(abp, a0))
{
if (SameDirection(ab, a0))
{
return (false, new Simplex2D(a, b), abp);
}
else
{
return (true, simplex.WithDirections(a, b));
}
}
}
return (false, new Simplex2D(a), a0);
}
}
else if (SameDirection(acp, a0))
{
if (SameDirection(ac, a0))
{
return (false, new Simplex2D(a, c), acp);
}
else
{
return (false, new Simplex2D(a), a0);
}
}
else
{
return (true, new Simplex2D(b, c), a0);
}
}
private static Vector2 TripleProduct(Vector2 a, Vector2 b, Vector2 c)
{
var A = new Vector3(a.X, a.Y, 0);
var B = new Vector3(b.X, b.Y, 0);
var C = new Vector3(c.X, c.Y, 0);
var first = Vector3.Cross(A, B);
var second = Vector3.Cross(first, C);
return new Vector2(second.X, second.Y);
}
private static Vector2 Direction(Vector2 a, Vector2 b)
{
var d = TripleProduct(a, b, a);
var collinear = d == Vector2.Zero;
return collinear ? new Vector2(a.Y, -a.X) : d;
}
private static bool SameDirection(Vector2 a, Vector2 b)
{
return Vector2.Dot(a, b) > 0;
}
private static Vector2 Perpendicular(Vector2 a, Vector2 b)
{
return TripleProduct(a, b, a);
}
}
}

View File

@ -1,41 +1,59 @@
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using MoonTools.Core.Structs;
using MoonTools.Core.Bonk.Extensions;
using MoreLinq;
namespace MoonTools.Core.Bonk
{
/// <summary>
/// A simplex is a shape used to calculate overlap. It is defined by a Minkowski difference and two direction vectors.
/// A simplex is a shape with up to n - 2 vertices in the nth dimension.
/// </summary>
public struct Simplex : IShape2D
public struct Simplex2D : IShape2D
{
MinkowskiDifference minkowskiDifference;
Vector2 directionA;
Vector2 directionB;
Vector2 a;
Vector2? b;
Vector2? c;
public Vector2 DirectionA { get { return directionA; } }
public Vector2 DirectionB { get { return directionB; } }
public Vector2 A => a;
public Vector2? B => b;
public Vector2? C => c;
public Simplex(MinkowskiDifference minkowskiDifference, Vector2 directionA, Vector2 directionB)
public bool ZeroSimplex { get { return !b.HasValue && !c.HasValue; } }
public bool OneSimplex { get { return b.HasValue && !c.HasValue; } }
public bool TwoSimplex { get { return b.HasValue && c.HasValue; } }
public int Count => TwoSimplex ? 3 : (OneSimplex ? 2 : 1);
public Simplex2D(Vector2 a)
{
this.minkowskiDifference = minkowskiDifference;
this.directionA = directionA;
this.directionB = directionB;
this.a = a;
this.b = null;
this.c = null;
}
public Simplex WithDirections(Vector2 a, Vector2 b)
public Simplex2D(Vector2 a, Vector2 b)
{
return new Simplex(minkowskiDifference, a, b);
this.a = a;
this.b = b;
this.c = null;
}
public Simplex2D(Vector2 a, Vector2 b, Vector2 c)
{
this.a = a;
this.b = b;
this.c = c;
}
public IEnumerable<Position2D> Vertices
{
get
{
yield return (Position2D)Support(directionA);
yield return (Position2D)Support(directionB);
yield return (Position2D)Support(-(directionB - directionA).Perpendicular());
yield return (Position2D)a;
if (b.HasValue) { yield return (Position2D)b; }
if (c.HasValue) { yield return (Position2D)c; }
}
}
@ -46,7 +64,7 @@ namespace MoonTools.Core.Bonk
public Vector2 Support(Vector2 direction)
{
return minkowskiDifference.Support(direction);
return Vertices.MaxBy(vertex => Vector2.Dot(vertex, direction)).First();
}
public Vector2 Support(Vector2 direction, Transform2D transform)
@ -66,11 +84,10 @@ namespace MoonTools.Core.Bonk
public bool Equals(IShape2D other)
{
if (other is Simplex otherSimplex)
if (other is Simplex2D otherSimplex)
{
return minkowskiDifference == otherSimplex.minkowskiDifference &&
((directionA == otherSimplex.directionA && directionB == otherSimplex.directionB) ||
(directionA == otherSimplex.directionB && directionB == otherSimplex.directionA));
if (Count != otherSimplex.Count) { return false; }
return Vertices.Intersect(otherSimplex.Vertices).Count() == Count;
}
return false;
@ -78,22 +95,23 @@ namespace MoonTools.Core.Bonk
public override int GetHashCode()
{
var hashCode = 74270316;
hashCode = hashCode * -1521134295 + EqualityComparer<MinkowskiDifference>.Default.GetHashCode(minkowskiDifference);
hashCode = hashCode * -1521134295 + EqualityComparer<Vector2>.Default.GetHashCode(directionA);
hashCode = hashCode * -1521134295 + EqualityComparer<Vector2>.Default.GetHashCode(directionB);
hashCode = hashCode * -1521134295 + EqualityComparer<Vector2>.Default.GetHashCode(DirectionA);
hashCode = hashCode * -1521134295 + EqualityComparer<Vector2>.Default.GetHashCode(DirectionB);
var hashCode = -495772172;
hashCode = hashCode * -1521134295 + EqualityComparer<Vector2>.Default.GetHashCode(a);
hashCode = hashCode * -1521134295 + EqualityComparer<Vector2?>.Default.GetHashCode(b);
hashCode = hashCode * -1521134295 + EqualityComparer<Vector2?>.Default.GetHashCode(c);
hashCode = hashCode * -1521134295 + ZeroSimplex.GetHashCode();
hashCode = hashCode * -1521134295 + OneSimplex.GetHashCode();
hashCode = hashCode * -1521134295 + TwoSimplex.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<IEnumerable<Position2D>>.Default.GetHashCode(Vertices);
return hashCode;
}
public static bool operator ==(Simplex a, Simplex b)
public static bool operator ==(Simplex2D a, Simplex2D b)
{
return a.Equals(b);
}
public static bool operator !=(Simplex a, Simplex b)
public static bool operator !=(Simplex2D a, Simplex2D b)
{
return !(a == b);
}

View File

@ -4,15 +4,15 @@ namespace MoonTools.Core.Bonk.Extensions
{
internal static class Vector2Extensions
{
internal static Vector2 Cross(this Vector2 a, Vector2 b)
internal static float Cross(this Vector2 a, Vector2 b)
{
var vec3 = Vector3.Cross(new Vector3(a.X, a.Y, 0), new Vector3(b.X, b.Y, 0));
return new Vector2(vec3.X, vec3.Y);
return Vector3.Cross(new Vector3(a.X, a.Y, 0), new Vector3(b.X, b.Y, 0)).Z;
}
internal static Vector2 Perpendicular(this Vector2 v)
internal static Vector2 Perpendicular(this Vector2 a, Vector2 b)
{
return new Vector2(v.Y, -v.X);
var ab = b - a;
return a.Cross(b) > 0 ? Vector2.Normalize(new Vector2(ab.Y, ab.X)) : Vector2.Normalize(new Vector2(ab.Y, -ab.X));
}
}
}

View File

@ -26,6 +26,10 @@ namespace Tests
intersection.X.Should().Be(1f);
intersection.Y.Should().Be(0);
var movedTransform = new Transform2D(transformA.Position - intersection * 1.01f); // move a tiny bit past
GJK2D.TestCollision(squareA, movedTransform, squareB, transformB).Should().BeFalse();
}
[Test]
@ -47,6 +51,10 @@ namespace Tests
intersection.X.Should().BeApproximately(ix, 0.01f);
intersection.Y.Should().BeApproximately(iy, 0.01f);
var movedTransform = new Transform2D(transformA.Position - intersection * 1.01f); // move a tiny bit past
GJK2D.TestCollision(circleA, movedTransform, circleB, transformB).Should().BeFalse();
}
[Test]
@ -63,8 +71,9 @@ namespace Tests
var intersection = EPA2D.Intersect(line, transformA, square, transformB, simplex);
intersection.X.Should().Be(-1);
intersection.Y.Should().Be(1);
var movedTransform = new Transform2D(transformA.Position - intersection * 1.01f); // move a tiny bit past
GJK2D.TestCollision(line, movedTransform, square, transformB).Should().BeFalse();
}
}
}

View File

@ -258,169 +258,163 @@ namespace Tests
public class SimplexTests
{
[Test]
public void SimplexEquals()
public void ZeroSimplexEquals()
{
var shapeA = new Circle(3);
var transformA = new Transform2D(new Position2D(1, 2));
var shapeB = new Circle(2);
var transformB = new Transform2D(new Position2D(4, 5));
var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var directionA = Vector2.UnitX;
var directionB = Vector2.UnitY;
var simplexA = new Simplex(minkowskiDifference, directionA, directionB);
var simplexB = new Simplex(minkowskiDifference, directionA, directionB);
var simplexA = new Simplex2D(Vector2.One);
var simplexB = new Simplex2D(Vector2.One);
simplexA.Equals(simplexB).Should().BeTrue();
}
[Test]
public void SimplexEqualsOperator()
public void ZeroSimplexEqualsOperator()
{
var shapeA = new Circle(3);
var transformA = new Transform2D(new Position2D(1, 2));
var shapeB = new Circle(2);
var transformB = new Transform2D(new Position2D(4, 5));
var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var directionA = Vector2.UnitX;
var directionB = Vector2.UnitY;
var simplexA = new Simplex(minkowskiDifference, directionA, directionB);
var simplexB = new Simplex(minkowskiDifference, directionA, directionB);
var simplexA = new Simplex2D(Vector2.One);
var simplexB = new Simplex2D(Vector2.One);
(simplexA == simplexB).Should().BeTrue();
}
[Test]
public void SimplexDirectionOutOfOrderEqual()
public void ZeroSimplexNotEquals()
{
var shapeA = new Circle(3);
var transformA = new Transform2D(new Position2D(1, 2));
var simplexA = new Simplex2D(Vector2.Zero);
var simplexB = new Simplex2D(Vector2.One);
var shapeB = new Circle(2);
var transformB = new Transform2D(new Position2D(4, 5));
simplexA.Equals(simplexB).Should().BeFalse();
var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var simplexC = new Simplex2D(Vector2.Zero, Vector2.One);
var directionA = Vector2.UnitX;
var directionB = Vector2.UnitY;
simplexA.Equals(simplexC).Should().BeFalse();
}
var simplexA = new Simplex(minkowskiDifference, directionA, directionB);
var simplexB = new Simplex(minkowskiDifference, directionB, directionA);
[Test]
public void ZeroSimplexNotEqualsOperator()
{
var simplexA = new Simplex2D(Vector2.Zero);
var simplexB = new Simplex2D(Vector2.One);
(simplexA != simplexB).Should().BeTrue();
}
[Test]
public void OneSimplexEquals()
{
var simplexA = new Simplex2D(Vector2.One, Vector2.Zero);
var simplexB = new Simplex2D(Vector2.One, Vector2.Zero);
simplexA.Equals(simplexB).Should().BeTrue();
var simplexC = new Simplex2D(Vector2.One, Vector2.Zero);
var simplexD = new Simplex2D(Vector2.Zero, Vector2.One);
simplexC.Equals(simplexD).Should().BeTrue();
}
[Test]
public void SimplexDirectionOutOfOrderEqualOperator()
public void OneSimplexEqualsOperator()
{
var shapeA = new Circle(3);
var transformA = new Transform2D(new Position2D(1, 2));
var shapeB = new Circle(2);
var transformB = new Transform2D(new Position2D(4, 5));
var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var directionA = Vector2.UnitX;
var directionB = Vector2.UnitY;
var simplexA = new Simplex(minkowskiDifference, directionA, directionB);
var simplexB = new Simplex(minkowskiDifference, directionB, directionA);
var simplexA = new Simplex2D(Vector2.One, Vector2.Zero);
var simplexB = new Simplex2D(Vector2.One, Vector2.Zero);
(simplexA == simplexB).Should().BeTrue();
var simplexC = new Simplex2D(Vector2.One, Vector2.Zero);
var simplexD = new Simplex2D(Vector2.Zero, Vector2.One);
(simplexC == simplexD).Should().BeTrue();
}
[Test]
public void SimplexMinkowskiNotEqual()
public void OneSimplexNotEquals()
{
var shapeA = new Circle(3);
var transformA = new Transform2D(new Position2D(1, 2));
var shapeB = new Circle(2);
var transformB = new Transform2D(new Position2D(4, 5));
var minkowskiDifferenceA = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var minkowskiDifferenceB = new MinkowskiDifference(shapeB, transformB, shapeA, transformA);
var directionA = Vector2.UnitX;
var directionB = Vector2.UnitY;
var simplexA = new Simplex(minkowskiDifferenceA, directionA, directionB);
var simplexB = new Simplex(minkowskiDifferenceB, directionA, directionB);
var simplexA = new Simplex2D(Vector2.One, Vector2.Zero);
var simplexB = new Simplex2D(Vector2.One, Vector2.UnitX);
simplexA.Equals(simplexB).Should().BeFalse();
var simplexC = new Simplex2D(Vector2.One, Vector2.Zero);
var simplexD = new Simplex2D(Vector2.Zero, Vector2.UnitX);
simplexC.Equals(simplexD).Should().BeFalse();
var simplexE = new Simplex2D(Vector2.Zero);
simplexA.Equals(simplexE).Should().BeFalse();
}
[Test]
public void SimplexMinkowskiNotEqualOperator()
public void OneSimplexNotEqualsOperator()
{
var shapeA = new Circle(3);
var transformA = new Transform2D(new Position2D(1, 2));
var simplexA = new Simplex2D(Vector2.One, Vector2.Zero);
var simplexB = new Simplex2D(Vector2.One, Vector2.UnitX);
var shapeB = new Circle(2);
var transformB = new Transform2D(new Position2D(4, 5));
(simplexA == simplexB).Should().BeFalse();
var minkowskiDifferenceA = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var minkowskiDifferenceB = new MinkowskiDifference(shapeB, transformB, shapeA, transformA);
var simplexC = new Simplex2D(Vector2.One, Vector2.Zero);
var simplexD = new Simplex2D(Vector2.Zero, Vector2.UnitX);
var directionA = Vector2.UnitX;
var directionB = Vector2.UnitY;
var simplexA = new Simplex(minkowskiDifferenceA, directionA, directionB);
var simplexB = new Simplex(minkowskiDifferenceB, directionA, directionB);
(simplexA != simplexB).Should().BeTrue();
(simplexC == simplexD).Should().BeFalse();
}
[Test]
public void SimplexDirectionsNotEqual()
public void TwoSimplexEquals()
{
var shapeA = new Circle(3);
var transformA = new Transform2D(new Position2D(1, 2));
var simplexA = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
var simplexB = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
var shapeB = new Circle(2);
var transformB = new Transform2D(new Position2D(4, 5));
simplexA.Equals(simplexB).Should().BeTrue();
var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var simplexC = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
var simplexD = new Simplex2D(Vector2.Zero, Vector2.One, Vector2.UnitX);
var directionA = Vector2.UnitX;
var directionB = Vector2.UnitY;
var directionC = -Vector2.UnitX;
var directionD = -Vector2.UnitY;
simplexC.Equals(simplexD).Should().BeTrue();
}
var simplexA = new Simplex(minkowskiDifference, directionA, directionB);
var simplexB = new Simplex(minkowskiDifference, directionC, directionD);
[Test]
public void TwoSimplexEqualsOperator()
{
var simplexA = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
var simplexB = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
(simplexA == simplexB).Should().BeTrue();
var simplexC = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
var simplexD = new Simplex2D(Vector2.Zero, Vector2.One, Vector2.UnitX);
(simplexC == simplexD).Should().BeTrue();
}
[Test]
public void TwoSimplexNotEquals()
{
var simplexA = new Simplex2D(Vector2.One, Vector2.UnitY, Vector2.UnitX);
var simplexB = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
simplexA.Equals(simplexB).Should().BeFalse();
var simplexC = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
var simplexD = new Simplex2D(Vector2.Zero, Vector2.UnitY, Vector2.UnitX);
simplexC.Equals(simplexD).Should().BeFalse();
var simplexE = new Simplex2D(Vector2.Zero);
simplexA.Equals(simplexE).Should().BeFalse();
}
[Test]
public void SimplexDirectionsNotEqualOperator()
public void TwoSimplexNotEqualsOperator()
{
var shapeA = new Circle(3);
var transformA = new Transform2D(new Position2D(1, 2));
var simplexA = new Simplex2D(Vector2.One, Vector2.UnitY, Vector2.UnitX);
var simplexB = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
var shapeB = new Circle(2);
var transformB = new Transform2D(new Position2D(4, 5));
(simplexA == simplexB).Should().BeFalse();
var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
var simplexC = new Simplex2D(Vector2.One, Vector2.Zero, Vector2.UnitX);
var simplexD = new Simplex2D(Vector2.Zero, Vector2.UnitY, Vector2.UnitX);
var directionA = Vector2.UnitX;
var directionB = Vector2.UnitY;
var directionC = -Vector2.UnitX;
var directionD = -Vector2.UnitY;
var simplexA = new Simplex(minkowskiDifference, directionA, directionB);
var simplexB = new Simplex(minkowskiDifference, directionC, directionD);
(simplexA != simplexB).Should().BeTrue();
(simplexC == simplexD).Should().BeFalse();
}
}
}

View File

@ -259,6 +259,18 @@ namespace Tests
GJK2D.TestCollision(circle, circleTransform, square, squareTransform).Should().BeFalse();
}
[Test]
public void RectanglesNotOverlapping()
{
var rectangleA = new MoonTools.Core.Bonk.Rectangle(-6, -6, 6, 6);
var transformA = new Transform2D(new Position2D(39, 249));
var rectangleB = new MoonTools.Core.Bonk.Rectangle(0, 0, 16, 16);
var transformB = new Transform2D(new Position2D(16, 240));
GJK2D.TestCollision(rectangleA, transformA, rectangleB, transformB).Should().BeFalse();
}
[Test]
public void RotatedRectanglesOverlapping()
{