diff --git a/Bonk/IMultiShape2D.cs b/Bonk/IMultiShape2D.cs deleted file mode 100644 index 390d03c..0000000 --- a/Bonk/IMultiShape2D.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using MoonTools.Core.Structs; - -namespace MoonTools.Core.Bonk -{ - public interface IMultiShape2D : IHasAABB2D - { - IEnumerable<(IShape2D, Transform2D)> ShapeTransformPairs { get; } - } -} diff --git a/Bonk/MultiShape.cs b/Bonk/MultiShape.cs new file mode 100644 index 0000000..e9af874 --- /dev/null +++ b/Bonk/MultiShape.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using MoonTools.Core.Structs; + +namespace MoonTools.Core.Bonk +{ + public struct MultiShape : IHasAABB2D + { + public ImmutableArray<(IShape2D, Transform2D)> ShapeTransformPairs { get; } + + public AABB AABB { get; } + + public MultiShape(ImmutableArray<(IShape2D, Transform2D)> shapeTransformPairs) + { + ShapeTransformPairs = shapeTransformPairs; + + AABB = AABBFromShapes(shapeTransformPairs); + } + + public AABB TransformedAABB(Transform2D transform) + { + return AABB.Transformed(AABB, transform); + } + + public IEnumerable<(IShape2D, Transform2D)> TransformedShapeTransforms(Transform2D transform) + { + foreach (var (shape, shapeTransform) in ShapeTransformPairs) + { + yield return (shape, transform.Compose(shapeTransform)); + } + } + + private static AABB AABBFromShapes(IEnumerable<(IShape2D, Transform2D)> shapeTransforms) + { + var minX = float.MaxValue; + var minY = float.MaxValue; + var maxX = float.MinValue; + var maxY = float.MinValue; + + foreach (var (shape, transform) in shapeTransforms) + { + var aabb = shape.TransformedAABB(transform); + + if (aabb.Min.X < minX) + { + minX = aabb.Min.X; + } + if (aabb.Min.Y < minY) + { + minY = aabb.Min.Y; + } + if (aabb.Max.X > maxX) + { + maxX = aabb.Max.X; + } + if (aabb.Max.Y > maxY) + { + maxY = aabb.Max.Y; + } + } + + return new AABB(minX, minY, maxX, maxY); + } + } +} diff --git a/Bonk/MultiShapes/MultiRectangle.cs b/Bonk/MultiShapes/MultiRectangle.cs deleted file mode 100644 index 310af84..0000000 --- a/Bonk/MultiShapes/MultiRectangle.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using MoonTools.Core.Structs; - -namespace MoonTools.Core.Bonk -{ - public struct MultiRectangle : IMultiShape2D - { - private ImmutableArray<(Rectangle, Transform2D)> Rectangles { get; } - - public IEnumerable<(IShape2D, Transform2D)> ShapeTransformPairs - { - get - { - foreach (var rectangle in Rectangles) { yield return rectangle; } - } - } - - public AABB AABB { get; } - - public MultiRectangle(ImmutableArray<(Rectangle, Transform2D)> rectangles) - { - Rectangles = rectangles; - - AABB = AABBFromRectangles(rectangles); - } - - public AABB TransformedAABB(Transform2D transform) - { - return AABB.Transformed(AABB, transform); - } - - private static AABB AABBFromRectangles(IEnumerable<(Rectangle, Transform2D)> rectangles) - { - var minX = float.MaxValue; - var minY = float.MaxValue; - var maxX = float.MinValue; - var maxY = float.MinValue; - - foreach (var (rectangle, transform) in rectangles) - { - var transformedAABB = rectangle.TransformedAABB(transform); - - if (transformedAABB.Min.X < minX) - { - minX = transformedAABB.Min.X; - } - if (transformedAABB.Min.Y < minY) - { - minY = transformedAABB.Min.Y; - } - if (transformedAABB.Max.X > maxX) - { - maxX = transformedAABB.Max.X; - } - if (transformedAABB.Max.Y > maxY) - { - maxY = transformedAABB.Max.Y; - } - } - - return new AABB(minX, minY, maxX, maxY); - } - } -} diff --git a/Bonk/NarrowPhase/NarrowPhase.cs b/Bonk/NarrowPhase/NarrowPhase.cs index 7cd731b..676a6e8 100644 --- a/Bonk/NarrowPhase/NarrowPhase.cs +++ b/Bonk/NarrowPhase/NarrowPhase.cs @@ -14,6 +14,30 @@ namespace MoonTools.Core.Bonk /// /// Tests if two shape-transform pairs are overlapping. Automatically detects fast-path optimizations. /// + public static bool TestCollision(IHasAABB2D hasBoundingBoxA, Transform2D transformA, IHasAABB2D hasBoundingBoxB, Transform2D transformB) + { + if (hasBoundingBoxA is MultiShape && hasBoundingBoxB is MultiShape) + { + return TestCollision((MultiShape)hasBoundingBoxA, transformA, (MultiShape)hasBoundingBoxB, transformB); + } + else if (hasBoundingBoxA is MultiShape && hasBoundingBoxB is IShape2D) + { + return TestCollision((MultiShape)hasBoundingBoxA, transformA, (IShape2D)hasBoundingBoxB, transformB); + } + else if (hasBoundingBoxA is IShape2D && hasBoundingBoxB is MultiShape) + { + return TestCollision((IShape2D)hasBoundingBoxA, transformA, (MultiShape)hasBoundingBoxB, transformB); + } + else if (hasBoundingBoxA is IShape2D && hasBoundingBoxB is IShape2D) + { + return TestCollision((IShape2D)hasBoundingBoxA, transformA, (IShape2D)hasBoundingBoxB, transformB); + } + else + { + throw new System.ArgumentException("Collision testing requires MultiShapes or IShape2Ds."); + } + } + public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) { if (shapeA is Rectangle rectangleA && shapeB is Rectangle rectangleB && transformA.Rotation == 0 && transformB.Rotation == 0) @@ -44,7 +68,7 @@ namespace MoonTools.Core.Bonk /// /// /// - public static bool TestCollision(IMultiShape2D multiShape, Transform2D multiShapeTransform, IShape2D shape, Transform2D shapeTransform) + public static bool TestCollision(MultiShape multiShape, Transform2D multiShapeTransform, IShape2D shape, Transform2D shapeTransform) { foreach (var (otherShape, otherTransform) in multiShape.ShapeTransformPairs) { @@ -62,7 +86,7 @@ namespace MoonTools.Core.Bonk /// /// /// - public static bool TestCollision(IShape2D shape, Transform2D shapeTransform, IMultiShape2D multiShape, Transform2D multiShapeTransform) + public static bool TestCollision(IShape2D shape, Transform2D shapeTransform, MultiShape multiShape, Transform2D multiShapeTransform) { foreach (var (otherShape, otherTransform) in multiShape.ShapeTransformPairs) { @@ -80,7 +104,7 @@ namespace MoonTools.Core.Bonk /// /// /// - public static bool TestCollision(IMultiShape2D multiShapeA, Transform2D transformA, IMultiShape2D multiShapeB, Transform2D transformB) + public static bool TestCollision(MultiShape multiShapeA, Transform2D transformA, MultiShape multiShapeB, Transform2D transformB) { foreach (var (shapeA, shapeTransformA) in multiShapeA.ShapeTransformPairs) { diff --git a/Bonk/Shapes/Rectangle.cs b/Bonk/Shapes/Rectangle.cs index 5095d02..d60bf5b 100644 --- a/Bonk/Shapes/Rectangle.cs +++ b/Bonk/Shapes/Rectangle.cs @@ -5,7 +5,7 @@ using MoonTools.Core.Structs; namespace MoonTools.Core.Bonk { /// - /// A rectangle is a shape defined by a minimum and maximum X value and a minimum and maximum Y value. + /// A rectangle is a shape defined by a width and height. The origin is the center of the rectangle. /// public struct Rectangle : IShape2D, IEquatable { diff --git a/Test/NarrowPhaseTest.cs b/Test/NarrowPhaseTest.cs index 1b3755a..3cc1d1d 100644 --- a/Test/NarrowPhaseTest.cs +++ b/Test/NarrowPhaseTest.cs @@ -366,11 +366,11 @@ namespace Tests [Test] public void RotatedRectanglesOverlapping() { - var rectangleA = new Rectangle(3, 3); - var transformA = new Transform2D(new Vector2(-1, 0), -90f); + var rectangleA = new Rectangle(3, 6); + var transformA = new Transform2D(new Vector2(4f, 0), (float)System.Math.PI / 2); var rectangleB = new Rectangle(2, 2); - var transformB = new Transform2D(new Vector2(1, 0)); + var transformB = new Transform2D(new Vector2(0, 0)); NarrowPhase.TestCollision(rectangleA, transformA, rectangleB, transformB).Should().BeTrue(); } @@ -426,8 +426,8 @@ namespace Tests [Test] public void MultiRectanglesOverlapping() { - var multiRectangleA = new MultiRectangle( - ImmutableArray.Create( + var multiRectangleA = new MultiShape( + ImmutableArray.Create<(IShape2D, Transform2D)>( (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 0))), (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 1))), (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 2))) @@ -435,8 +435,8 @@ namespace Tests ); var transformA = new Transform2D(new Position2D(5, 0)); - var multiRectangleB = new MultiRectangle( - ImmutableArray.Create( + var multiRectangleB = new MultiShape( + ImmutableArray.Create<(IShape2D, Transform2D)>( (new Rectangle(4, 1), new Transform2D(new Position2D(4, -1))), (new Rectangle(4, 1), new Transform2D(new Position2D(4, 0))), (new Rectangle(4, 1), new Transform2D(new Position2D(4, 1))) @@ -450,8 +450,8 @@ namespace Tests [Test] public void MultiRectanglesNotOverlapping() { - var multiRectangleA = new MultiRectangle( - ImmutableArray.Create( + var multiRectangleA = new MultiShape( + ImmutableArray.Create<(IShape2D, Transform2D)>( (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 0))), (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 1))), (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 2))) @@ -459,8 +459,8 @@ namespace Tests ); var transformA = new Transform2D(new Position2D(5, 0)); - var multiRectangleB = new MultiRectangle( - ImmutableArray.Create( + var multiRectangleB = new MultiShape( + ImmutableArray.Create<(IShape2D, Transform2D)>( (new Rectangle(4, 1), new Transform2D(new Position2D(4, -1))), (new Rectangle(4, 1), new Transform2D(new Position2D(4, 0))), (new Rectangle(4, 1), new Transform2D(new Position2D(4, 1))) diff --git a/Test/SpatialHashTest.cs b/Test/SpatialHashTest.cs index da75900..671bc8c 100644 --- a/Test/SpatialHashTest.cs +++ b/Test/SpatialHashTest.cs @@ -38,8 +38,8 @@ namespace Tests var point = new Point(); var pointTransform = new Transform2D(new Position2D(8, 8)); - var multiRectangle = new MultiRectangle( - ImmutableArray.Create( + var multiRectangle = new MultiShape( + ImmutableArray.Create<(IShape2D, Transform2D)>( (new Rectangle(4, 1), new Transform2D(new Position2D(-2, -2))), (new Rectangle(4, 1), new Transform2D(new Position2D(-2, -1))), (new Rectangle(4, 1), new Transform2D(new Position2D(-2, 0))) @@ -70,6 +70,7 @@ namespace Tests spatialHash.Retrieve(6, line, lineTransform).Should().Contain((4, circleA, circleATransform)).And.Contain((2, rectC, rectCTransform)); spatialHash.Retrieve(8, multiRectangle, multiRectangleTransform).Should().Contain((1, rectB, rectBTransform)); + spatialHash.Retrieve(8, multiRectangle, multiRectangleTransform).Should().NotContain((0, rectA, rectATransform)); } [Test]