Properly intersect circle and oriented rect

This commit is contained in:
Andreas Torebring
2024-03-08 18:16:03 +01:00
parent 6a380e381b
commit bf1159d9f3
4 changed files with 87 additions and 79 deletions
@@ -175,44 +175,22 @@ namespace MonoGame.Extended.Collisions
/// <returns>The distance vector from the edge of b to a's Position</returns>
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
}
}
@@ -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);
}
/// <summary>
+17 -41
View File
@@ -29,50 +29,27 @@ namespace MonoGame.Extended
/// <summary>
/// Check if two shapes intersect.
/// </summary>
/// <param name="shapeA">The first shape.</param>
/// <param name="shapeB">The second shape.</param>
/// <param name="a">The first shape.</param>
/// <param name="b">The second shape.</param>
/// <returns>True if the two shapes intersect.</returns>
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
/// <returns>True if the circle and oriented bounded rectangle intersects, otherwise false.</returns>
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);
}
@@ -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));