From bf1159d9f3979193be185c16856768678585be97 Mon Sep 17 00:00:00 2001 From: Andreas Torebring Date: Fri, 8 Mar 2024 18:16:03 +0100 Subject: [PATCH] Properly intersect circle and oriented rect --- .../CollisionComponent.cs | 101 ++++++++++++------ .../Math/OrientedRectangle.cs | 5 +- src/cs/MonoGame.Extended/Math/ShapeF.cs | 58 +++------- .../Primitives/ShapeTests.cs | 2 +- 4 files changed, 87 insertions(+), 79 deletions(-) diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs b/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs index 9677c14f..d9211682 100644 --- a/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs +++ b/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs @@ -175,44 +175,22 @@ namespace MonoGame.Extended.Collisions /// The distance vector from the edge of b to a's Position private static Vector2 CalculatePenetrationVector(IShapeF a, IShapeF b) { - switch (a) - { - case RectangleF rectA when b is RectangleF rectB: - return PenetrationVector(rectA, rectB); - case CircleF circA when b is CircleF circB: - return PenetrationVector(circA, circB); - case CircleF circA when b is RectangleF rectB: - return PenetrationVector(circA, rectB); - case RectangleF rectA when b is CircleF circB: - return PenetrationVector(rectA, circB); - } + return a switch + { + CircleF circleA when b is CircleF circleB => PenetrationVector(circleA, circleB), + CircleF circleA when b is RectangleF rectangleB => PenetrationVector(circleA, rectangleB), + CircleF circleA when b is OrientedRectangle orientedRectangleB => PenetrationVector(circleA, orientedRectangleB), - throw new NotSupportedException("Shapes must be either a CircleF or RectangleF"); - } + RectangleF rectangleA when b is CircleF circleB => PenetrationVector(rectangleA, circleB), + RectangleF rectangleA when b is RectangleF rectangleB => PenetrationVector(rectangleA, rectangleB), + RectangleF rectangleA when b is OrientedRectangle orientedRectangleB => PenetrationVector(rectangleA, orientedRectangleB), - private static Vector2 PenetrationVector(RectangleF rect1, RectangleF rect2) - { - var intersectingRectangle = RectangleF.Intersection(rect1, rect2); - Debug.Assert(!intersectingRectangle.IsEmpty, - "Violation of: !intersect.IsEmpty; Rectangles must intersect to calculate a penetration vector."); + OrientedRectangle orientedRectangleA when b is CircleF circleB => PenetrationVector(orientedRectangleA, circleB), + OrientedRectangle orientedRectangleA when b is RectangleF rectangleB => PenetrationVector(orientedRectangleA, rectangleB), + OrientedRectangle orientedRectangleA when b is OrientedRectangle orientedRectangleB => PenetrationVector(orientedRectangleA, orientedRectangleB), - Vector2 penetration; - if (intersectingRectangle.Width < intersectingRectangle.Height) - { - var d = rect1.Center.X < rect2.Center.X - ? intersectingRectangle.Width - : -intersectingRectangle.Width; - penetration = new Vector2(d, 0); - } - else - { - var d = rect1.Center.Y < rect2.Center.Y - ? intersectingRectangle.Height - : -intersectingRectangle.Height; - penetration = new Vector2(0, d); - } - - return penetration; + _ => throw new ArgumentOutOfRangeException(nameof(a)) + }; } private static Vector2 PenetrationVector(CircleF circ1, CircleF circ2) @@ -287,11 +265,64 @@ namespace MonoGame.Extended.Collisions } } + private static Vector2 PenetrationVector(CircleF circleA, OrientedRectangle orientedRectangleB) + { + return new Vector2(); + throw new NotImplementedException(); + } + private static Vector2 PenetrationVector(RectangleF rect, CircleF circ) { return -PenetrationVector(circ, rect); } + private static Vector2 PenetrationVector(RectangleF rect1, RectangleF rect2) + { + var intersectingRectangle = RectangleF.Intersection(rect1, rect2); + Debug.Assert(!intersectingRectangle.IsEmpty, + "Violation of: !intersect.IsEmpty; Rectangles must intersect to calculate a penetration vector."); + + Vector2 penetration; + if (intersectingRectangle.Width < intersectingRectangle.Height) + { + var d = rect1.Center.X < rect2.Center.X + ? intersectingRectangle.Width + : -intersectingRectangle.Width; + penetration = new Vector2(d, 0); + } + else + { + var d = rect1.Center.Y < rect2.Center.Y + ? intersectingRectangle.Height + : -intersectingRectangle.Height; + penetration = new Vector2(0, d); + } + + return penetration; + } + + private static Vector2 PenetrationVector(RectangleF rectangleA, OrientedRectangle orientedRectangleB) + { + return new Vector2(); + throw new NotImplementedException(); + } + + private static Vector2 PenetrationVector(OrientedRectangle orientedRectangleA, CircleF circleB) + { + return -PenetrationVector(circleB, orientedRectangleA); + } + + private static Vector2 PenetrationVector(OrientedRectangle orientedRectangleA, RectangleF rectangleB) + { + return -PenetrationVector(rectangleB, orientedRectangleA); + } + + private static Vector2 PenetrationVector(OrientedRectangle orientedRectangleA, OrientedRectangle orientedRectangleB) + { + return new Vector2(); + throw new NotImplementedException(); + } + #endregion } } diff --git a/src/cs/MonoGame.Extended/Math/OrientedRectangle.cs b/src/cs/MonoGame.Extended/Math/OrientedRectangle.cs index e1911b62..d4057704 100644 --- a/src/cs/MonoGame.Extended/Math/OrientedRectangle.cs +++ b/src/cs/MonoGame.Extended/Math/OrientedRectangle.cs @@ -173,9 +173,10 @@ namespace MonoGame.Extended public static explicit operator RectangleF(OrientedRectangle orientedRectangle) { - var topLeft = orientedRectangle.Center - orientedRectangle.Radii; + var topLeft = -orientedRectangle.Radii; var rectangle = new RectangleF(topLeft, orientedRectangle.Radii * 2); - return RectangleF.Transform(rectangle, ref orientedRectangle.Orientation); + var orientation = orientedRectangle.Orientation * Matrix2.CreateTranslation(orientedRectangle.Center); + return RectangleF.Transform(rectangle, ref orientation); } /// diff --git a/src/cs/MonoGame.Extended/Math/ShapeF.cs b/src/cs/MonoGame.Extended/Math/ShapeF.cs index 979477f3..274a0a40 100644 --- a/src/cs/MonoGame.Extended/Math/ShapeF.cs +++ b/src/cs/MonoGame.Extended/Math/ShapeF.cs @@ -29,50 +29,27 @@ namespace MonoGame.Extended /// /// Check if two shapes intersect. /// - /// The first shape. - /// The second shape. + /// The first shape. + /// The second shape. /// True if the two shapes intersect. - public static bool Intersects(this IShapeF shapeA, IShapeF shapeB) + public static bool Intersects(this IShapeF a, IShapeF b) { - return shapeA switch + return a switch { - CircleF circleA => IntersectsInternal(circleA, shapeB), - RectangleF rectangleA => IntersectsInternal(rectangleA, shapeB), - OrientedRectangle orientedRectangleA => IntersectsInternal(orientedRectangleA, shapeB), - _ => throw new ArgumentOutOfRangeException(nameof(shapeA)) - }; - } + CircleF circleA when b is CircleF circleB => circleA.Intersects(circleB), + CircleF circleA when b is RectangleF rectangleB => circleA.Intersects(rectangleB), + CircleF circleA when b is OrientedRectangle orientedRectangleB => Intersects(circleA, orientedRectangleB), - private static bool IntersectsInternal(CircleF circle, IShapeF shape) - { - return shape switch - { - CircleF otherCircle => CircleF.Intersects(circle, otherCircle), - RectangleF otherRectangle => Intersects(circle, otherRectangle), - OrientedRectangle otherOrientedRectangle => Intersects(circle, otherOrientedRectangle), - _ => throw new ArgumentOutOfRangeException(nameof(shape)) - }; - } + RectangleF rectangleA when b is CircleF circleB => Intersects(circleB, rectangleA), + RectangleF rectangleA when b is RectangleF rectangleB => Intersects(rectangleA, rectangleB), + RectangleF rectangleA when b is OrientedRectangle orientedRectangleB => Intersects(rectangleA, orientedRectangleB), - private static bool IntersectsInternal(RectangleF rectangle, IShapeF shape) - { - return shape switch - { - CircleF otherCircle => Intersects(otherCircle, rectangle), - RectangleF otherRectangle => RectangleF.Intersects(rectangle, otherRectangle), - OrientedRectangle otherOrientedRectangle => Intersects(rectangle, otherOrientedRectangle), - _ => throw new ArgumentOutOfRangeException(nameof(shape)) - }; - } + OrientedRectangle orientedRectangleA when b is CircleF circleB => Intersects(circleB, orientedRectangleA), + OrientedRectangle orientedRectangleA when b is RectangleF rectangleB => Intersects(rectangleB, orientedRectangleA), + OrientedRectangle orientedRectangleA when b is OrientedRectangle orientedRectangleB + => OrientedRectangle.Intersects(orientedRectangleA, orientedRectangleB), - private static bool IntersectsInternal(OrientedRectangle orientedRectangle, IShapeF shape) - { - return shape switch - { - CircleF circleB => Intersects(circleB, orientedRectangle), - RectangleF rectangleB => Intersects(rectangleB, orientedRectangle), - OrientedRectangle orientedRectangleB => OrientedRectangle.Intersects(orientedRectangle, orientedRectangleB), - _ => throw new ArgumentOutOfRangeException(nameof(shape)) + _ => throw new ArgumentOutOfRangeException(nameof(a)) }; } @@ -96,11 +73,10 @@ namespace MonoGame.Extended /// True if the circle and oriented bounded rectangle intersects, otherwise false. public static bool Intersects(CircleF circle, OrientedRectangle orientedRectangle) { - var rotation = Matrix2.CreateRotationZ(-orientedRectangle.Orientation.Rotation); + var rotation = Matrix2.CreateRotationZ(orientedRectangle.Orientation.Rotation); var circleCenterInRectangleSpace = rotation.Transform(circle.Center - orientedRectangle.Center); var circleInRectangleSpace = new CircleF(circleCenterInRectangleSpace, circle.Radius); - var rectangleInLocalSpace = OrientedRectangle.Transform(orientedRectangle, ref rotation); - var boundingRectangle = new BoundingRectangle(rectangleInLocalSpace.Center, rectangleInLocalSpace.Radii); + var boundingRectangle = new BoundingRectangle(new Point2(), orientedRectangle.Radii); return circleInRectangleSpace.Intersects(boundingRectangle); } diff --git a/src/cs/Tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs b/src/cs/Tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs index dcc6f3af..37b2fc4c 100644 --- a/src/cs/Tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs +++ b/src/cs/Tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs @@ -135,7 +135,7 @@ public class ShapeTests * * * * :* * */ - IShapeF rectangle = new OrientedRectangle(new Point2(-1, 1), new Size2(2.8f, 2.8f), Matrix2.CreateRotationZ(MathHelper.PiOver4)); + IShapeF rectangle = new OrientedRectangle(new Point2(-1, 1), new Size2(1.42f, 1.42f), Matrix2.CreateRotationZ(-MathHelper.PiOver4)); var circle = new CircleF(new Point2(1, -1), 1.4f); Assert.False(rectangle.Intersects(circle));