OrientedBoundedRectangle implements ShapeF

In that way the OrientedBoundedRectangle can take part of collision tests.
This commit is contained in:
Andreas Torebring
2022-09-19 21:55:00 +02:00
parent 135507e850
commit 82303e48b1
3 changed files with 146 additions and 26 deletions
@@ -13,7 +13,7 @@ namespace MonoGame.Extended
/// </summary>
/// <seealso cref="IEquatable{T}" />
[DebuggerDisplay($"{{{nameof(DebugDisplayString)},nq}}")]
public struct OrientedBoundingRectangle : IEquatable<OrientedBoundingRectangle>
public struct OrientedBoundingRectangle : IEquatable<OrientedBoundingRectangle>, IShapeF
{
/// <summary>
/// The centre position of this <see cref="OrientedBoundingRectangle" />.
@@ -68,6 +68,14 @@ namespace MonoGame.Extended
}
}
public Point2 Position
{
get => Vector2.Transform(-Radii, Orientation) + Center;
set => throw new NotImplementedException();
}
public RectangleF BoundingRectangle => (RectangleF)this;
/// <summary>
/// Computes the <see cref="OrientedBoundingRectangle"/> from the specified <paramref name="rectangle"/>
/// transformed by <paramref name="transformMatrix"/>.
@@ -87,6 +95,7 @@ namespace MonoGame.Extended
ref rectangle.Center,
ref rectangle.Orientation,
ref transformMatrix);
result = new OrientedBoundingRectangle();
result.Center = rectangle.Center;
result.Radii = rectangle.Radii;
result.Orientation = rectangle.Orientation;
@@ -141,7 +150,7 @@ namespace MonoGame.Extended
}
/// <summary>
///
/// Returns a hash code for this object instance.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
@@ -149,6 +158,11 @@ namespace MonoGame.Extended
return HashCode.Combine(Center, Radii, Orientation);
}
/// <summary>
/// Performs an implicit conversion from a <see cref="RectangleF" /> to <see cref="OrientedBoundingRectangle" />.
/// </summary>
/// <param name="rectangle">The rectangle to convert.</param>
/// <returns>The resulting <see cref="OrientedBoundingRectangle" />.</returns>
public static explicit operator OrientedBoundingRectangle(RectangleF rectangle)
{
var radii = new Size2(rectangle.Width * 0.5f, rectangle.Height * 0.5f);
@@ -157,6 +171,25 @@ namespace MonoGame.Extended
return new OrientedBoundingRectangle(centre, radii, Matrix2.Identity);
}
public static explicit operator RectangleF(OrientedBoundingRectangle orientedBoundingRectangle)
{
var topLeft = orientedBoundingRectangle.Center - orientedBoundingRectangle.Radii;
var rectangle = new RectangleF(topLeft, orientedBoundingRectangle.Radii * 2);
return RectangleF.Transform(rectangle, ref orientedBoundingRectangle.Orientation);
}
/// <summary>
///
/// </summary>
/// <param name="rectangle"></param>
/// <param name="otherRectangle"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public static bool Intersects(OrientedBoundingRectangle rectangle, OrientedBoundingRectangle otherRectangle)
{
throw new NotImplementedException();
}
/// <summary>
/// Returns a <see cref="string" /> that represents this <see cref="OrientedBoundingRectangle" />.
/// </summary>
+71 -19
View File
@@ -1,4 +1,6 @@
namespace MonoGame.Extended
using System;
namespace MonoGame.Extended
{
/// <summary>
/// Base class for shapes.
@@ -32,26 +34,46 @@
/// <returns>True if the two shapes intersect.</returns>
public static bool Intersects(this IShapeF shapeA, IShapeF shapeB)
{
var intersects = false;
return shapeA switch
{
CircleF circleA => IntersectsInternal(circleA, shapeB),
RectangleF rectangleA => IntersectsInternal(rectangleA, shapeB),
OrientedBoundingRectangle orientedBoundingRectangleA => IntersectsInternal(orientedBoundingRectangleA, shapeB),
_ => throw new ArgumentOutOfRangeException(nameof(shapeA))
};
}
if (shapeA is RectangleF rectangleA && shapeB is RectangleF rectangleB)
{
intersects = rectangleA.Intersects(rectangleB);
}
else if (shapeA is CircleF circleA && shapeB is CircleF circleB)
{
intersects = circleA.Intersects(circleB);
}
else if (shapeA is RectangleF rect1 && shapeB is CircleF circ1)
{
return Intersects(circ1, rect1);
}
else if (shapeA is CircleF circ2 && shapeB is RectangleF rect2)
{
return Intersects(circ2, rect2);
}
private static bool IntersectsInternal(CircleF circle, IShapeF shape)
{
return shape switch
{
CircleF otherCircle => CircleF.Intersects(circle, otherCircle),
RectangleF otherRectangle => Intersects(circle, otherRectangle),
OrientedBoundingRectangle otherOrientedBoundingRectangle => Intersects(circle, otherOrientedBoundingRectangle),
_ => throw new ArgumentOutOfRangeException(nameof(shape))
};
}
return intersects;
private static bool IntersectsInternal(RectangleF rectangle, IShapeF shape)
{
return shape switch
{
CircleF otherCircle => Intersects(otherCircle, rectangle),
RectangleF otherRectangle => RectangleF.Intersects(rectangle, otherRectangle),
OrientedBoundingRectangle otherOrientedBoundingRectangle => Intersects(rectangle, otherOrientedBoundingRectangle),
_ => throw new ArgumentOutOfRangeException(nameof(shape))
};
}
private static bool IntersectsInternal(OrientedBoundingRectangle orientedBoundingRectangle, IShapeF shape)
{
return shape switch
{
CircleF circleB => Intersects(circleB, orientedBoundingRectangle),
RectangleF rectangleB => Intersects(rectangleB, orientedBoundingRectangle),
OrientedBoundingRectangle orientedBoundingRectangleB => OrientedBoundingRectangle.Intersects(orientedBoundingRectangle, orientedBoundingRectangleB),
_ => throw new ArgumentOutOfRangeException(nameof(shape))
};
}
/// <summary>
@@ -65,5 +87,35 @@
var closestPoint = rectangle.ClosestPointTo(circle.Center);
return circle.Contains(closestPoint);
}
/// <summary>
///
/// </summary>
/// <param name="circle"></param>
/// <param name="orientedBoundingRectangle"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public static bool Intersects(CircleF circle, OrientedBoundingRectangle orientedBoundingRectangle)
{
var rotation = Matrix2.CreateRotationZ(-orientedBoundingRectangle.Orientation.Rotation);
var circleCenterInRectangleSpace = rotation.Transform(orientedBoundingRectangle.Center - circle.Center);
var circleInRectangleSpace = new CircleF(circleCenterInRectangleSpace, circle.Radius);
var rectangleInLocalSpace = OrientedBoundingRectangle.Transform(orientedBoundingRectangle, ref rotation);
rectangleInLocalSpace.Center = Point2.Zero;
var rectangle = (BoundingRectangle)new RectangleF(0, 0, rectangleInLocalSpace.Radii.X, rectangleInLocalSpace.Radii.Y);
return circleInRectangleSpace.Intersects(rectangle);
}
/// <summary>
///
/// </summary>
/// <param name="rectangleF"></param>
/// <param name="orientedBoundingRectangle"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public static bool Intersects(RectangleF rectangleF, OrientedBoundingRectangle orientedBoundingRectangle)
{
throw new NotImplementedException();
}
}
}
@@ -1,8 +1,11 @@
using Xunit;
using Microsoft.Xna.Framework;
using Xunit;
namespace MonoGame.Extended.Tests.Primitives
namespace MonoGame.Extended.Tests.Primitives;
public class ShapeTests
{
public class ShapeTests
public class CircleFTests
{
[Fact]
public void CircCircIntersectionSameCircleTest()
@@ -30,7 +33,10 @@ namespace MonoGame.Extended.Tests.Primitives
Assert.False(shape1.Intersects(shape2));
}
}
public class RectangleFTests
{
[Fact]
public void RectRectSameRectTest()
{
@@ -68,7 +74,6 @@ namespace MonoGame.Extended.Tests.Primitives
Assert.True(shape2.Intersects(shape1));
}
[Fact]
public void RectCircOnEdgeTest()
{
@@ -79,4 +84,34 @@ namespace MonoGame.Extended.Tests.Primitives
Assert.True(shape2.Intersects(shape1));
}
}
}
public class OrientedBoundingRectangleTests
{
[Fact]
public void Axis_aligned_rectangle_overlaps_circle()
{
IShapeF rectangle = new OrientedBoundingRectangle(Point2.Zero, new Size2(1, 1), Matrix2.Identity);
var circle = new CircleF(Point2.Zero, 1);
Assert.True(rectangle.Intersects(circle));
}
[Fact]
public void Axis_aligned_rectangle_does_not_intersect_circle_in_top_left()
{
IShapeF rectangle = new OrientedBoundingRectangle(Point2.Zero, new Size2(1, 1), Matrix2.Identity);
var circle = new CircleF(new Point2(1, 1), 1);
Assert.False(rectangle.Intersects(circle));
}
[Fact]
public void Rectangle_rotated_45_degrees_does_not_intersect_circle_in_bottom_right()
{
IShapeF rectangle = new OrientedBoundingRectangle(Point2.Zero, new Size2(1, 1), Matrix2.CreateRotationZ(MathHelper.PiOver4));
var circle = new CircleF(new Point2(1, -1), 1.4f);
Assert.False(rectangle.Intersects(circle));
}
}
}