forked from MoonsideGames/MoonTools.Bonk
				
			multishape system
							parent
							
								
									b28196605e
								
							
						
					
					
						commit
						d23238bcfc
					
				| 
						 | 
				
			
			@ -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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +14,30 @@ namespace MoonTools.Core.Bonk
 | 
			
		|||
        /// <summary>
 | 
			
		||||
        /// Tests if two shape-transform pairs are overlapping. Automatically detects fast-path optimizations.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        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
 | 
			
		|||
        /// <param name="shape"></param>
 | 
			
		||||
        /// <param name="shapeTransform"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        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
 | 
			
		|||
        /// <param name="shape"></param>
 | 
			
		||||
        /// <param name="shapeTransform"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        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
 | 
			
		|||
        /// <param name="multiShapeB"></param>
 | 
			
		||||
        /// <param name="transformB"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        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)
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ using MoonTools.Core.Structs;
 | 
			
		|||
namespace MoonTools.Core.Bonk
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 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.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public struct Rectangle : IShape2D, IEquatable<Rectangle>
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue