Add Points property to OrientedBoundingRectangle

This commit is contained in:
Andreas Torebring
2022-09-07 01:56:47 +02:00
parent 88a51a6f68
commit db807cd659
4 changed files with 112 additions and 18 deletions
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
@@ -45,6 +46,28 @@ namespace MonoGame.Extended
Orientation = orientation;
}
/// <summary>
/// Gets a list of points defining the corner points of the oriented rectangle.
/// </summary>
public IReadOnlyList<Vector2> Points
{
get
{
var topLeft = -Radii;
var bottomLeft = -new Vector2(Radii.X, -Radii.Y);
var topRight = (Vector2)new Point2(Radii.X, -Radii.Y);
var bottomRight = Radii;
return new List<Vector2>
{
Vector2.Transform(topRight, Orientation) + Center,
Vector2.Transform(topLeft, Orientation) + Center,
Vector2.Transform(bottomLeft, Orientation) + Center,
Vector2.Transform(bottomRight, Orientation) + Center
};
}
}
/// <summary>
/// Computes the <see cref="OrientedBoundingRectangle"/> from the specified <paramref name="rectangle"/>
/// transformed by <paramref name="transformMatrix"/>.
@@ -62,7 +85,6 @@ namespace MonoGame.Extended
{
PrimitivesHelper.TransformOrientedBoundingRectangle(
ref rectangle.Center,
ref rectangle.Radii,
ref rectangle.Orientation,
ref transformMatrix);
result.Center = rectangle.Center;
@@ -127,6 +149,14 @@ namespace MonoGame.Extended
return HashCode.Combine(Center, Radii, Orientation);
}
public static explicit operator OrientedBoundingRectangle(RectangleF rectangle)
{
var radii = new Size2(rectangle.Width * 0.5f, rectangle.Height * 0.5f);
var centre = new Point2(rectangle.X + radii.Width, rectangle.Y + radii.Height);
return new OrientedBoundingRectangle(centre, radii, Matrix2.Identity);
}
/// <summary>
/// Returns a <see cref="string" /> that represents this <see cref="OrientedBoundingRectangle" />.
/// </summary>
@@ -74,17 +74,12 @@ namespace MonoGame.Extended
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void TransformOrientedBoundingRectangle(
ref Point2 center,
ref Vector2 radii,
ref Matrix2 orientation,
ref Matrix2 transformMatrix)
{
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.4; Oriented Bounding Boxes (OBBs), pg 101-105.
center = transformMatrix.Transform(center);
var xRadius = radii.X;
var yRadius = radii.Y;
radii.X = xRadius * Math.Abs(transformMatrix.M11) + yRadius * Math.Abs(transformMatrix.M12);
radii.Y = xRadius * Math.Abs(transformMatrix.M21) + yRadius * Math.Abs(transformMatrix.M22);
orientation *= transformMatrix;
// Reset the translation since orientation is only about rotation
orientation.M31 = 0;
@@ -0,0 +1,14 @@
using System.Collections.Generic;
using Xunit;
namespace MonoGame.Extended.Tests;
public static class CollectionAssert
{
public static void Equal<T>(IReadOnlyList<T> expected, IReadOnlyList<T> actual)
{
Assert.True(expected.Count == actual.Count, "The number of items in the collections does not match.");
Assert.All(actual, x => Assert.Contains(x, expected));
}
}
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Xna.Framework;
using Xunit;
using Vector2 = Microsoft.Xna.Framework.Vector2;
@@ -11,11 +10,20 @@ public class OrientedBoundingRectangleTests
[Fact]
public void Initializes_oriented_bounding_rectangle()
{
var rect = new OrientedBoundingRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10));
var rectangle = new OrientedBoundingRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10));
Assert.Equal(new Point2(1, 2), rect.Center);
Assert.Equal(new Vector2(3, 4), rect.Radii);
Assert.Equal(new Matrix2(5, 6, 7, 8, 9, 10), rect.Orientation);
Assert.Equal(new Point2(1, 2), rectangle.Center);
Assert.Equal(new Vector2(3, 4), rectangle.Radii);
Assert.Equal(new Matrix2(5, 6, 7, 8, 9, 10), rectangle.Orientation);
CollectionAssert.Equal(
new List<Vector2>
{
new(-3, -2),
new(-33, -38),
new(23, 26),
new(53, 62)
},
rectangle.Points);
}
public static readonly IEnumerable<object[]> _equalsComparisons = new[]
@@ -49,7 +57,7 @@ public class OrientedBoundingRectangleTests
[Fact]
public void Center_point_is_not_translated()
{
var rectangle = new OrientedBoundingRectangle(new Point2(1, 2), new Size2(), new Matrix2());
var rectangle = new OrientedBoundingRectangle(new Point2(1, 2), new Size2(), Matrix2.Identity);
var transform = Matrix2.Identity;
var result = OrientedBoundingRectangle.Transform(rectangle, ref transform);
@@ -91,7 +99,7 @@ public class OrientedBoundingRectangleTests
}
[Fact]
public void Rotate_45_degrees_left()
public void Orientation_is_rotated_45_degrees_left()
{
var rectangle = new OrientedBoundingRectangle(new Point2(), new Size2(), Matrix2.Identity);
var transform = Matrix2.CreateRotationZ(MathHelper.PiOver4);
@@ -104,7 +112,7 @@ public class OrientedBoundingRectangleTests
}
[Fact]
public void Rotate_to_45_degrees_from_180()
public void Orientation_is_rotated_to_45_degrees_from_180()
{
var rectangle = new OrientedBoundingRectangle(new Point2(), new Size2(), Matrix2.CreateRotationZ(MathHelper.Pi));
var transform = Matrix2.CreateRotationZ(-3 * MathHelper.PiOver4);
@@ -122,6 +130,44 @@ public class OrientedBoundingRectangleTests
Assert.Equal(expectedOrientation.M32, result.Orientation.M32, 6);
}
[Fact]
public void Points_are_same_as_center()
{
var rectangle = new OrientedBoundingRectangle(new Point2(1, 2), new Size2(), Matrix2.Identity);
var transform = Matrix2.Identity;
var result = OrientedBoundingRectangle.Transform(rectangle, ref transform);
CollectionAssert.Equal(
new List<Vector2>
{
new(1, 2),
new(1, 2),
new(1, 2),
new(1, 2)
},
result.Points);
}
[Fact]
public void Points_are_translated()
{
var rectangle = new OrientedBoundingRectangle(new Point2(0, 0), new Size2(2, 4), Matrix2.Identity);
var transform = Matrix2.CreateTranslation(10, 20);
var result = OrientedBoundingRectangle.Transform(rectangle, ref transform);
CollectionAssert.Equal(
new List<Vector2>
{
new(8, 16),
new(8, 24),
new(12, 24),
new(12, 16)
},
result.Points);
}
[Fact]
public void Applies_rotation_and_translation()
{
@@ -169,11 +215,20 @@ public class OrientedBoundingRectangleTests
var result = OrientedBoundingRectangle.Transform(rectangle, ref transform);
Assert.Equal(-2 + 10, result.Center.X, 6);
Assert.Equal(1 + 20, result.Center.Y, 6);
Assert.Equal(4, result.Radii.X, 6);
Assert.Equal(2, result.Radii.Y, 6);
Assert.Equal(8, result.Center.X, 6);
Assert.Equal(21, result.Center.Y, 6);
Assert.Equal(2, result.Radii.X, 6);
Assert.Equal(4, result.Radii.Y, 6);
Assert.Equal(Matrix2.CreateRotationZ(MathHelper.PiOver2), result.Orientation);
CollectionAssert.Equal(
new List<Vector2>
{
new(4, 23),
new(4, 19),
new(12, 19),
new(12, 23)
},
result.Points);
}
}
}