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