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> |         /// <summary> | ||||||
|         /// Tests if two shape-transform pairs are overlapping. Automatically detects fast-path optimizations. |         /// Tests if two shape-transform pairs are overlapping. Automatically detects fast-path optimizations. | ||||||
|         /// </summary> |         /// </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) |         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) |             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="shape"></param> | ||||||
|         /// <param name="shapeTransform"></param> |         /// <param name="shapeTransform"></param> | ||||||
|         /// <returns></returns> |         /// <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) |             foreach (var (otherShape, otherTransform) in multiShape.ShapeTransformPairs) | ||||||
|             { |             { | ||||||
|  | @ -62,7 +86,7 @@ namespace MoonTools.Core.Bonk | ||||||
|         /// <param name="shape"></param> |         /// <param name="shape"></param> | ||||||
|         /// <param name="shapeTransform"></param> |         /// <param name="shapeTransform"></param> | ||||||
|         /// <returns></returns> |         /// <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) |             foreach (var (otherShape, otherTransform) in multiShape.ShapeTransformPairs) | ||||||
|             { |             { | ||||||
|  | @ -80,7 +104,7 @@ namespace MoonTools.Core.Bonk | ||||||
|         /// <param name="multiShapeB"></param> |         /// <param name="multiShapeB"></param> | ||||||
|         /// <param name="transformB"></param> |         /// <param name="transformB"></param> | ||||||
|         /// <returns></returns> |         /// <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) |             foreach (var (shapeA, shapeTransformA) in multiShapeA.ShapeTransformPairs) | ||||||
|             { |             { | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ using MoonTools.Core.Structs; | ||||||
| namespace MoonTools.Core.Bonk | namespace MoonTools.Core.Bonk | ||||||
| { | { | ||||||
|     /// <summary> |     /// <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> |     /// </summary> | ||||||
|     public struct Rectangle : IShape2D, IEquatable<Rectangle> |     public struct Rectangle : IShape2D, IEquatable<Rectangle> | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -366,11 +366,11 @@ namespace Tests | ||||||
|         [Test] |         [Test] | ||||||
|         public void RotatedRectanglesOverlapping() |         public void RotatedRectanglesOverlapping() | ||||||
|         { |         { | ||||||
|             var rectangleA = new Rectangle(3, 3); |             var rectangleA = new Rectangle(3, 6); | ||||||
|             var transformA = new Transform2D(new Vector2(-1, 0), -90f); |             var transformA = new Transform2D(new Vector2(4f, 0), (float)System.Math.PI / 2); | ||||||
| 
 | 
 | ||||||
|             var rectangleB = new Rectangle(2, 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(); |             NarrowPhase.TestCollision(rectangleA, transformA, rectangleB, transformB).Should().BeTrue(); | ||||||
|         } |         } | ||||||
|  | @ -426,8 +426,8 @@ namespace Tests | ||||||
|         [Test] |         [Test] | ||||||
|         public void MultiRectanglesOverlapping() |         public void MultiRectanglesOverlapping() | ||||||
|         { |         { | ||||||
|             var multiRectangleA = new MultiRectangle( |             var multiRectangleA = new MultiShape( | ||||||
|                 ImmutableArray.Create( |                 ImmutableArray.Create<(IShape2D, Transform2D)>( | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 0))), |                     (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, 1))), | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 2))) |                     (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 transformA = new Transform2D(new Position2D(5, 0)); | ||||||
| 
 | 
 | ||||||
|             var multiRectangleB = new MultiRectangle( |             var multiRectangleB = new MultiShape( | ||||||
|                 ImmutableArray.Create( |                 ImmutableArray.Create<(IShape2D, Transform2D)>( | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(4, -1))), |                     (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, 0))), | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(4, 1))) |                     (new Rectangle(4, 1), new Transform2D(new Position2D(4, 1))) | ||||||
|  | @ -450,8 +450,8 @@ namespace Tests | ||||||
|         [Test] |         [Test] | ||||||
|         public void MultiRectanglesNotOverlapping() |         public void MultiRectanglesNotOverlapping() | ||||||
|         { |         { | ||||||
|             var multiRectangleA = new MultiRectangle( |             var multiRectangleA = new MultiShape( | ||||||
|                 ImmutableArray.Create( |                 ImmutableArray.Create<(IShape2D, Transform2D)>( | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 0))), |                     (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, 1))), | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(-5, 2))) |                     (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 transformA = new Transform2D(new Position2D(5, 0)); | ||||||
| 
 | 
 | ||||||
|             var multiRectangleB = new MultiRectangle( |             var multiRectangleB = new MultiShape( | ||||||
|                 ImmutableArray.Create( |                 ImmutableArray.Create<(IShape2D, Transform2D)>( | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(4, -1))), |                     (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, 0))), | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(4, 1))) |                     (new Rectangle(4, 1), new Transform2D(new Position2D(4, 1))) | ||||||
|  |  | ||||||
|  | @ -38,8 +38,8 @@ namespace Tests | ||||||
|             var point = new Point(); |             var point = new Point(); | ||||||
|             var pointTransform = new Transform2D(new Position2D(8, 8)); |             var pointTransform = new Transform2D(new Position2D(8, 8)); | ||||||
| 
 | 
 | ||||||
|             var multiRectangle = new MultiRectangle( |             var multiRectangle = new MultiShape( | ||||||
|                 ImmutableArray.Create( |                 ImmutableArray.Create<(IShape2D, Transform2D)>( | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(-2, -2))), |                     (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, -1))), | ||||||
|                     (new Rectangle(4, 1), new Transform2D(new Position2D(-2, 0))) |                     (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(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().Contain((1, rectB, rectBTransform)); | ||||||
|  |             spatialHash.Retrieve(8, multiRectangle, multiRectangleTransform).Should().NotContain((0, rectA, rectATransform)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         [Test] |         [Test] | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue