diff --git a/Bonk/AABB.cs b/Bonk/AABB.cs index 5386117..0074d01 100644 --- a/Bonk/AABB.cs +++ b/Bonk/AABB.cs @@ -8,13 +8,13 @@ namespace MoonTools.Core.Bonk { public struct AABB { - public int MinX { get; private set; } - public int MinY { get; private set; } - public int MaxX { get; private set; } - public int MaxY { get; private set; } + public float MinX { get; private set; } + public float MinY { get; private set; } + public float MaxX { get; private set; } + public float MaxY { get; private set; } - public int Width { get { return MaxX - MinX; } } - public int Height { get { return MaxY - MinY; } } + public float Width { get { return MaxX - MinX; } } + public float Height { get { return MaxY - MinY; } } public static AABB FromTransformedVertices(IEnumerable vertices, Transform2D transform) { @@ -22,14 +22,14 @@ namespace MoonTools.Core.Bonk return new AABB { - MinX = (int)Math.Round(TransformedVertices.Min(vertex => vertex.X)), - MinY = (int)Math.Round(TransformedVertices.Min(vertex => vertex.Y)), - MaxX = (int)Math.Round(TransformedVertices.Max(vertex => vertex.X)), - MaxY = (int)Math.Round(TransformedVertices.Max(vertex => vertex.Y)) + MinX = TransformedVertices.Min(vertex => vertex.X), + MinY = TransformedVertices.Min(vertex => vertex.Y), + MaxX = TransformedVertices.Max(vertex => vertex.X), + MaxY = TransformedVertices.Max(vertex => vertex.Y) }; } - public AABB(int minX, int minY, int maxX, int maxY) + public AABB(float minX, float minY, float maxX, float maxY) { MinX = minX; MinY = minY; diff --git a/Bonk/Bonk.csproj b/Bonk/Bonk.csproj index 2110f49..c36cbeb 100644 --- a/Bonk/Bonk.csproj +++ b/Bonk/Bonk.csproj @@ -1,6 +1,6 @@ - 1.0.2 + 1.1.0 netstandard2.0 .NET Core Collision Detection for MonoGame MoonTools.Core.Bonk diff --git a/Bonk/SpatialHash.cs b/Bonk/BroadPhase/SpatialHash.cs similarity index 62% rename from Bonk/SpatialHash.cs rename to Bonk/BroadPhase/SpatialHash.cs index 9dafeb8..cb45db0 100644 --- a/Bonk/SpatialHash.cs +++ b/Bonk/BroadPhase/SpatialHash.cs @@ -8,22 +8,22 @@ namespace MoonTools.Core.Bonk { private readonly int cellSize; - private readonly Dictionary>> hashDictionary = new Dictionary>>(); - private readonly Dictionary<(IShape2D, Transform2D), T> IDLookup = new Dictionary<(IShape2D, Transform2D), T>(); + private readonly Dictionary>> hashDictionary = new Dictionary>>(); + private readonly Dictionary IDLookup = new Dictionary(); public SpatialHash(int cellSize) { this.cellSize = cellSize; } - private (int, int) Hash(int x, int y) + private (int, int) Hash(float x, float y) { - return ((int)Math.Floor((float)x / cellSize), (int)Math.Floor((float)y / cellSize)); + return ((int)Math.Floor(x / cellSize), (int)Math.Floor(y / cellSize)); } - public void Insert(T id, IShape2D shape, Transform2D Transform2D) + public void Insert(T id, IShape2D shape, Transform2D transform2D) { - var box = shape.AABB(Transform2D); + var box = shape.AABB(transform2D); var minHash = Hash(box.MinX, box.MinY); var maxHash = Hash(box.MaxX, box.MaxY); @@ -33,23 +33,23 @@ namespace MoonTools.Core.Bonk { if (!hashDictionary.ContainsKey(i)) { - hashDictionary.Add(i, new Dictionary>()); + hashDictionary.Add(i, new Dictionary>()); } if (!hashDictionary[i].ContainsKey(j)) { - hashDictionary[i].Add(j, new HashSet<(IShape2D, Transform2D)>()); + hashDictionary[i].Add(j, new HashSet()); } - hashDictionary[i][j].Add((shape, Transform2D)); - IDLookup[(shape, Transform2D)] = id; + hashDictionary[i][j].Add(id); + IDLookup[id] = (shape, transform2D); } } } - public IEnumerable<(T, IShape2D, Transform2D)> Retrieve(T id, IShape2D shape, Transform2D Transform2D) + public IEnumerable<(T, IShape2D, Transform2D)> Retrieve(T id, IShape2D shape, Transform2D transform2D) { - var box = shape.AABB(Transform2D); + var box = shape.AABB(transform2D); var minHash = Hash(box.MinX, box.MinY); var maxHash = Hash(box.MaxX, box.MaxY); @@ -59,10 +59,10 @@ namespace MoonTools.Core.Bonk { if (hashDictionary.ContainsKey(i) && hashDictionary[i].ContainsKey(j)) { - foreach (var (otherShape, otherTransform2D) in hashDictionary[i][j]) + foreach (var t in hashDictionary[i][j]) { - var otherID = IDLookup[(otherShape, otherTransform2D)]; - if (!id.Equals(otherID)) { yield return (otherID, otherShape, otherTransform2D); } + var (otherShape, otherTransform) = IDLookup[t]; + if (!id.Equals(t)) { yield return (t, otherShape, otherTransform); } } } } diff --git a/Bonk/EPA2D.cs b/Bonk/NarrowPhase/EPA2D.cs similarity index 100% rename from Bonk/EPA2D.cs rename to Bonk/NarrowPhase/EPA2D.cs diff --git a/Bonk/Edge.cs b/Bonk/NarrowPhase/Edge.cs similarity index 100% rename from Bonk/Edge.cs rename to Bonk/NarrowPhase/Edge.cs diff --git a/Bonk/GJK2D.cs b/Bonk/NarrowPhase/GJK2D.cs similarity index 100% rename from Bonk/GJK2D.cs rename to Bonk/NarrowPhase/GJK2D.cs diff --git a/Bonk/Shapes/Circle.cs b/Bonk/Shapes/Circle.cs index 26d43dd..334e504 100644 --- a/Bonk/Shapes/Circle.cs +++ b/Bonk/Shapes/Circle.cs @@ -18,13 +18,13 @@ namespace MoonTools.Core.Bonk return Vector2.Transform(Vector2.Normalize(direction) * Radius, transform.TransformMatrix); } - public AABB AABB(Transform2D Transform2D) + public AABB AABB(Transform2D transform2D) { return new AABB( - Transform2D.Position.X - Radius, - Transform2D.Position.Y - Radius, - Transform2D.Position.X + Radius, - Transform2D.Position.Y + Radius + transform2D.Position.X - Radius * transform2D.Scale.X, + transform2D.Position.Y - Radius * transform2D.Scale.Y, + transform2D.Position.X + Radius * transform2D.Scale.X, + transform2D.Position.Y + Radius * transform2D.Scale.Y ); } diff --git a/README.md b/README.md new file mode 100644 index 0000000..bc66b8b --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# MoonTools.Core.Bonk +.NET Core Collision Detection for MonoGame + +## Documentation +Detailed docs can be found here: https://moontools-docs.github.io/bonk/ diff --git a/Test/GJK2DTest.cs b/Test/GJK2DTest.cs index b054e15..f6e5ff8 100644 --- a/Test/GJK2DTest.cs +++ b/Test/GJK2DTest.cs @@ -17,6 +17,17 @@ namespace Tests GJK2D.TestCollision(lineA, Transform2D.DefaultTransform, lineB, Transform2D.DefaultTransform).Should().BeTrue(); } + [Test] + public void ScaledLinesOverlapping() + { + var lineA = new Line(new Position2D(-1, -1), new Position2D(1, 1)); + var lineB = new Line(new Position2D(-1, 1), new Position2D(1, -1)); + + var transform = new Transform2D(new Position2D(0, 0), 0f, new Vector2(2, 2)); + + Assert.IsTrue(GJK2D.TestCollision(lineA, transform, lineB, transform).Item1); + } + [Test] public void LineLineNotOverlapping() { @@ -26,6 +37,17 @@ namespace Tests GJK2D.TestCollision(lineA, Transform2D.DefaultTransform, lineB, Transform2D.DefaultTransform).Should().BeFalse(); } + [Test] + public void ScaledLinesNotOverlapping() + { + var lineA = new Line(new Position2D(0, 1), new Position2D(1, 0)); + var lineB = new Line(new Position2D(-1, -1), new Position2D(-2, -2)); + + var transform = new Transform2D(new Position2D(0, 0), 0f, new Vector2(2, 2)); + + Assert.IsFalse(GJK2D.TestCollision(lineA, transform, lineB, transform).Item1); + } + [Test] public void CircleCircleOverlapping() { @@ -37,6 +59,17 @@ namespace Tests Assert.IsTrue(GJK2D.TestCollision(circleA, transformA, circleB, transformB)); } + [Test] + public void ScaledCirclesOverlapping() + { + var circleA = new Circle(2); + var transformA = new Transform2D(new Vector2(-3, 0), 0f, new Vector2(2, 2)); + var circleB = new Circle(2); + var transformB = new Transform2D(new Vector2(3, 0), 0f, new Vector2(2, 2)); + + Assert.IsTrue(GJK2D.TestCollision(circleA, transformA, circleB, transformB).Item1); + } + [Test] public void CircleCircleNotOverlapping() { @@ -48,6 +81,17 @@ namespace Tests Assert.IsFalse(GJK2D.TestCollision(circleA, transformA, circleB, transformB)); } + [Test] + public void ScaledCirclesNotOverlapping() + { + var circleA = new Circle(2); + var transformA = new Transform2D(new Vector2(-5, -5), 0, new Vector2(0.2f, 0.2f)); + var circleB = new Circle(2); + var transformB = new Transform2D(new Vector2(5, 5), 0, new Vector2(0.2f, 0.2f)); + + Assert.IsFalse(GJK2D.TestCollision(circleA, transformA, circleB, transformB).Item1); + } + [Test] public void PolygonPolygonOverlapping() { @@ -68,6 +112,26 @@ namespace Tests Assert.IsTrue(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB)); } + [Test] + public void ScaledPolygonsOverlapping() + { + var shapeA = new Polygon( + new Position2D(-1, 1), new Position2D(1, 1), + new Position2D(-1, -1), new Position2D(1, -1) + ); + + var transformA = Transform2D.DefaultTransform; + + var shapeB = new Polygon( + new Position2D(-1, 1), new Position2D(1, 1), + new Position2D(-1, -1), new Position2D(1, -1) + ); + + var transformB = new Transform2D(new Vector2(3f, 0f), 0f, new Vector2(3f, 3f)); + + Assert.IsTrue(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Item1); + } + [Test] public void PolygonPolygonNotOverlapping() { @@ -88,6 +152,26 @@ namespace Tests Assert.IsFalse(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB)); } + [Test] + public void ScaledPolygonsNotOverlapping() + { + var shapeA = new Polygon( + new Position2D(-1, 1), new Position2D(1, 1), + new Position2D(-1, -1), new Position2D(1, -1) + ); + + var transformA = Transform2D.DefaultTransform; + + var shapeB = new Polygon( + new Position2D(-1, 1), new Position2D(1, 1), + new Position2D(-1, -1), new Position2D(1, -1) + ); + + var transformB = new Transform2D(new Vector2(2f, 0), 0f, new Vector2(0.2f, 0.2f)); + + Assert.IsFalse(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Item1); + } + [Test] public void LinePolygonOverlapping() { diff --git a/Test/SpatialHashTest.cs b/Test/SpatialHashTest.cs index 62ce8a7..6da09e0 100644 --- a/Test/SpatialHashTest.cs +++ b/Test/SpatialHashTest.cs @@ -53,6 +53,27 @@ namespace Tests spatialHash.Retrieve(6, line, lineTransform).Should().Contain((4, circleA, circleATransform)).And.Contain((2, rectC, rectCTransform)); } + [Test] + public void InsertAndRetrieveSameValues() + { + var spatialHash = new SpatialHash(16); + + var rectA = new MoonTools.Core.Bonk.Rectangle(-2, -2, 2, 2); + var rectATransform = new Transform2D(new Vector2(-8, -8)); + + var rectB = new MoonTools.Core.Bonk.Rectangle(-2, -2, 2, 2); + var rectBTransform = new Transform2D(new Vector2(-8, -8)); + + var rectC = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); + var rectCTransform = new Transform2D(new Vector2(-8, -8)); + + spatialHash.Insert(0, rectA, rectATransform); + spatialHash.Insert(1, rectB, rectBTransform); + spatialHash.Insert(2, rectC, rectCTransform); + + spatialHash.Retrieve(2, rectC, rectCTransform).Should().HaveCount(2); + } + [Test] public void Clear() {