From 354912d6746b0b95d54754939dd8e5b300520021 Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sun, 5 Jan 2020 13:10:52 -0800 Subject: [PATCH] fixing sweep test --- Bonk/BroadPhase/SpatialHash.cs | 33 +++++++++- Bonk/MultiShape.cs | 10 +++ Bonk/SweepTest/SweepTest.cs | 115 +++++++++++++++++++-------------- Test/SweepTestTest.cs | 8 +++ 4 files changed, 118 insertions(+), 48 deletions(-) diff --git a/Bonk/BroadPhase/SpatialHash.cs b/Bonk/BroadPhase/SpatialHash.cs index 8f54856..cdd96bd 100644 --- a/Bonk/BroadPhase/SpatialHash.cs +++ b/Bonk/BroadPhase/SpatialHash.cs @@ -73,7 +73,38 @@ namespace MoonTools.Core.Bonk foreach (var t in hashDictionary[key]) { var (otherShape, otherTransform) = IDLookup[t]; - if (!id.Equals(t) && AABB.TestOverlap(shape.TransformedAABB(transform2D), otherShape.TransformedAABB(otherTransform))) + if (!id.Equals(t) && AABB.TestOverlap(box, otherShape.TransformedAABB(otherTransform))) + { + yield return (t, otherShape, otherTransform); + } + } + } + } + } + } + + + /// + /// Retrieves objects based on a pre-transformed AABB. + /// + /// A transformed AABB. + /// + public IEnumerable<(T, IHasAABB2D, Transform2D)> Retrieve(AABB aabb) + { + var minHash = Hash(aabb.Min); + var maxHash = Hash(aabb.Max); + + for (var i = minHash.Item1; i <= maxHash.Item1; i++) + { + for (var j = minHash.Item2; j <= maxHash.Item2; j++) + { + var key = MakeLong(i, j); + if (hashDictionary.ContainsKey(key)) + { + foreach (var t in hashDictionary[key]) + { + var (otherShape, otherTransform) = IDLookup[t]; + if (AABB.TestOverlap(aabb, otherShape.TransformedAABB(otherTransform))) { yield return (t, otherShape, otherTransform); } diff --git a/Bonk/MultiShape.cs b/Bonk/MultiShape.cs index e9af874..eb34cda 100644 --- a/Bonk/MultiShape.cs +++ b/Bonk/MultiShape.cs @@ -30,6 +30,16 @@ namespace MoonTools.Core.Bonk } } + public bool IsSingleShape() where T : struct, IShape2D + { + return ShapeTransformPairs.Length == 1 && ShapeTransformPairs[0].Item1 is T; + } + + public (T, Transform2D) ShapeTransformPair() where T : struct, IShape2D + { + return ((T, Transform2D))ShapeTransformPairs[0]; + } + private static AABB AABBFromShapes(IEnumerable<(IShape2D, Transform2D)> shapeTransforms) { var minX = float.MaxValue; diff --git a/Bonk/SweepTest/SweepTest.cs b/Bonk/SweepTest/SweepTest.cs index 5001188..65ea470 100644 --- a/Bonk/SweepTest/SweepTest.cs +++ b/Bonk/SweepTest/SweepTest.cs @@ -7,7 +7,7 @@ namespace MoonTools.Core.Bonk public static class SweepTest { /// - /// Performs a sweep test on rectangles. + /// Performs a sweep test on rectangles. Returns the position 1 pixel before overlap occurs. /// /// /// A spatial hash. @@ -27,67 +27,88 @@ namespace MoonTools.Core.Bonk foreach (var (id, shape, shapeTransform) in spatialHash.Retrieve(sweepBox)) { - if (shape is Rectangle otherRectangle) + Rectangle otherRectangle; + Transform2D otherTransform; + AABB otherTransformedAABB; + if (shape is Rectangle) { - var otherTransformedAABB = otherRectangle.TransformedAABB(shapeTransform); - float xInvEntry, yInvEntry; + otherRectangle = (Rectangle)shape; + otherTransformedAABB = shape.TransformedAABB(shapeTransform); + otherTransform = shapeTransform; + } + else if (shape is MultiShape multiShape && multiShape.IsSingleShape()) + { + Transform2D rectangleOffset; + (otherRectangle, rectangleOffset) = multiShape.ShapeTransformPair(); + otherTransform = shapeTransform.Compose(rectangleOffset); + otherTransformedAABB = shape.TransformedAABB(otherTransform); + } + else + { + continue; + } - if (ray.X > 0) - { - xInvEntry = shapeTransform.Position.X - (transform.Position.X + transformedAABB.Width); - } - else - { - xInvEntry = (shapeTransform.Position.X + otherTransformedAABB.Width) - transform.Position.X; - } + float xInvEntry, yInvEntry; - if (ray.Y > 0) - { - yInvEntry = shapeTransform.Position.Y - (transform.Position.Y + transformedAABB.Height); - } - else - { - yInvEntry = (shapeTransform.Position.Y + otherTransformedAABB.Height) - shapeTransform.Position.Y; - } + if (ray.X > 0) + { + xInvEntry = otherTransformedAABB.Left - (transformedAABB.Right); + } + else + { + xInvEntry = (otherTransformedAABB.Right) - transformedAABB.Left; + } - float xEntry, yEntry; + if (ray.Y > 0) + { + yInvEntry = otherTransformedAABB.Top - (transformedAABB.Bottom); + } + else + { + yInvEntry = (otherTransformedAABB.Bottom) - transformedAABB.Top; + } - if (ray.X == 0) - { - xEntry = float.MinValue; - } - else - { - xEntry = xInvEntry / ray.X; - } + float xEntry, yEntry; - if (ray.Y == 0) - { - yEntry = float.MinValue; - } - else - { - yEntry = yInvEntry / ray.Y; - } + if (ray.X == 0) + { + xEntry = float.MinValue; + } + else + { + xEntry = xInvEntry / ray.X; + } - var entryTime = Math.Max(xEntry, yEntry); + if (ray.Y == 0) + { + yEntry = float.MinValue; + } + else + { + yEntry = yInvEntry / ray.Y; + } - if (entryTime > 0 && entryTime < 1) + var entryTime = Math.Max(xEntry, yEntry); + + if (entryTime >= 0 && entryTime <= 1) + { + if (entryTime < shortestDistance) { - if (entryTime < shortestDistance) - { - shortestDistance = entryTime; - nearestID = id; - nearestRectangle = rectangle; - nearestTransform = shapeTransform; - } + shortestDistance = entryTime; + nearestID = id; + nearestRectangle = otherRectangle; + nearestTransform = shapeTransform; } } + } if (nearestRectangle.HasValue) { - return new SweepResult(true, ray * shortestDistance, nearestID, nearestRectangle.Value, nearestTransform.Value); + var overlapPosition = ray * shortestDistance; + var correctionX = ray.X > 0 ? -1 : 1; + var correctionY = ray.Y > 0 ? -1 : 1; + return new SweepResult(true, new Position2D((int)overlapPosition.X + correctionX, (int)overlapPosition.Y + correctionY), nearestID, nearestRectangle.Value, nearestTransform.Value); } else { diff --git a/Test/SweepTestTest.cs b/Test/SweepTestTest.cs index cd7b1d4..cf07de6 100644 --- a/Test/SweepTestTest.cs +++ b/Test/SweepTestTest.cs @@ -20,15 +20,23 @@ namespace Tests var farthestRectangle = new Rectangle(4, 4); var farthestTransform = new Transform2D(new Position2D(12, 0)); + var downRectangle = new Rectangle(12, 4); + var downTransform = new Transform2D(new Position2D(-6, 20)); + var spatialHash = new SpatialHash(16); spatialHash.Insert(1, otherRectangle, otherTransform); spatialHash.Insert(2, farthestRectangle, farthestTransform); + spatialHash.Insert(3, downRectangle, downTransform); SweepTest.Rectangle(spatialHash, rectangle, transform, new Vector2(12, 0)).Should().Be( new SweepResult(true, new Vector2(8, 0), 1, otherRectangle, otherTransform) ); SweepTest.Rectangle(spatialHash, rectangle, transform, new Vector2(-12, 0)).Hit.Should().BeFalse(); + + SweepTest.Rectangle(spatialHash, rectangle, transform, new Vector2(0, 20)).Should().Be( + new SweepResult(true, new Vector2(0, 16), 3, downRectangle, downTransform) + ); } } }