From d23238bcfc88b3044449e92d715af175076620eb Mon Sep 17 00:00:00 2001
From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com>
Date: Sat, 4 Jan 2020 16:13:07 -0800
Subject: [PATCH] multishape system
---
Bonk/IMultiShape2D.cs | 10 -----
Bonk/MultiShape.cs | 65 ++++++++++++++++++++++++++++++
Bonk/MultiShapes/MultiRectangle.cs | 65 ------------------------------
Bonk/NarrowPhase/NarrowPhase.cs | 30 ++++++++++++--
Bonk/Shapes/Rectangle.cs | 2 +-
Test/NarrowPhaseTest.cs | 22 +++++-----
Test/SpatialHashTest.cs | 5 ++-
7 files changed, 107 insertions(+), 92 deletions(-)
delete mode 100644 Bonk/IMultiShape2D.cs
create mode 100644 Bonk/MultiShape.cs
delete mode 100644 Bonk/MultiShapes/MultiRectangle.cs
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]