From 49038f5d428b03a13dc8e1010b6b6962ff51ca43 Mon Sep 17 00:00:00 2001 From: Christopher Whitley <103014489+AristurtleDev@users.noreply.github.com> Date: Mon, 27 May 2024 15:44:38 -0400 Subject: [PATCH] Replace `Matrix2` with `Matrix3x2` (#870) * Replace `Matrix2` with `Matrix3x2` * Update benchmarks * Add XML documentation for `Matrix3x2` --- .gitignore | 2 +- Directory.Build.props | 4 +- benchmarks/Directory.Build.props | 22 + ...Game.Extended.Benchmarks.Collisions.csproj | 5 - .../Matrix3x2Benchmarks.cs | 195 +++ .../MonoGame.Extended.Benchmarks.csproj | 14 + .../MonoGame.Extended.Benchmarks/Program.cs | 7 + .../CollisionComponent.cs | 4 +- .../MonoGame.Extended.Graphics/Batcher2D.cs | 36 +- .../Geometry/GeometryBuilder2D.cs | 2 +- .../Math/BoundingRectangle.cs | 12 +- source/MonoGame.Extended/Math/Matrix2.cs | 1038 ---------------- source/MonoGame.Extended/Math/Matrix3x2.cs | 1082 +++++++++++++++++ .../Math/OrientedRectangle.cs | 18 +- .../Math/PrimitivesHelper.cs | 6 +- source/MonoGame.Extended/Math/RectangleF.cs | 8 +- source/MonoGame.Extended/Math/ShapeF.cs | 2 +- source/MonoGame.Extended/Transform.cs | 34 +- .../MonoGame.Extended.Tests/Math/Matrix3x2.cs | 20 + .../Primitives/BoundingRectangleTests.cs | 8 +- .../Primitives/OrientedRectangleTests.cs | 56 +- .../Primitives/RectangleFTests.cs | 12 +- .../Primitives/ShapeTests.cs | 10 +- 23 files changed, 1447 insertions(+), 1150 deletions(-) create mode 100644 benchmarks/Directory.Build.props create mode 100644 benchmarks/MonoGame.Extended.Benchmarks/Matrix3x2Benchmarks.cs create mode 100644 benchmarks/MonoGame.Extended.Benchmarks/MonoGame.Extended.Benchmarks.csproj create mode 100644 benchmarks/MonoGame.Extended.Benchmarks/Program.cs delete mode 100644 source/MonoGame.Extended/Math/Matrix2.cs create mode 100644 source/MonoGame.Extended/Math/Matrix3x2.cs create mode 100644 tests/MonoGame.Extended.Tests/Math/Matrix3x2.cs diff --git a/.gitignore b/.gitignore index d64f1efd8..456e6515f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,7 @@ ################################################################################ ### Build artifacts directory ################################################################################ -.artifacts/ +*.[Aa]rtifacts/ ################################################################################ ### OS specific auto generated files diff --git a/Directory.Build.props b/Directory.Build.props index 52d9ed9e5..f600fa288 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -32,7 +32,7 @@ - + diff --git a/benchmarks/Directory.Build.props b/benchmarks/Directory.Build.props new file mode 100644 index 000000000..0381bf797 --- /dev/null +++ b/benchmarks/Directory.Build.props @@ -0,0 +1,22 @@ + + + + + + + $(SolutionDirectory).artifacts/benchmarks + false + false + NU1701 + true + + + + + + + + diff --git a/benchmarks/MonoGame.Extended.Benchmarks.Collisions/MonoGame.Extended.Benchmarks.Collisions.csproj b/benchmarks/MonoGame.Extended.Benchmarks.Collisions/MonoGame.Extended.Benchmarks.Collisions.csproj index 530bd13c6..8d095d327 100644 --- a/benchmarks/MonoGame.Extended.Benchmarks.Collisions/MonoGame.Extended.Benchmarks.Collisions.csproj +++ b/benchmarks/MonoGame.Extended.Benchmarks.Collisions/MonoGame.Extended.Benchmarks.Collisions.csproj @@ -7,11 +7,6 @@ enable - - - - - diff --git a/benchmarks/MonoGame.Extended.Benchmarks/Matrix3x2Benchmarks.cs b/benchmarks/MonoGame.Extended.Benchmarks/Matrix3x2Benchmarks.cs new file mode 100644 index 000000000..301cfbdc2 --- /dev/null +++ b/benchmarks/MonoGame.Extended.Benchmarks/Matrix3x2Benchmarks.cs @@ -0,0 +1,195 @@ +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Benchmarks; + +[MemoryDiagnoser] +public class Matrix3x2Benchmarks +{ + private Matrix2 _matrix2; + private Matrix3x2 _matrix3x2; + + [GlobalSetup] + public void Setup() + { + _matrix2 = new Matrix2(1, 2, 3, 4, 5, 6); + _matrix3x2 = new Matrix3x2(1, 2, 3, 4, 5, 6); + } + + [Benchmark] + public Vector2 Matrix2_getTranslation() => _matrix2.Translation; + + [Benchmark] + public Vector2 Matrix3x2_getTranslation() => _matrix3x2.Translation; + + // [Benchmark] + // public float Matrix2_getRotation() => _matrix2.Rotation; + + // [Benchmark] + // public float Matrix3x2_getRotation() => _matrix3x2.Rotation; + + // [Benchmark] + // public Vector2 Matrix2_getScale() => _matrix2.Scale; + + // [Benchmark] + // public Vector2 Matrix3x2_getScale() => _matrix3x2.Scale; + + // [Benchmark] + // public (Vector2, float, Vector2) Matrix2_Decompose() + // { + // Vector2 translation = _matrix2.Translation; + // float rotation = _matrix2.Rotation; + // Vector2 scale = _matrix2.Scale; + // return (translation, rotation, scale); + // } + + // [Benchmark] + // public (Vector2, float, Vector2) Matrix3x2_Decompose() + // { + // _matrix3x2.Decompose(out Vector2 translation, out float rotation, out Vector2 scale); + // return (translation, rotation, scale); + // } + + // [Benchmark] + // public Vector2 Matrix2_Transform() => _matrix2.Transform(Vector2.One); + + // [Benchmark] + // public Vector2 Matrix3x2_Transform() => _matrix3x2.Transform(Vector2.One); + + // [Benchmark] + // public float Matrix2_Determinant() => _matrix2.Determinant(); + + // [Benchmark] + // public float Matrix3x2_Determinant() => _matrix3x2.Determinant(); + + // [Benchmark] + // public Matrix2 Matrix2_CreateFrom() + // { + // Matrix2.CreateFrom(Vector2.Zero, 1.0f, Vector2.One, Vector2.Zero, out Matrix2 result); + // return result; + // } + + // [Benchmark] + // public Matrix3x2 Matrix3x2_CreateFrom() + // { + // Matrix3x2.CreateFrom(Vector2.Zero, 1.0f, Vector2.One, Vector2.Zero, out Matrix3x2 result); + // return result; + // } + + // [Benchmark] + // public Matrix2 Matrix2_CreateRotationZ() + // { + // Matrix2.CreateRotationZ(1.0f, out Matrix2 result); + // return result; + // } + + // [Benchmark] + // public Matrix3x2 Matrix3x2_CreateRotationZ() + // { + // Matrix3x2.CreateRotationZ(1.0f, out Matrix3x2 result); + // return result; + // } + + // [Benchmark] + // public Matrix2 Matrix2_CreateScale() + // { + // Matrix2.CreateScale(1.0f, out Matrix2 result); + // return result; + // } + + // [Benchmark] + // public Matrix3x2 Matrix3x2_CreateScale() + // { + // Matrix3x2.CreateScale(1.0f, out Matrix3x2 result); + // return result; + // } + + // [Benchmark] + // public Matrix2 Matrix2_CreateTranslation() + // { + // Matrix2.CreateTranslation(1.0f, 1.0f, out Matrix2 result); + // return result; + // } + + // [Benchmark] + // public Matrix3x2 Matrix3x2_CreateTranslation() + // { + // Matrix3x2.CreateTranslation(1.0f, 1.0f, out Matrix3x2 result); + // return result; + // } + + // [Benchmark] + // public Matrix2 Matrix2_Invert() + // { + // Matrix2.Invert(ref _matrix2, out Matrix2 result); + // return result; + // } + + // [Benchmark] + // public Matrix3x2 Matrix3x2_Invert() + // { + // Matrix3x2.Invert(_matrix3x2); + // return _matrix3x2; + // } + + // [Benchmark] + // public Matrix2 Matrix2_Add() + // { + // return Matrix2.Add(_matrix2, _matrix2); + // } + + // [Benchmark] + // public Matrix3x2 Matrix3x2_Add() + // { + // return Matrix3x2.Add(_matrix3x2, _matrix3x2); + // } + + // [Benchmark] + // public Matrix2 Matrix2_Subtract() + // { + // return Matrix2.Subtract(_matrix2, _matrix2); + // } + + // [Benchmark] + // public Matrix3x2 Matrix3x2_Subtract() + // { + // return Matrix3x2.Subtract(_matrix3x2, _matrix3x2); + // } + + // [Benchmark] + // public Matrix2 Matrix2_Multiply() + // { + // return Matrix2.Subtract(_matrix2, _matrix2); + // } + + // [Benchmark] + // public Matrix3x2 Matrix3x2_Multiply() + // { + // return Matrix3x2.Multiply(_matrix3x2, _matrix3x2); + // } + + // [Benchmark] + // public Matrix2 Matrix2_Divide() + // { + // return Matrix2.Divide(_matrix2, _matrix2); + // } + + // [Benchmark] + // public Matrix3x2 Matrix3x2_Divide() + // { + // return Matrix3x2.Divide(_matrix3x2, _matrix3x2); + // } + + // [Benchmark] + // public Matrix Matrix2_ToMatrix() + // { + // return _matrix2.ToMatrix(); + // } + + // [Benchmark] + // public Matrix Matrix3x2_ToMatrix() + // { + // return _matrix3x2.ToMatrix(); + // } +} diff --git a/benchmarks/MonoGame.Extended.Benchmarks/MonoGame.Extended.Benchmarks.csproj b/benchmarks/MonoGame.Extended.Benchmarks/MonoGame.Extended.Benchmarks.csproj new file mode 100644 index 000000000..04b670df2 --- /dev/null +++ b/benchmarks/MonoGame.Extended.Benchmarks/MonoGame.Extended.Benchmarks.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/benchmarks/MonoGame.Extended.Benchmarks/Program.cs b/benchmarks/MonoGame.Extended.Benchmarks/Program.cs new file mode 100644 index 000000000..79fbe5488 --- /dev/null +++ b/benchmarks/MonoGame.Extended.Benchmarks/Program.cs @@ -0,0 +1,7 @@ +using BenchmarkDotNet.Running; +using MonoGame.Extended.Benchmarks; + + +BenchmarkRunner.Run(); + +Console.WriteLine("finished"); diff --git a/source/MonoGame.Extended.Collisions/CollisionComponent.cs b/source/MonoGame.Extended.Collisions/CollisionComponent.cs index 51467d9d9..d5393ba94 100644 --- a/source/MonoGame.Extended.Collisions/CollisionComponent.cs +++ b/source/MonoGame.Extended.Collisions/CollisionComponent.cs @@ -273,13 +273,13 @@ private static Vector2 PenetrationVector(CircleF circ, RectangleF rect) private static Vector2 PenetrationVector(CircleF circleA, OrientedRectangle orientedRectangleB) { - var rotation = Matrix2.CreateRotationZ(orientedRectangleB.Orientation.Rotation); + var rotation = Matrix3x2.CreateRotationZ(orientedRectangleB.Orientation.Rotation); var circleCenterInRectangleSpace = rotation.Transform(circleA.Center - orientedRectangleB.Center); var circleInRectangleSpace = new CircleF(circleCenterInRectangleSpace, circleA.Radius); var boundingRectangle = new BoundingRectangle(new Point2(), orientedRectangleB.Radii); var penetrationVector = PenetrationVector(circleInRectangleSpace, boundingRectangle); - var inverseRotation = Matrix2.CreateRotationZ(-orientedRectangleB.Orientation.Rotation); + var inverseRotation = Matrix3x2.CreateRotationZ(-orientedRectangleB.Orientation.Rotation); var transformedPenetration = inverseRotation.Transform(penetrationVector); return transformedPenetration; diff --git a/source/MonoGame.Extended.Graphics/Batcher2D.cs b/source/MonoGame.Extended.Graphics/Batcher2D.cs index e7ec533f7..aeb03c61c 100644 --- a/source/MonoGame.Extended.Graphics/Batcher2D.cs +++ b/source/MonoGame.Extended.Graphics/Batcher2D.cs @@ -91,7 +91,7 @@ protected override void SortDrawCallsAndBindBuffers() // Upload the indices to the GPU and then select that index stream for drawing _indexBuffer.SetData(_sortedIndices, 0, _indexCount); GraphicsDevice.Indices = _indexBuffer; - + _indexCount = 0; _vertexCount = 0; } @@ -136,12 +136,12 @@ protected override void InvokeDrawCall(ref DrawCallInfo drawCall) } /// - /// Draws a sprite using a specified , transform , source + /// Draws a sprite using a specified , transform , source /// , and an optional /// , origin , , and depth . /// /// The . - /// The transform . + /// The transform . /// /// The texture region of the . Use /// null to use the entire . @@ -151,7 +151,7 @@ protected override void InvokeDrawCall(ref DrawCallInfo drawCall) /// The depth . The default value is 0. /// The method has not been called. /// is null. - public void DrawSprite(Texture2D texture, ref Matrix2 transformMatrix, ref Rectangle sourceRectangle, + public void DrawSprite(Texture2D texture, ref Matrix3x2 transformMatrix, ref Rectangle sourceRectangle, Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0) { _geometryBuilder.BuildSprite(_vertexCount, ref transformMatrix, texture, ref sourceRectangle, color, flags, depth); @@ -159,17 +159,17 @@ public void DrawSprite(Texture2D texture, ref Matrix2 transformMatrix, ref Recta } /// - /// Draws a using the specified transform and an optional + /// Draws a using the specified transform and an optional /// , origin , , and depth . /// /// The . - /// The transform . + /// The transform . /// The . Use null to use the default . /// The . The default value is . /// The depth . The default value is 0. /// The method has not been called. /// is null. - public void DrawTexture(Texture2D texture, ref Matrix2 transformMatrix, Color? color = null, + public void DrawTexture(Texture2D texture, ref Matrix3x2 transformMatrix, Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0) { var rectangle = default(Rectangle); @@ -193,12 +193,12 @@ private void EnqueueBuiltGeometry(Texture2D texture, float depth) /// /// Draws unicode (UTF-16) characters as sprites using the specified , text - /// , transform and optional , origin + /// , transform and optional , origin /// , , and depth . /// /// The . /// The text . - /// The transform . + /// The transform . /// /// The . Use null to use the default /// . @@ -207,7 +207,7 @@ private void EnqueueBuiltGeometry(Texture2D texture, float depth) /// The depth . The default value is 0f. /// The method has not been called. /// is null or is null. - public void DrawString(BitmapFont bitmapFont, StringBuilder text, ref Matrix2 transformMatrix, + public void DrawString(BitmapFont bitmapFont, StringBuilder text, ref Matrix3x2 transformMatrix, Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0f) { EnsureHasBegun(); @@ -316,19 +316,19 @@ public void DrawString(BitmapFont bitmapFont, StringBuilder text, Vector2 positi float rotation = 0f, Vector2? origin = null, Vector2? scale = null, FlipFlags flags = FlipFlags.None, float depth = 0f) { - Matrix2 transformMatrix; - Matrix2.CreateFrom(position, rotation, scale, origin, out transformMatrix); + Matrix3x2 transformMatrix; + Matrix3x2.CreateFrom(position, rotation, scale, origin, out transformMatrix); DrawString(bitmapFont, text, ref transformMatrix, color, flags, depth); } /// /// Draws unicode (UTF-16) characters as sprites using the specified , text - /// , transform and optional , origin + /// , transform and optional , origin /// , , and depth . /// /// The . /// The text . - /// The transform . + /// The transform . /// /// The . Use null to use the default /// . @@ -337,7 +337,7 @@ public void DrawString(BitmapFont bitmapFont, StringBuilder text, Vector2 positi /// The depth . The default value is 0f /// The method has not been called. /// is null or is null. - public void DrawString(BitmapFont bitmapFont, string text, ref Matrix2 transformMatrix, Color? color = null, + public void DrawString(BitmapFont bitmapFont, string text, ref Matrix3x2 transformMatrix, Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0f) { EnsureHasBegun(); @@ -394,8 +394,8 @@ public void DrawString(BitmapFont bitmapFont, string text, Vector2 position, Col float rotation = 0f, Vector2? origin = null, Vector2? scale = null, FlipFlags flags = FlipFlags.None, float depth = 0f) { - Matrix2 matrix; - Matrix2.CreateFrom(position, rotation, scale, origin, out matrix); + Matrix3x2 matrix; + Matrix3x2.CreateFrom(position, rotation, scale, origin, out matrix); DrawString(bitmapFont, text, ref matrix, color, flags, depth); } @@ -447,4 +447,4 @@ public int CompareTo(DrawCallInfo other) } } } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder2D.cs b/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder2D.cs index c113fbe03..a0eb4a0f3 100644 --- a/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder2D.cs +++ b/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder2D.cs @@ -12,7 +12,7 @@ public GeometryBuilder2D(int maximumVerticesCount, int maximumIndicesCount) { } - public void BuildSprite(int indexOffset, ref Matrix2 transformMatrix, Texture2D texture, + public void BuildSprite(int indexOffset, ref Matrix3x2 transformMatrix, Texture2D texture, ref Rectangle sourceRectangle, Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0) { diff --git a/source/MonoGame.Extended/Math/BoundingRectangle.cs b/source/MonoGame.Extended/Math/BoundingRectangle.cs index 324ebb24e..ca4af01d9 100644 --- a/source/MonoGame.Extended/Math/BoundingRectangle.cs +++ b/source/MonoGame.Extended/Math/BoundingRectangle.cs @@ -5,7 +5,7 @@ namespace MonoGame.Extended { - // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 77 + // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 77 /// /// An axis-aligned, four sided, two dimensional box defined by a centre and a radii @@ -112,7 +112,7 @@ public static BoundingRectangle CreateFrom(IReadOnlyList points) /// /// Computes the from the specified transformed by /// the - /// specified . + /// specified . /// /// The bounding rectangle. /// The transform matrix. @@ -129,7 +129,7 @@ public static BoundingRectangle CreateFrom(IReadOnlyList points) /// /// public static void Transform(ref BoundingRectangle boundingRectangle, - ref Matrix2 transformMatrix, out BoundingRectangle result) + ref Matrix3x2 transformMatrix, out BoundingRectangle result) { PrimitivesHelper.TransformRectangle(ref boundingRectangle.Center, ref boundingRectangle.HalfExtents, ref transformMatrix); result.Center = boundingRectangle.Center; @@ -139,7 +139,7 @@ public static void Transform(ref BoundingRectangle boundingRectangle, /// /// Computes the from the specified transformed by /// the - /// specified . + /// specified . /// /// The bounding rectangle. /// The transform matrix. @@ -155,7 +155,7 @@ public static void Transform(ref BoundingRectangle boundingRectangle, /// /// public static BoundingRectangle Transform(BoundingRectangle boundingRectangle, - ref Matrix2 transformMatrix) + ref Matrix3x2 transformMatrix) { BoundingRectangle result; Transform(ref boundingRectangle, ref transformMatrix, out result); @@ -576,4 +576,4 @@ public override string ToString() internal string DebugDisplayString => ToString(); } -} \ No newline at end of file +} diff --git a/source/MonoGame.Extended/Math/Matrix2.cs b/source/MonoGame.Extended/Math/Matrix2.cs deleted file mode 100644 index cb5671098..000000000 --- a/source/MonoGame.Extended/Math/Matrix2.cs +++ /dev/null @@ -1,1038 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using Microsoft.Xna.Framework; - -// ReSharper disable CompareOfFloatsByEqualityOperator - -namespace MonoGame.Extended -{ - // https://en.wikipedia.org/wiki/Matrix_(mathematics) - // "Immersive Linear Algebra"; Jacob Ström, Kalle Åström & Tomas Akenine-Möller; 2015-2016. Chapter 6: The Matrix. http://immersivemath.com/ila/ch06_matrices/ch06.html - // "Real-Time Collision Detection"; Christer Ericson; 2005. Chapter 3.1: A Math and Geometry Primer - Matrices. pg 23-34 - - // Original code was from Matrix2D.cs in the Nez Library: https://github.com/prime31/Nez/ - - /// - /// Defines a 3x3 matrix using floating point numbers which can store two dimensional translation, scale and rotation - /// information in a right-handed coordinate system. - /// - /// - /// - /// Matrices use a row vector layout in the XNA / MonoGame Framework but, in general, matrices can be either have - /// a row vector or column vector layout. Row vector matrices view vectors as a row from left to right, while - /// column vector matrices view vectors as a column from top to bottom. For example, the - /// corresponds to the fields and . - /// - /// - /// The fields M13 and M23 always have a value of 0.0f, and thus are removed from the - /// to reduce its memory footprint. Same is true for the field M33, except it always has a - /// value of 1.0f. - /// - /// - [DebuggerDisplay("{DebugDisplayString,nq}")] - public struct Matrix2 : IEquatable, IEquatableByRef - { - public float M11; // x scale, also used for rotation - public float M12; // used for rotation - - public float M21; // used for rotation - public float M22; // y scale, also used for rotation - - public float M31; // x translation - public float M32; // y translation - - /// - /// Gets the identity matrix. - /// - /// - /// The identity matrix. - /// - public static Matrix2 Identity { get; } = new Matrix2(1f, 0f, 0f, 1f, 0f, 0f); - - /// - /// Gets the translation. - /// - /// - /// The translation. - /// - /// The is equal to the vector (M31, M32). - public Vector2 Translation => new Vector2(M31, M32); - - /// - /// Gets the rotation angle in radians. - /// - /// - /// The rotation angle in radians. - /// - /// - /// The is equal to Atan2(M21, M11). - /// - public float Rotation => (float)Math.Atan2(M21, M11); - - /// - /// Gets the scale. - /// - /// - /// The scale. - /// - /// - /// The is equal to the vector - /// (Sqrt(M11 * M11 + M21 * M21), Sqrt(M12 * M12 + M22 * M22)). - /// - public Vector2 Scale - { - get - { - var scaleX = (float)Math.Sqrt(M11 * M11 + M21 * M21); - var scaleY = (float)Math.Sqrt(M12 * M12 + M22 * M22); - return new Vector2(scaleX, scaleY); - } - } - - /// - /// Initializes a new instance of the struct. - /// - /// The value to initialize to. - /// The value to initialize to. - /// The value to initialize to. - /// The value to initialize to. - /// The value to initialize to. - /// The value to initialize to. - /// - /// - /// The fields M13 and M23 always have a value of 0.0f, and thus are removed from the - /// to reduce its memory footprint. Same is true for the field M33, except it always has a - /// value of 1.0f. - /// - /// - public Matrix2(float m11, float m12, float m21, float m22, float m31, float m32) - { - M11 = m11; - M12 = m12; - - M21 = m21; - M22 = m22; - - M31 = m31; - M32 = m32; - } - - /// - /// Transforms the specified by this . - /// - /// The vector. - /// The resulting . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2 Transform(Vector2 vector) - { - Vector2 result; - Transform(vector, out result); - return result; - } - - /// - /// Transforms the specified by this . - /// - /// The vector. - /// The resulting . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Transform(Vector2 vector, out Vector2 result) - { - result.X = vector.X * M11 + vector.Y * M21 + M31; - result.Y = vector.X * M12 + vector.Y * M22 + M32; - } - - /// - /// Transforms the specified by this . - /// - /// The x value of the vector. - /// The y value of the vector. - /// The resulting . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Transform(float x, float y, out Vector2 result) - { - result.X = x * M11 + y * M21 + M31; - result.Y = x * M12 + y * M22 + M32; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Transform(float x, float y, ref Vector3 result) - { - result.X = x * M11 + y * M21 + M31; - result.Y = x * M12 + y * M22 + M32; - } - - /// - /// Initializes a new instance of the struct that can be used to translate, rotate, and scale a set of vertices in two dimensions. - /// - /// The amounts to translate by on the x and y axes. - /// The amount, in radians, in which to rotate around the z-axis. - /// The amount to scale by on the x and y axes. - /// The point which to rotate and scale around. - /// The resulting - public static void CreateFrom(Vector2 position, float rotation, Vector2? scale, Vector2? origin, - out Matrix2 transformMatrix) - { - transformMatrix = Identity; - - if (origin.HasValue) - { - transformMatrix.M31 = -origin.Value.X; - transformMatrix.M32 = -origin.Value.Y; - } - - if (scale.HasValue) - { - var scaleMatrix = CreateScale(scale.Value); - Multiply(ref transformMatrix, ref scaleMatrix, out transformMatrix); - } - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (rotation != 0f) - { - var rotationMatrix = CreateRotationZ(-rotation); - Multiply(ref transformMatrix, ref rotationMatrix, out transformMatrix); - } - - var translationMatrix = CreateTranslation(position); - Multiply(ref transformMatrix, ref translationMatrix, out transformMatrix); - } - - /// - /// Initializes a new instance of the struct that can be used to translate, rotate, and scale a set of vertices in two dimensions. - /// - /// The amounts to translate by on the x and y axes. - /// The amount, in radians, in which to rotate around the z-axis. - /// The amount to scale by on the x and y axes. - /// The point which to rotate and scale around. - /// The resulting . - public static Matrix2 CreateFrom(Vector2 position, float rotation, Vector2? scale = null, Vector2? origin = null) - { - var transformMatrix = Identity; - - if (origin.HasValue) - { - transformMatrix.M31 = -origin.Value.X; - transformMatrix.M32 = -origin.Value.Y; - } - - if (scale.HasValue) - { - var scaleMatrix = CreateScale(scale.Value); - transformMatrix = Multiply(transformMatrix, scaleMatrix); - } - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (rotation != 0f) - { - var rotationMatrix = CreateRotationZ(-rotation); - transformMatrix = Multiply(transformMatrix, rotationMatrix); - } - - var translationMatrix = CreateTranslation(position); - transformMatrix = Multiply(transformMatrix, translationMatrix); - - return transformMatrix; - } - - /// - /// Initializes a new instance of the struct that can be used to rotate a set of vertices - /// around the z-axis. - /// - /// The amount, in radians, in which to rotate around the z-axis. - /// The resulting . - public static Matrix2 CreateRotationZ(float radians) - { - Matrix2 result; - CreateRotationZ(radians, out result); - return result; - } - - /// - /// Calculates the struct that can be used to rotate a set of vertices around the z-axis. - /// - /// The amount, in radians, in which to rotate around the z-axis. - /// The resulting . - public static void CreateRotationZ(float radians, out Matrix2 result) - { - var val1 = (float)Math.Cos(radians); - var val2 = (float)Math.Sin(radians); - - result = new Matrix2 - { - M11 = val1, - M12 = val2, - M21 = -val2, - M22 = val1, - M31 = 0, - M32 = 0 - }; - } - - /// - /// Initializes a new instance of the struct that can be used to scale a set vertices. - /// - /// The amount to scale by on the x and y axes. - /// The resulting . - public static Matrix2 CreateScale(float scale) - { - Matrix2 result; - CreateScale(scale, scale, out result); - return result; - } - - /// - /// Calculates the struct that can be used to scale a set vertices. - /// - /// The amount to scale by on the x and y axes. - /// The resulting . - public static void CreateScale(float scale, out Matrix2 result) - { - CreateScale(scale, scale, out result); - } - - /// - /// Initializes a new instance of the struct that can be used to scale a set vertices. - /// - /// The amount to scale by on the x-axis. - /// The amount to scale by on the y-axis. - /// The resulting . - public static Matrix2 CreateScale(float xScale, float yScale) - { - Matrix2 result; - CreateScale(xScale, yScale, out result); - return result; - } - - /// - /// Calculates the struct that can be used to scale a set vertices. - /// - /// The amount to scale by on the x-axis. - /// The amount to scale by on the y-axis. - /// The resulting . - public static void CreateScale(float xScale, float yScale, out Matrix2 result) - { - result = new Matrix2 - { - M11 = xScale, - M12 = 0, - M21 = 0, - M22 = yScale, - M31 = 0, - M32 = 0 - }; - } - - /// - /// Initializes a new instance of the struct that can be used to scale a set vertices. - /// - /// The amounts to scale by on the x and y axes. - /// The resulting . - public static Matrix2 CreateScale(Vector2 scale) - { - Matrix2 result; - CreateScale(ref scale, out result); - return result; - } - - /// - /// Calculates the struct that can be used to scale a set vertices. - /// - /// The amounts to scale by on the x and y axes. - /// The resulting . - public static void CreateScale(ref Vector2 scale, out Matrix2 result) - { - result = new Matrix2 - { - M11 = scale.X, - M12 = 0, - M21 = 0, - M22 = scale.Y, - M31 = 0, - M32 = 0 - }; - } - - /// - /// Initializes a new instance of the struct that can be used to translate a set vertices. - /// - /// The amount to translate by on the x-axis. - /// The amount to translate by on the y-axis. - /// The resulting . - public static Matrix2 CreateTranslation(float xPosition, float yPosition) - { - Matrix2 result; - CreateTranslation(xPosition, yPosition, out result); - return result; - } - - /// - /// Calculates the struct that can be used to translate a set vertices. - /// - /// The amount to translate by on the x-axis. - /// The amount to translate by on the y-axis. - /// The resulting . - public static void CreateTranslation(float xPosition, float yPosition, out Matrix2 result) - { - result = new Matrix2 - { - M11 = 1, - M12 = 0, - M21 = 0, - M22 = 1, - M31 = xPosition, - M32 = yPosition - }; - } - - /// - /// Initializes a new instance of the struct that can be used to translate a set vertices. - /// - /// The amounts to translate by on the x and y axes. - /// The resulting . - public static Matrix2 CreateTranslation(Vector2 position) - { - Matrix2 result; - CreateTranslation(ref position, out result); - return result; - } - - /// - /// Calculates the struct that can be used to translate a set vertices. - /// - /// The amounts to translate by on the x and y axes. - /// The resulting . - public static void CreateTranslation(ref Vector2 position, out Matrix2 result) - { - result = new Matrix2 - { - M11 = 1, - M12 = 0, - M21 = 0, - M22 = 1, - M31 = position.X, - M32 = position.Y - }; - } - - /// - /// Calculates the determinant of the . - /// - /// The determinant of the . - public float Determinant() - { - return M11 * M22 - M12 * M21; - } - - /// - /// Initializes a new instance of the struct with the summation of two - /// s. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix2 Add(Matrix2 matrix1, Matrix2 matrix2) - { - matrix1.M11 += matrix2.M11; - matrix1.M12 += matrix2.M12; - - matrix1.M21 += matrix2.M21; - matrix1.M22 += matrix2.M22; - - matrix1.M31 += matrix2.M31; - matrix1.M32 += matrix2.M32; - - return matrix1; - } - - /// - /// Calculates the summation of two s. - /// - /// The first . - /// The second . - /// The resulting . - public static void Add(ref Matrix2 matrix1, ref Matrix2 matrix2, out Matrix2 result) - { - result = new Matrix2 - { - M11 = matrix1.M11 + matrix2.M11, - M12 = matrix1.M12 + matrix2.M12, - M21 = matrix1.M21 + matrix2.M21, - M22 = matrix1.M22 + matrix2.M22, - M31 = matrix1.M31 + matrix2.M31, - M32 = matrix1.M32 + matrix2.M32 - }; - } - - /// - /// Initializes a new instance of the struct with the summation of two - /// s. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix2 operator +(Matrix2 matrix1, Matrix2 matrix2) - { - matrix1.M11 = matrix1.M11 + matrix2.M11; - matrix1.M12 = matrix1.M12 + matrix2.M12; - - matrix1.M21 = matrix1.M21 + matrix2.M21; - matrix1.M22 = matrix1.M22 + matrix2.M22; - - matrix1.M31 = matrix1.M31 + matrix2.M31; - matrix1.M32 = matrix1.M32 + matrix2.M32; - - return matrix1; - } - - /// - /// Initializes a new instance of the struct with the substraction of two - /// - /// s. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix2 Subtract(Matrix2 matrix1, Matrix2 matrix2) - { - matrix1.M11 = matrix1.M11 - matrix2.M11; - matrix1.M12 = matrix1.M12 - matrix2.M12; - - matrix1.M21 = matrix1.M21 - matrix2.M21; - matrix1.M22 = matrix1.M22 - matrix2.M22; - - matrix1.M31 = matrix1.M31 - matrix2.M31; - matrix1.M32 = matrix1.M32 - matrix2.M32; - return matrix1; - } - - /// - /// Calculates the substraction of two s. - /// - /// The first . - /// The second . - /// The resulting . - public static void Subtract(ref Matrix2 matrix1, ref Matrix2 matrix2, out Matrix2 result) - { - result = new Matrix2 - { - M11 = matrix1.M11 - matrix2.M11, - M12 = matrix1.M12 - matrix2.M12, - M21 = matrix1.M21 - matrix2.M21, - M22 = matrix1.M22 - matrix2.M22, - M31 = matrix1.M31 - matrix2.M31, - M32 = matrix1.M32 - matrix2.M32 - }; - } - - /// - /// Initializes a new instance of the struct with the substraction of two - /// - /// s. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix2 operator -(Matrix2 matrix1, Matrix2 matrix2) - { - matrix1.M11 = matrix1.M11 - matrix2.M11; - matrix1.M12 = matrix1.M12 - matrix2.M12; - - matrix1.M21 = matrix1.M21 - matrix2.M21; - matrix1.M22 = matrix1.M22 - matrix2.M22; - - matrix1.M31 = matrix1.M31 - matrix2.M31; - matrix1.M32 = matrix1.M32 - matrix2.M32; - return matrix1; - } - - /// - /// Initializes a new instance of the struct with the multiplication of two - /// - /// s. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix2 Multiply(Matrix2 matrix1, Matrix2 matrix2) - { - var m11 = matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21; - var m12 = matrix1.M11 * matrix2.M12 + matrix1.M12 * matrix2.M22; - - var m21 = matrix1.M21 * matrix2.M11 + matrix1.M22 * matrix2.M21; - var m22 = matrix1.M21 * matrix2.M12 + matrix1.M22 * matrix2.M22; - - var m31 = matrix1.M31 * matrix2.M11 + matrix1.M32 * matrix2.M21 + matrix2.M31; - var m32 = matrix1.M31 * matrix2.M12 + matrix1.M32 * matrix2.M22 + matrix2.M32; - - matrix1.M11 = m11; - matrix1.M12 = m12; - - matrix1.M21 = m21; - matrix1.M22 = m22; - - matrix1.M31 = m31; - matrix1.M32 = m32; - - return matrix1; - } - - /// - /// Calculates the multiplication of two s. - /// - /// The first . - /// The second . - /// The resulting . - public static void Multiply(ref Matrix2 matrix1, ref Matrix2 matrix2, out Matrix2 result) - { - var m11 = matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21; - var m12 = matrix1.M11 * matrix2.M12 + matrix1.M12 * matrix2.M22; - - var m21 = matrix1.M21 * matrix2.M11 + matrix1.M22 * matrix2.M21; - var m22 = matrix1.M21 * matrix2.M12 + matrix1.M22 * matrix2.M22; - - var m31 = matrix1.M31 * matrix2.M11 + matrix1.M32 * matrix2.M21 + matrix2.M31; - var m32 = matrix1.M31 * matrix2.M12 + matrix1.M32 * matrix2.M22 + matrix2.M32; - - result = new Matrix2 - { - M11 = m11, - M12 = m12, - M21 = m21, - M22 = m22, - M31 = m31, - M32 = m32 - }; - } - - /// - /// Initializes a new instance of the struct with the division of a by - /// a scalar. - /// - /// The . - /// The amount to divide the by. - /// The resulting . - public static Matrix2 Multiply(Matrix2 matrix, float scalar) - { - matrix.M11 *= scalar; - matrix.M12 *= scalar; - - matrix.M21 *= scalar; - matrix.M22 *= scalar; - - matrix.M31 *= scalar; - matrix.M32 *= scalar; - return matrix; - } - - /// - /// Calculates the multiplication of a by a scalar. - /// - /// The . - /// The amount to multiple the by. - /// The resulting . - public static void Multiply(ref Matrix2 matrix, float scalar, out Matrix2 result) - { - result = new Matrix2 - { - M11 = matrix.M11 * scalar, - M12 = matrix.M12 * scalar, - M21 = matrix.M21 * scalar, - M22 = matrix.M22 * scalar, - M31 = matrix.M31 * scalar, - M32 = matrix.M32 * scalar - }; - } - - /// - /// Initializes a new instance of the struct with the multiplication of two - /// - /// s. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix2 operator *(Matrix2 matrix1, Matrix2 matrix2) - { - var m11 = matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21; - var m12 = matrix1.M11 * matrix2.M12 + matrix1.M12 * matrix2.M22; - - var m21 = matrix1.M21 * matrix2.M11 + matrix1.M22 * matrix2.M21; - var m22 = matrix1.M21 * matrix2.M12 + matrix1.M22 * matrix2.M22; - - var m31 = matrix1.M31 * matrix2.M11 + matrix1.M32 * matrix2.M21 + matrix2.M31; - var m32 = matrix1.M31 * matrix2.M12 + matrix1.M32 * matrix2.M22 + matrix2.M32; - - matrix1.M11 = m11; - matrix1.M12 = m12; - - matrix1.M21 = m21; - matrix1.M22 = m22; - - matrix1.M31 = m31; - matrix1.M32 = m32; - - return matrix1; - } - - /// - /// Initializes a new instance of the struct with the division of a by - /// a scalar. - /// - /// The . - /// The amount to divide the by. - /// The resulting . - public static Matrix2 operator *(Matrix2 matrix, float scalar) - { - matrix.M11 = matrix.M11 * scalar; - matrix.M12 = matrix.M12 * scalar; - - matrix.M21 = matrix.M21 * scalar; - matrix.M22 = matrix.M22 * scalar; - - matrix.M31 = matrix.M31 * scalar; - matrix.M32 = matrix.M32 * scalar; - - return matrix; - } - - /// - /// Initializes a new instance of the struct with the division of two - /// s. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix2 Divide(Matrix2 matrix1, Matrix2 matrix2) - { - matrix1.M11 = matrix1.M11 / matrix2.M11; - matrix1.M12 = matrix1.M12 / matrix2.M12; - - matrix1.M21 = matrix1.M21 / matrix2.M21; - matrix1.M22 = matrix1.M22 / matrix2.M22; - - matrix1.M31 = matrix1.M31 / matrix2.M31; - matrix1.M32 = matrix1.M32 / matrix2.M32; - return matrix1; - } - - /// - /// Calculates the division of two s. - /// - /// The first . - /// The second . - /// The resulting . - public static void Divide(ref Matrix2 matrix1, ref Matrix2 matrix2, out Matrix2 result) - { - result = new Matrix2 - { - M11 = matrix1.M11 / matrix2.M11, - M12 = matrix1.M12 / matrix2.M12, - M21 = matrix1.M21 / matrix2.M21, - M22 = matrix1.M22 / matrix2.M22, - M31 = matrix1.M31 / matrix2.M31, - M32 = matrix1.M32 / matrix2.M32 - }; - } - - /// - /// Initializes a new instance of the struct with the division of a by - /// a scalar. - /// - /// The . - /// The amount to divide the by. - /// The resulting . - public static Matrix2 Divide(Matrix2 matrix, float scalar) - { - var num = 1f / scalar; - matrix.M11 = matrix.M11 * num; - matrix.M12 = matrix.M12 * num; - - matrix.M21 = matrix.M21 * num; - matrix.M22 = matrix.M22 * num; - - matrix.M31 = matrix.M31 * num; - matrix.M32 = matrix.M32 * num; - - return matrix; - } - - /// - /// Calculates the division of a by a scalar. - /// - /// The . - /// The amount to divide the by. - /// The resulting . - public static void Divide(ref Matrix2 matrix, float scalar, out Matrix2 result) - { - var num = 1f / scalar; - result = new Matrix2 - { - M11 = matrix.M11 * num, - M12 = matrix.M12 * num, - M21 = matrix.M21 * num, - M22 = matrix.M22 * num, - M31 = matrix.M31 * num, - M32 = matrix.M32 * num - }; - } - - /// - /// Initializes a new instance of the struct with the division of two - /// s. - /// - /// The first . - /// The second . - /// The resulting . - public static Matrix2 operator /(Matrix2 matrix1, Matrix2 matrix2) - { - matrix1.M11 = matrix1.M11 / matrix2.M11; - matrix1.M12 = matrix1.M12 / matrix2.M12; - - matrix1.M21 = matrix1.M21 / matrix2.M21; - matrix1.M22 = matrix1.M22 / matrix2.M22; - - matrix1.M31 = matrix1.M31 / matrix2.M31; - matrix1.M32 = matrix1.M32 / matrix2.M32; - return matrix1; - } - - /// - /// Initializes a new instance of the struct with the division of a by - /// a scalar. - /// - /// The . - /// The amount to divide the by. - /// The resulting . - public static Matrix2 operator /(Matrix2 matrix, float scalar) - { - var num = 1f / scalar; - matrix.M11 = matrix.M11 * num; - matrix.M12 = matrix.M12 * num; - - matrix.M21 = matrix.M21 * num; - matrix.M22 = matrix.M22 * num; - - matrix.M31 = matrix.M31 * num; - matrix.M32 = matrix.M32 * num; - return matrix; - } - - /// - /// Initializes a new instance of the struct with the inversion of a . - /// - /// The . - /// The resulting . - public static Matrix2 Invert(Matrix2 matrix) - { - var det = 1 / matrix.Determinant(); - - var m11 = matrix.M22 * det; - var m12 = -matrix.M12 * det; - - var m21 = -matrix.M21 * det; - var m22 = matrix.M11 * det; - - var m31 = (matrix.M32 * matrix.M21 - matrix.M31 * matrix.M22) * det; - var m32 = -(matrix.M32 * matrix.M11 - matrix.M31 * matrix.M12) * det; - - return new Matrix2(m11, m12, m21, m22, m31, m32); - } - - /// - /// Calculates the inversion of a . - /// - /// The . - /// The resulting . - public static void Invert(ref Matrix2 matrix, out Matrix2 result) - { - var det = 1 / matrix.Determinant(); - - result = new Matrix2 - { - M11 = matrix.M22 * det, - M12 = -matrix.M12 * det, - M21 = -matrix.M21 * det, - M22 = matrix.M11 * det, - M31 = (matrix.M32 * matrix.M21 - matrix.M31 * matrix.M22) * det, - M32 = -(matrix.M32 * matrix.M11 - matrix.M31 * matrix.M12) * det - }; - } - - /// - /// Initializes a new instance of the struct with the inversion of a . - /// - /// The . - /// The resulting . - public static Matrix2 operator -(Matrix2 matrix) - { - matrix.M11 = -matrix.M11; - matrix.M12 = -matrix.M12; - - matrix.M21 = -matrix.M21; - matrix.M22 = -matrix.M22; - - matrix.M31 = -matrix.M31; - matrix.M32 = -matrix.M32; - return matrix; - } - - /// - /// Compares a for equality with another without any tolerance. - /// - /// The first . - /// The second . - /// true if the s are equal; false otherwise. - public static bool operator ==(Matrix2 matrix1, Matrix2 matrix2) - { - return (matrix1.M11 == matrix2.M11) && (matrix1.M12 == matrix2.M12) && (matrix1.M21 == matrix2.M21) && - (matrix1.M22 == matrix2.M22) && (matrix1.M31 == matrix2.M31) && (matrix1.M32 == matrix2.M32); - } - - /// - /// Compares a for inequality with another without any tolerance. - /// - /// The first . - /// The second . - /// true if the s are not equal; false otherwise. - public static bool operator !=(Matrix2 matrix1, Matrix2 matrix2) - { - return (matrix1.M11 != matrix2.M11) || (matrix1.M12 != matrix2.M12) || (matrix1.M21 != matrix2.M21) || - (matrix1.M22 != matrix2.M22) || (matrix1.M31 != matrix2.M31) || (matrix1.M32 != matrix2.M32); - } - - /// - /// Returns a value that indicates whether the current is equal to a specified - /// . - /// - /// The with which to make the comparison. - /// - /// true if the current is equal to the specified ; - /// false otherwise. - /// - public bool Equals(ref Matrix2 matrix) - { - return (M11 == matrix.M11) && (M12 == matrix.M12) && (M21 == matrix.M21) && (M22 == matrix.M22) && - (M31 == matrix.M31) && (M32 == matrix.M32); - } - - /// - /// Returns a value that indicates whether the current is equal to a specified - /// . - /// - /// The with which to make the comparison. - /// - /// true if the current is equal to the specified ; - /// false otherwise. - /// - public bool Equals(Matrix2 matrix) - { - return Equals(ref matrix); - } - - /// - /// Returns a value that indicates whether the current is equal to a specified object. - /// - /// The object with which to make the comparison. - /// - /// true if the current is equal to the specified object; - /// false otherwise. - /// - public override bool Equals(object obj) - { - return obj is Matrix2 && Equals((Matrix2)obj); - } - - /// - /// Returns a hash code for this . - /// - /// - /// A hash code for this , suitable for use in hashing algorithms and data structures like a - /// hash table. - /// - public override int GetHashCode() - { - // ReSharper disable NonReadonlyMemberInGetHashCode - return M11.GetHashCode() + M12.GetHashCode() + M21.GetHashCode() + M22.GetHashCode() + M31.GetHashCode() + - M32.GetHashCode(); - // ReSharper restore NonReadonlyMemberInGetHashCode - } - - /// - /// Performs an implicit conversion from to . - /// - /// The . - /// - /// The resulting . - /// - public static implicit operator Matrix(Matrix2 matrix) - { - return new Matrix(matrix.M11, matrix.M12, 0, 0, matrix.M21, matrix.M22, 0, 0, 0, 0, 1, 0, matrix.M31, - matrix.M32, 0, 1); - } - - /// - /// Performs an explicit conversion from a specified to a . - /// - /// The . - /// The depth value. - /// The resulting . - public static void ToMatrix(ref Matrix2 matrix, float depth, out Matrix result) - { - result.M11 = matrix.M11; - result.M12 = matrix.M12; - result.M13 = 0; - result.M14 = 0; - - result.M21 = matrix.M21; - result.M22 = matrix.M22; - result.M23 = 0; - result.M24 = 0; - - result.M31 = 0; - result.M32 = 0; - result.M33 = 1; - result.M34 = 0; - - result.M41 = matrix.M31; - result.M42 = matrix.M32; - result.M43 = depth; - result.M44 = 1; - } - - /// - /// Performs an explicit conversion from a specified to a . - /// - /// The depth value. - /// The resulting . - public Matrix ToMatrix(float depth = 0) - { - Matrix result; - ToMatrix(ref this, depth, out result); - return result; - } - - /// - /// Gets the debug display string. - /// - /// - /// The debug display string. - /// - internal string DebugDisplayString => this == Identity - ? "Identity" - : $"T:({Translation.X:0.##},{Translation.Y:0.##}), R:{MathHelper.ToDegrees(Rotation):0.##}°, S:({Scale.X:0.##},{Scale.Y:0.##})" - ; - - /// - /// Returns a that represents this . - /// - /// - /// A that represents this . - /// - public override string ToString() - { - return $"{{M11:{M11} M12:{M12}}} {{M21:{M21} M22:{M22}}} {{M31:{M31} M32:{M32}}}"; - } - } -} \ No newline at end of file diff --git a/source/MonoGame.Extended/Math/Matrix3x2.cs b/source/MonoGame.Extended/Math/Matrix3x2.cs new file mode 100644 index 000000000..3a6d52098 --- /dev/null +++ b/source/MonoGame.Extended/Math/Matrix3x2.cs @@ -0,0 +1,1082 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended; + +/// +/// Represents a 3x2 matrix using floating point values for each component that can store two dimensional translation, +/// scale, and rotation information for a right-handed coordinate system. +/// +/// +/// +/// Matrices use a row vector layout in the XNA / MonoGame Framework but, in general, matrices can be either +/// have a row vector or column vector layout. Row vector matrices view vectors as a row from left to right, +/// while column vector matrices view vectors as a column from top to bottom. For example, the +/// corresponds to the fields and . +/// +/// +/// The fields see M13 and M23 always have a value of 0.0f, and thus are removed from +/// the to reduce its memory footprint. Same is true for the field M33, except +/// it always has a value of 1.0f. +/// +/// +[DebuggerDisplay($"{nameof(DebugDisplayString)},nq")] +public struct Matrix3x2 : IEquatable +{ + /// The first element of the first row. + /// Represents the scaling factor on the x-axis or a combination of scaling and rotation. + public float M11; + + /// The second element of the first row. + /// Represents the shearing factor on the y-axis or a combination of shearing and rotation. + public float M12; + + /// The first element of the second row. + /// Represents the shearing factor on the x-axis or a combination of shear and rotation. + public float M21; + + /// The second element of the second row. + /// Represents the scaling factor on the y-axis or a combination of scale and rotation. + public float M22; + + /// The first element of the third row. + /// Represents the translation on the x-axis + public float M31; + + /// The second element of the third row. + /// Represents the translation on the y-axis + public float M32; + + /// + /// Gets or Sets the vector formed by the first row of this + /// + /// + /// + /// The component of the vector represents the scaling factor on the x-axis or a + /// combination of scaling and rotation. + /// + /// + /// The component of the vector represents the shearing factor on the y-axis or a + /// combination of shearing and rotation. + /// + /// + public Vector2 X + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in M11)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref M11) = value; + } + + /// + /// Gets the vector formed by the second row of this + /// + /// + /// + /// The component of the vector represents the shearing factor on the x-axis or a + /// combination of shearing and rotation. + /// + /// + /// The component of the vector represents the scaling factor on the y-axis or a + /// combination of scaling and rotation. + /// + /// + public Vector2 Y + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in M21)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref M21) = value; + } + + /// + /// Gets or Sets the vector formed by the third row of this + /// + /// + /// + /// The component of the vector represents the translation on the x-axis. + /// + /// + /// The component of the vector represents the translation on the y-axis. + /// + /// + public Vector2 Z + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.As(ref Unsafe.AsRef(in M31)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Unsafe.As(ref M31) = value; + } + + /// + /// Gets the multiplicative identity matrix. + /// + /// + /// The first row of the identity matrix is equal to + /// The second row of the identity matrix is equal to + /// The third row of the identity matrix is equal to + /// + public static readonly Matrix3x2 Identity = new Matrix3x2(Vector2.UnitX, Vector2.UnitY, Vector2.Zero); + + /// + /// Gets the translation component of this . + /// + /// + /// The translation is equal to the third row vector composed of the and + /// values. + /// + [Obsolete("Use Decompose method. This property will be removed in 4.0")] + public readonly Vector2 Translation + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Z; + } + + /// + /// Gets the rotation component of this . + /// + /// + /// The rotation is equal to the arctangent of the and . + /// Math.Atan2(M21, M11); + /// + [Obsolete("Use Decompose method. This property will be removed in 4.0")] + public readonly float Rotation + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (float)Math.Atan2(M21, M11); + } + + /// + /// Gets the scale component of this . + /// + /// + /// The scale is equal to equal to the square root of the sum of the squares of matrix elements, with sign + /// adjustment. + /// + [Obsolete("Use Decompose method. This property will be removed in 4.0")] + public readonly Vector2 Scale + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + var xSign = Math.Sign((M11 * M11) + (M21 * M21)) < 0 ? -1 : 1; + var ySign = Math.Sign((M11 * M12) + (M22 * M22)) < 0 ? -1 : 1; + + Vector2 scale; + scale.X = xSign * (float)Math.Sqrt((M11 * M11) + (M21 * M21)); + scale.Y = ySign * (float)Math.Sqrt((M11 * M12) + (M22 * M22)); + + return scale; + } + } + + /// + /// Creates a 3x2 matrix from the specified components. + /// + /// The value to assign to the first element of the first row. + /// The value to assign to the second element of the first row. + /// The value to assign to the first element of the second row. + /// The value to assign to the second element of the second row. + /// The value to assign to the first element of the third row. + /// The value to assign to the second element of the third row. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Matrix3x2(float m11, float m12, + float m21, float m22, + float m31, float m32) + { + M11 = m11; + M12 = m12; + M21 = m21; + M22 = m22; + M31 = m31; + M32 = m32; + } + + /// + /// Creates a new 3x2 matrix from the specified components. + /// + /// The value to assign to the elements of the first row. + /// The value to assign to the elements of the second row. + /// The value to assign to the elements of the third row. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Matrix3x2(Vector2 x, Vector2 y, Vector2 z) + : this(x.X, x.Y, y.X, y.Y, z.X, z.Y) { } + + + /// + /// Transforms the given vector by this + /// + /// The vector to transform. + /// The result of the transformation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Transform(Vector2 vector) => Transform(vector.X, vector.Y); + + /// + /// Transforms the given vector by this + /// + /// The vector to transform. + /// + /// When this method returns, contains the result of the transformation. This parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Transform(Vector2 vector, out Vector2 result) => Transform(vector.X, vector.Y, out result); + + /// + /// Transforms a vector with the specified x- and y-coordinate component values by this + /// + /// The x-coordinate component value of the vector to transform. + /// The y-coordinate component value of the vector to transform. + /// The result of the transformation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector2 Transform(in float x, in float y) + { + Vector2 result; + Transform(x, y, out result); + return result; + } + + /// + /// Transforms a vector with the specified x- and y-coordinate component values by this + /// + /// The x-coordinate component value of the vector to transform. + /// The y-coordinate component value of the vector to transform. + /// + /// When this method returns, contains the result of the transformation. This parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Transform(in float x, in float y, out Vector2 result) + { + result.X = (x * M11) + (y * M21) + M31; + result.Y = (x * M12) + (y * M22) + M32; + } + + /// + /// Transforms a vector with the specified x- and y-coordinate component values by this + /// + /// The x-coordinate component value of the vector to transform. + /// The y-coordinate component value of the vector to transform. + /// When this method returns, contains the result of the transformation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Transform(in float x, in float y, ref Vector3 result) + { + result.X = (x * M11) + (y * M21) + M31; + result.Y = (x * M12) + (y * M22) + M32; + } + + /// + /// Calculates the determinant of this . + /// + /// + /// The determinant is calculated by expanding this matrix with a third column whose values are (0, 0, 1). + /// + /// The determinant of this . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Determinant() => (M11 * M22) - (M12 * M21); + + /// + /// Deconstructs this into its translation, rotation, and scale component representations. + /// + /// + /// When this method returns, contains the translation component of this . This parameter is + /// passed uninitialized. + /// + /// + /// When this method returns, contains the rotation component of this . This parameter is + /// passed uninitialized. + /// + /// + /// When this method returns, contains the scale component of this . This parameter is + /// passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Decompose(out Vector2 translation, out float rotation, out Vector2 scale) + { + translation.X = M31; + translation.Y = M31; + + rotation = (float)Math.Atan2(M21, M11); + + var x = M11 * M11 + M21 * M21; + var y = M12 * M12 + M22 * M22; + + var xSign = Math.Sign(x) < 0 ? -1 : 1; + var ySign = Math.Sign(y) < 0 ? -1 : 1; + + scale.X = xSign * (float)Math.Sqrt(x); + scale.Y = ySign * (float)Math.Sqrt(y); + } + + /// + /// Creates a new value for 2D translation, rotation, and scale. + /// + /// Rotation is performed along the z-axis. + /// The amount to translate the matrix by on the x- and y-axis. + /// The amount to rotate, in radians, the matrix along the z-axis. + /// The amount to scale the matrix along the x- and y-axis. + /// The origin point at which to scale and rotate the matrix around. + /// The resulting value created by this method. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateFrom(Vector2 position, in float rotation, Vector2? scale, Vector2? origin) + { + CreateFrom(position, rotation, scale, origin, out Matrix3x2 result); + return result; + } + + /// + /// Creates a new value for 2D translation, rotation, and scale. + /// + /// Rotation is performed along the z-axis. + /// The amount to translate the matrix by on the x- and y-axis. + /// The amount to rotate, in radians, the matrix along the z-axis. + /// The amount to scale the matrix along the x- and y-axis. + /// The origin point at which to scale and rotate the matrix around. + /// + /// When this method returns, contains the resulting value for 2D translation rotation, and + /// scale. This parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CreateFrom(Vector2 position, in float rotation, Vector2? scale, Vector2? origin, out Matrix3x2 result) + { + result = Identity; + + if (origin.HasValue) + { + result.Z = -origin.Value; + } + + if (scale.HasValue) + { + CreateScale(scale.Value, out Matrix3x2 scaleMatrix); + Multiply(result, scaleMatrix, out result); + } + + if (rotation != 0.0f) + { + CreateRotationZ(-rotation, out Matrix3x2 rotationMatrix); + Multiply(result, rotationMatrix, out result); + } + + CreateTranslation(position, out Matrix3x2 translationMatrix); + Multiply(result, translationMatrix, out result); + } + + /// + /// Creates a value for 2D rotation. + /// + /// Rotation is performed along the z-axis. + /// The mount to rotate, in radians, the matrix along the z-axis. + /// The resulting value created by this method. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotationZ(in float radians) + { + CreateRotationZ(radians, out Matrix3x2 result); + return result; + } + + /// + /// Creates a value for 2D rotation. + /// + /// Rotation is performed along the z-axis. + /// The mount to rotate, in radians, the matrix along the z-axis. + /// + /// When this method returns, contains the resulting value for 2D rotation. This parameter + /// is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CreateRotationZ(in float radians, out Matrix3x2 result) + { + var cos = (float)Math.Cos(radians); + var sin = (float)Math.Sin(radians); + + result.M11 = cos; + result.M12 = sin; + + result.M21 = -sin; + result.M22 = cos; + + result.M31 = 0; + result.M32 = 0; + } + + /// + /// Creates a value for 2D scaling. + /// + /// The amount to scale the matrix along the x- and y-axis. + /// The value created by this method. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateScale(in float scale) + { + CreateScale(scale, scale, out Matrix3x2 result); + return result; + } + + /// + /// Creates a value for 2D scaling. + /// + /// The amount to scale the matrix along the x- and y-axis. + /// + /// When this method returns, contains the resulting value for 2D scaling created. This + /// parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CreateScale(in float scale, out Matrix3x2 result) => CreateScale(scale, scale, out result); + + /// + /// Creates a value for 2D scaling. + /// + /// A vector that represents the x- and y-axis scale factors. + /// The value created by this method. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateScale(Vector2 scale) + { + CreateScale(scale.X, scale.Y, out Matrix3x2 result); + return result; + } + + /// + /// Creates a value for 2D scaling. + /// + /// A vector that represents the x- and y-axis scale factors. + /// + /// When this method returns, contains the resulting value for 2D scaling. This parameter + /// is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CreateScale(in Vector2 scale, out Matrix3x2 result) => + CreateScale(scale.X, scale.Y, out result); + + /// + /// Creates a value for 2D scaling. + /// + /// The scale factor for the x-axis. + /// The scale factor for the y-axis. + /// The value created by this method. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateScale(in float xScale, in float yScale) + { + CreateScale(xScale, yScale, out Matrix3x2 result); + return result; + } + + /// + /// Creates a value for 2D scaling. + /// + /// The scale factor for the x-axis. + /// The scale factor for the y-axis. + /// + /// When this method returns, contains the resulting value for 2D scaling. This parameter + /// is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CreateScale(in float xScale, in float yScale, out Matrix3x2 result) + { + result.M11 = xScale; + result.M12 = 0; + + result.M21 = 0; + result.M22 = yScale; + + result.M31 = 0; + result.M32 = 0; + } + + /// + /// Creates a value for 2D translation. + /// + /// The translation vector + /// The resulting value for 2D translation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateTranslation(Vector2 vector) => CreateTranslation(vector.X, vector.Y); + + /// + /// Creates a for 2D translation. + /// + /// The translation vector + /// + /// When this method returns, contains the resulting value for 2D translation. This + /// parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CreateTranslation(Vector2 vector, out Matrix3x2 result) => + CreateTranslation(vector.X, vector.Y, out result); + + /// + /// Creates a value for 2D translation. + /// + /// The X-coordinate of the translation vector. + /// The Y-coordinate of the translation vector. + /// The resulting value for 2D translation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateTranslation(in float x, in float y) + { + CreateTranslation(x, y, out Matrix3x2 result); + return result; + } + + /// + /// Creates a value for 2D translation. + /// + /// The X-coordinate of the translation vector. + /// The Y-coordinate of the translation vector. + /// + /// When this method returns, contains the resulting value. This parameter is passed + /// uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CreateTranslation(in float x, in float y, out Matrix3x2 result) + { + result.M11 = 1; + result.M12 = 0; + + result.M21 = 0; + result.M22 = 1; + + result.M31 = x; + result.M32 = y; + } + + /// + /// Inverts the provided + /// + /// The value to invert. + /// The result of inverting the . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 Invert(Matrix3x2 matrix) + { + Invert(ref matrix); + return matrix; + } + + /// + /// Inverts the provided value. + /// + /// The value to invert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Invert(ref Matrix3x2 matrix) + { + var det = 1.0f / matrix.Determinant(); + + matrix.M11 = matrix.M22 * det; + matrix.M12 = -matrix.M12 * det; + + matrix.M21 = -matrix.M21 * det; + matrix.M22 = matrix.M11 * det; + + matrix.M31 = (matrix.M32 * matrix.M21 - matrix.M31 * matrix.M22) * det; + matrix.M32 = -(matrix.M32 * matrix.M11 - matrix.M31 * matrix.M12) * det; + } + + /// + /// Adds the elements of two values. + /// + /// + /// This operation is performed component-wise. + /// + /// The first value. + /// The second value. + /// The result of the addition. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 Add(Matrix3x2 left, Matrix3x2 right) => left + right; + + /// + /// Adds the elements of two values. + /// + /// + /// This operation is performed component-wise. + /// + /// The first value. + /// The second value. + /// + /// When this method returns, contains the result of the addition. This parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(Matrix3x2 left, Matrix3x2 right, out Matrix3x2 result) => result = Add(left, right); + + /// + /// Subtracts the elements of a from the corresponding elements of another + /// . + /// + /// + /// This operation is performed component-wise. + /// + /// The first value. + /// The second value. + /// The result of the subtraction. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 Subtract(Matrix3x2 left, Matrix3x2 right) => left - right; + + /// + /// Subtracts the elements of a from the corresponding elements of another + /// . + /// + /// + /// This operation is performed component-wise. + /// + /// The first value. + /// The second value. + /// + /// When this method returns, contains the result of the subtraction. This method is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Subtract(Matrix3x2 left, Matrix3x2 right, out Matrix3x2 result) => result = Subtract(left, right); + + /// + /// Multiplies the elements of a by the elements of another . + /// + /// + /// This operation performs matrix multiplication. + /// + /// The first value. + /// The second value. + /// The result of the multiplication. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 Multiply(Matrix3x2 left, Matrix3x2 right) => left * right; + + /// + /// Multiplies the elements of a by the elements of another . + /// + /// + /// This operation performs matrix multiplication. + /// + /// The first value. + /// The second value. + /// + /// When this method returns, contains the result of the multiplication. This parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(Matrix3x2 left, Matrix3x2 right, out Matrix3x2 result) => result = left * right; + + /// + /// Multiplies the elements of a by the elements of another . + /// + /// + /// This operation performs matrix multiplication. + /// + /// The first value. + /// The second value. + /// + /// When this method returns, contains the result of the multiplication. This parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(ref Matrix3x2 left, ref Matrix3x2 right, out Matrix3x2 result) => result = left * right; + + /// + /// Multiplies the elements of a by a scalar value. + /// + /// + /// This operation is performed component-wise. + /// + /// The source . + /// The scalar value. + /// The result of the multiplication. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 Multiply(Matrix3x2 matrix, in float scalar) => matrix * scalar; + + /// + /// Multiplies the elements of a by a scalar value. + /// + /// + /// This operation is performed component-wise. + /// + /// The source . + /// The scalar value. + /// + /// When this method returns, contains the result of the multiplication. This parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(Matrix3x2 matrix, in float scalar, out Matrix3x2 result) => result = Multiply(matrix, scalar); + + /// + /// Divides the elements of a by the elements of another . + /// + /// + /// This operation is performed component-wise. + /// + /// The first value. + /// The second value. + /// The result of the division. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 Divide(Matrix3x2 left, Matrix3x2 right) => left / right; + + /// + /// Divides the elements of a by the elements of another . + /// + /// + /// This operation is performed component-wise. + /// + /// The first value. + /// The second value. + /// + /// When this method returns, contains the result of the division. This parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Divide(Matrix3x2 left, Matrix3x2 right, out Matrix3x2 result) => result = Divide(left, right); + + /// + /// Divides the elements of a by a scalar value. + /// + /// + /// This operation is performed component-wise. + /// + /// The source . + /// The scalar value. + /// The result of the division. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 Divide(Matrix3x2 matrix, in float scalar) => matrix / scalar; + + /// + /// Divides the elements of a by a scalar value. + /// + /// + /// This operation is performed component-wise. + /// + /// The source . + /// The scalar value. + /// + /// When this method returns, contains the result of the division. This parameter is passed uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Divide(Matrix3x2 matrix, in float scalar, out Matrix3x2 result) => result = Divide(matrix, scalar); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly bool Equals([NotNullWhen(true)] object obj) => obj is Matrix3x2 other && Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Matrix3x2 other) => this == other; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(X, Y, Z); + + /// + /// Converts the specified value into a value. + /// + /// + /// The third row of the resulting is set to (0, 0, 1, 0), and the fourth row is set to + /// (, , 0, 1). + /// + /// The resulting value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Matrix ToMatrix() => ToMatrix(0.0f); + + /// + /// Converts the specified value into a value. + /// + /// + /// The third row of the resulting is set to (0, 0, 1, 0), and the fourth row is set to + /// (, , , 1). + /// + /// + /// The depth value to be used for the third row of the resulting value. + /// + /// The resulting value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Matrix ToMatrix(in float depth) + { + ToMatrix(depth, out Matrix result); + return result; + } + + /// + /// Converts the specified value into a value. + /// + /// + /// The third row of the resulting is set to (0, 0, 1, 0), and the fourth row is set to + /// (, , 0, 1). + /// + /// + /// When this method returns, contains the resulting value. This parameter is passed + /// uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToMatrix(out Matrix result) => ToMatrix(0.0f, out result); + + /// + /// Converts the specified value into a value. + /// + /// + /// The third row of the resulting is set to (0, 0, 1, 0), and the fourth row is set to + /// (, , , 1). + /// + /// + /// The depth value to be used for the third row of the resulting value. + /// + /// + /// When this method returns, contains the resulting value. This parameter is passed + /// uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToMatrix(in float depth, out Matrix result) => ToMatrix(this, depth, out result); + + /// + /// Converts the specified value into a value. + /// + /// + /// The third row of the resulting is set to (0, 0, 1, 0), and the fourth row is set to + /// (, , 0, 1). + /// + /// The source value. + /// The resulting value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix ToMatrix(Matrix3x2 matrix) => ToMatrix(matrix, 0.0f); + + /// + /// Converts the specified value into a value. + /// + /// + /// The third row of the resulting is set to (0, 0, 1, 0), and the fourth row is set to + /// (, , , 1). + /// + /// The source value. + /// + /// The depth value to be used for the third row of the resulting value. + /// + /// The resulting value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix ToMatrix(Matrix3x2 matrix, in float depth) + { + ToMatrix(matrix, depth, out Matrix result); + return result; + } + + /// + /// Converts the specified value into a value. + /// + /// + /// The third row of the resulting is set to (0, 0, 1, 0), and the fourth row is set to + /// (, , 0, 1). + /// + /// The source value. + /// + /// When this method returns, contains the resulting value. This parameter is passed + /// uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToMatrix(Matrix3x2 matrix, out Matrix result) => ToMatrix(matrix, 0.0f, out result); + + /// + /// Converts the specified value into a value. + /// + /// + /// The third row of the resulting is set to (0, 0, 1, 0), and the fourth row is set to + /// (, , , 1). + /// + /// The source value. + /// + /// The depth value to be used for the third row of the resulting value. + /// + /// + /// When this method returns, contains the resulting value. This parameter is passed + /// uninitialized. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToMatrix(Matrix3x2 matrix, in float depth, out Matrix result) + { + result.M11 = matrix.M11; + result.M12 = matrix.M12; + result.M13 = 0; + result.M14 = 0; + + result.M21 = matrix.M21; + result.M22 = matrix.M22; + result.M23 = 0; + result.M24 = 0; + + result.M31 = 0; + result.M32 = 0; + result.M33 = 1; + result.M34 = 0; + + result.M41 = matrix.M31; + result.M42 = matrix.M32; + result.M43 = depth; + result.M44 = 1; + } + + /// + /// Converts a value to a value. + /// + /// The source value. + /// The resulting value. + public static implicit operator Matrix(Matrix3x2 matrix) => ToMatrix(matrix, 0.0f); + + /// + /// Checks if two values are equal. + /// + /// + /// Two values are considered equal if all their corresponding elements are equal. + /// + /// The first value. + /// The second value. + /// True if the values are equal; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Matrix3x2 left, Matrix3x2 right) => + (left.X == right.X) && (left.Y == right.Y) && (left.Z == right.Z); + + /// + /// Checks if two values are not equal. + /// + /// + /// Two values are considered not equal if any of their corresponding elements differ. + /// + /// The first value. + /// The second value. + /// True if the values are not equal; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Matrix3x2 left, Matrix3x2 right) => + (left.X != right.X) || (left.Y != right.Y) || (left.Z != right.Z); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 operator +(Matrix3x2 left, Matrix3x2 right) + { + Matrix3x2 result; + + result.M11 = left.M11 + right.M11; + result.M12 = left.M12 + right.M12; + + result.M21 = left.M21 + right.M21; + result.M22 = left.M22 + right.M22; + + result.M31 = left.M31 + right.M31; + result.M32 = left.M32 + right.M32; + + return result; + } + + /// + public static Matrix3x2 operator -(Matrix3x2 left, Matrix3x2 right) + { + Matrix3x2 result; + + result.M11 = left.M11 - right.M11; + result.M12 = left.M12 - right.M12; + + result.M21 = left.M21 - right.M21; + result.M22 = left.M22 - right.M22; + + result.M31 = left.M31 - right.M31; + result.M32 = left.M32 - right.M32; + + return result; + } + + /// + /// Negates the elements of a . + /// + /// + /// This operation is performed component-wise. + /// + /// The source . + /// The result of the negation. + public static Matrix3x2 operator -(Matrix3x2 matrix) + { + Matrix3x2 result; + + result.M11 = -matrix.M11; + result.M12 = -matrix.M12; + + result.M21 = -matrix.M21; + result.M22 = -matrix.M22; + + result.M31 = -matrix.M31; + result.M32 = -matrix.M32; + + return result; + } + + /// + public static Matrix3x2 operator *(Matrix3x2 left, Matrix3x2 right) + { + Matrix3x2 result; + + result.M11 = left.M11 * right.M11 + left.M12 * right.M21; + result.M12 = left.M11 * right.M12 + left.M12 * right.M22; + + result.M21 = left.M21 * right.M11 + left.M22 * right.M21; + result.M22 = left.M21 * right.M12 + left.M22 * right.M22; + + result.M31 = left.M31 * right.M11 + left.M32 * right.M21 + right.M31; + result.M32 = left.M31 * right.M12 + left.M32 * right.M22 + right.M32; + + return result; + } + + /// + public static Matrix3x2 operator *(Matrix3x2 matrix, in float scalar) + { + Matrix3x2 result; + + result.M11 = matrix.M11 * scalar; + result.M12 = matrix.M12 * scalar; + + result.M21 = matrix.M21 * scalar; + result.M22 = matrix.M22 * scalar; + + result.M31 = matrix.M31 * scalar; + result.M32 = matrix.M32 * scalar; + + return result; + } + + /// + public static Matrix3x2 operator /(Matrix3x2 left, Matrix3x2 right) + { + Matrix3x2 result; + + result.M11 = left.M11 / right.M11; + result.M12 = left.M12 / right.M12; + + result.M21 = left.M21 / right.M21; + result.M22 = left.M22 / right.M22; + + result.M31 = left.M31 / right.M31; + result.M32 = left.M32 / right.M32; + + return result; + } + + /// + public static Matrix3x2 operator /(Matrix3x2 matrix, in float scalar) + { + var num = 1.0f / scalar; + Matrix3x2 result; + + result.M11 = matrix.M11 * num; + result.M12 = matrix.M12 * num; + + result.M21 = matrix.M21 * num; + result.M22 = matrix.M22 * num; + + result.M31 = matrix.M31 * num; + result.M32 = matrix.M32 * num; + + return result; + } + + /// + /// Returns a string representation of this + /// + /// The string representation of this + public override string ToString() => + $"{{M11:{M11} M12:{M12}}} {{M21:{M21} M22:{M22}}} {{M31:{M31} M32:{M32}}}"; + + internal string DebugDisplayString() + { + if (this == Identity) + { + return nameof(Identity); + } + + Decompose(out Vector2 translation, out float rotation, out Vector2 scale); + return $"T:({translation.X:0.##},{translation.Y:0.##}), R:{MathHelper.ToDegrees(rotation):0.##}°, S:({scale.X:0.##},{scale.Y:0.##})"; + } +} diff --git a/source/MonoGame.Extended/Math/OrientedRectangle.cs b/source/MonoGame.Extended/Math/OrientedRectangle.cs index 1439e9f7e..9bd909f2d 100644 --- a/source/MonoGame.Extended/Math/OrientedRectangle.cs +++ b/source/MonoGame.Extended/Math/OrientedRectangle.cs @@ -29,9 +29,9 @@ public struct OrientedRectangle : IEquatable, IShapeF public Vector2 Radii; /// - /// The rotation matrix of the bounding rectangle . + /// The rotation matrix of the bounding rectangle . /// - public Matrix2 Orientation; + public Matrix3x2 Orientation; /// /// Initializes a new instance of the structure from the specified centre @@ -39,8 +39,8 @@ public struct OrientedRectangle : IEquatable, IShapeF /// /// The centre . /// The radii . - /// The orientation . - public OrientedRectangle(Point2 center, Size2 radii, Matrix2 orientation) + /// The orientation . + public OrientedRectangle(Point2 center, Size2 radii, Matrix3x2 orientation) { Center = center; Radii = radii; @@ -82,15 +82,15 @@ public Point2 Position /// transformed by . /// /// The to transform. - /// The transformation. + /// The transformation. /// A new . - public static OrientedRectangle Transform(OrientedRectangle rectangle, ref Matrix2 transformMatrix) + public static OrientedRectangle Transform(OrientedRectangle rectangle, ref Matrix3x2 transformMatrix) { Transform(ref rectangle, ref transformMatrix, out var result); return result; } - private static void Transform(ref OrientedRectangle rectangle, ref Matrix2 transformMatrix, out OrientedRectangle result) + private static void Transform(ref OrientedRectangle rectangle, ref Matrix3x2 transformMatrix, out OrientedRectangle result) { PrimitivesHelper.TransformOrientedRectangle( ref rectangle.Center, @@ -169,14 +169,14 @@ public static explicit operator OrientedRectangle(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 OrientedRectangle(centre, radii, Matrix2.Identity); + return new OrientedRectangle(centre, radii, Matrix3x2.Identity); } public static explicit operator RectangleF(OrientedRectangle orientedRectangle) { var topLeft = -orientedRectangle.Radii; var rectangle = new RectangleF(topLeft, orientedRectangle.Radii * 2); - var orientation = orientedRectangle.Orientation * Matrix2.CreateTranslation(orientedRectangle.Center); + var orientation = orientedRectangle.Orientation * Matrix3x2.CreateTranslation(orientedRectangle.Center); return RectangleF.Transform(rectangle, ref orientation); } diff --git a/source/MonoGame.Extended/Math/PrimitivesHelper.cs b/source/MonoGame.Extended/Math/PrimitivesHelper.cs index ed082708e..0180943ba 100644 --- a/source/MonoGame.Extended/Math/PrimitivesHelper.cs +++ b/source/MonoGame.Extended/Math/PrimitivesHelper.cs @@ -60,7 +60,7 @@ internal static void CreateRectangleFromPoints(IReadOnlyList points, out } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void TransformRectangle(ref Point2 center, ref Vector2 halfExtents, ref Matrix2 transformMatrix) + internal static void TransformRectangle(ref Point2 center, ref Vector2 halfExtents, ref Matrix3x2 transformMatrix) { // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 86-87 @@ -74,8 +74,8 @@ internal static void TransformRectangle(ref Point2 center, ref Vector2 halfExten [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void TransformOrientedRectangle( ref Point2 center, - ref Matrix2 orientation, - ref Matrix2 transformMatrix) + ref Matrix3x2 orientation, + ref Matrix3x2 transformMatrix) { // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.4; Oriented Bounding Boxes (OBBs), pg 101-105. diff --git a/source/MonoGame.Extended/Math/RectangleF.cs b/source/MonoGame.Extended/Math/RectangleF.cs index 1a1d6859e..77d91ad97 100644 --- a/source/MonoGame.Extended/Math/RectangleF.cs +++ b/source/MonoGame.Extended/Math/RectangleF.cs @@ -223,7 +223,7 @@ public static RectangleF CreateFrom(IReadOnlyList points) /// /// Computes the from the specified transformed by - /// the specified . + /// the specified . /// /// The rectangle to be transformed. /// The transform matrix. @@ -240,7 +240,7 @@ public static RectangleF CreateFrom(IReadOnlyList points) /// /// public static void Transform(ref RectangleF rectangle, - ref Matrix2 transformMatrix, out RectangleF result) + ref Matrix3x2 transformMatrix, out RectangleF result) { var center = rectangle.Center; var halfExtents = (Vector2)rectangle.Size * 0.5f; @@ -256,7 +256,7 @@ public static void Transform(ref RectangleF rectangle, /// /// Computes the from the specified transformed by /// the - /// specified . + /// specified . /// /// The bounding rectangle. /// The transform matrix. @@ -271,7 +271,7 @@ public static void Transform(ref RectangleF rectangle, /// not desired. /// /// - public static RectangleF Transform(RectangleF rectangle, ref Matrix2 transformMatrix) + public static RectangleF Transform(RectangleF rectangle, ref Matrix3x2 transformMatrix) { RectangleF result; Transform(ref rectangle, ref transformMatrix, out result); diff --git a/source/MonoGame.Extended/Math/ShapeF.cs b/source/MonoGame.Extended/Math/ShapeF.cs index 7478f759d..662930380 100644 --- a/source/MonoGame.Extended/Math/ShapeF.cs +++ b/source/MonoGame.Extended/Math/ShapeF.cs @@ -74,7 +74,7 @@ public static bool Intersects(CircleF circle, RectangleF rectangle) /// 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 = Matrix3x2.CreateRotationZ(orientedRectangle.Orientation.Rotation); var circleCenterInRectangleSpace = rotation.Transform(circle.Center - orientedRectangle.Center); var circleInRectangleSpace = new CircleF(circleCenterInRectangleSpace, circle.Radius); var boundingRectangle = new BoundingRectangle(new Point2(), orientedRectangle.Radii); diff --git a/source/MonoGame.Extended/Transform.cs b/source/MonoGame.Extended/Transform.cs index b0a0be4ee..2f6eae5b5 100644 --- a/source/MonoGame.Extended/Transform.cs +++ b/source/MonoGame.Extended/Transform.cs @@ -45,10 +45,10 @@ internal BaseTransform() } /// - /// Gets the model-to-local space . + /// Gets the model-to-local space . /// /// - /// The model-to-local space . + /// The model-to-local space . /// public TMatrix LocalMatrix { @@ -60,10 +60,10 @@ public TMatrix LocalMatrix } /// - /// Gets the local-to-world space . + /// Gets the local-to-world space . /// /// - /// The local-to-world space . + /// The local-to-world space . /// public TMatrix WorldMatrix { @@ -106,9 +106,9 @@ public BaseTransform Parent public event Action TranformUpdated; // observer pattern for after the world (or local) matrix was re-calculated /// - /// Gets the model-to-local space . + /// Gets the model-to-local space . /// - /// The model-to-local space . + /// The model-to-local space . public void GetLocalMatrix(out TMatrix matrix) { RecalculateLocalMatrixIfNecessary(); @@ -116,9 +116,9 @@ public void GetLocalMatrix(out TMatrix matrix) } /// - /// Gets the local-to-world space . + /// Gets the local-to-world space . /// - /// The local-to-world space . + /// The local-to-world space . public void GetWorldMatrix(out TMatrix matrix) { RecalculateWorldMatrixIfNecessary(); @@ -189,7 +189,7 @@ private void RecalculateLocalMatrixIfNecessary() /// /// Represents the position, rotation, and scale of a two-dimensional game object. /// - /// + /// /// /// /// @@ -200,7 +200,7 @@ private void RecalculateLocalMatrixIfNecessary() /// objects hierarchically. /// /// - public class Transform2 : BaseTransform, IMovable, IRotatable, IScalable + public class Transform2 : BaseTransform, IMovable, IRotatable, IScalable { private Vector2 _position; private float _rotation; @@ -298,12 +298,12 @@ public Vector2 Scale } } - protected internal override void RecalculateWorldMatrix(ref Matrix2 localMatrix, out Matrix2 matrix) + protected internal override void RecalculateWorldMatrix(ref Matrix3x2 localMatrix, out Matrix3x2 matrix) { if (Parent != null) { Parent.GetWorldMatrix(out matrix); - Matrix2.Multiply(ref localMatrix, ref matrix, out matrix); + Matrix3x2.Multiply(ref localMatrix, ref matrix, out matrix); } else { @@ -311,11 +311,11 @@ protected internal override void RecalculateWorldMatrix(ref Matrix2 localMatrix, } } - protected internal override void RecalculateLocalMatrix(out Matrix2 matrix) + protected internal override void RecalculateLocalMatrix(out Matrix3x2 matrix) { - matrix = Matrix2.CreateScale(_scale) * - Matrix2.CreateRotationZ(_rotation) * - Matrix2.CreateTranslation(_position); + matrix = Matrix3x2.CreateScale(_scale) * + Matrix3x2.CreateRotationZ(_rotation) * + Matrix3x2.CreateTranslation(_position); } public override string ToString() @@ -453,4 +453,4 @@ public override string ToString() { return $"Position: {Position}, Rotation: {Rotation}, Scale: {Scale}"; } } -} \ No newline at end of file +} diff --git a/tests/MonoGame.Extended.Tests/Math/Matrix3x2.cs b/tests/MonoGame.Extended.Tests/Math/Matrix3x2.cs new file mode 100644 index 000000000..56ab1e7b1 --- /dev/null +++ b/tests/MonoGame.Extended.Tests/Math/Matrix3x2.cs @@ -0,0 +1,20 @@ +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Tests; + +public sealed class Matrix3x2Tests +{ + [Fact] + public void ConstructorTest() + { + Vector2 x = new Vector2(1, 2); + Vector2 y = new Vector2(3, 4); + Vector2 z = new Vector2(5, 6); + + var matrix = new Matrix3x2(x.X, x.Y, y.X, y.Y, z.X, z.Y); + + Assert.Equal(x, matrix.X); + Assert.Equal(y, matrix.Y); + Assert.Equal(z, matrix.Z); + } +} diff --git a/tests/MonoGame.Extended.Tests/Primitives/BoundingRectangleTests.cs b/tests/MonoGame.Extended.Tests/Primitives/BoundingRectangleTests.cs index 6f95cf573..2d6dc5b1c 100644 --- a/tests/MonoGame.Extended.Tests/Primitives/BoundingRectangleTests.cs +++ b/tests/MonoGame.Extended.Tests/Primitives/BoundingRectangleTests.cs @@ -5,7 +5,7 @@ //namespace MonoGame.Extended.Tests.Primitives //{ -// +// // public class BoundingRectangleTests // { // public IEnumerable ConstructorTestCases @@ -88,11 +88,11 @@ // get // { // yield return -// new TestCaseData(new BoundingRectangle(), Matrix2.Identity, new BoundingRectangle()).SetName( +// new TestCaseData(new BoundingRectangle(), Matrix3x2.Identity, new BoundingRectangle()).SetName( // "The bounding rectangle created from the empty bounding rectangle transformed by the identity matrix is the empty bounding rectangle.") // ; // yield return -// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(20, 20)), Matrix2.CreateScale(2), new BoundingRectangle(new Point2(0, 0), new Size2(40, 40))).SetName( +// new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(20, 20)), Matrix3x2.CreateScale(2), new BoundingRectangle(new Point2(0, 0), new Size2(40, 40))).SetName( // "The bounding rectangle created from a non-empty bounding rectangle transformed by a non-identity matrix is the expected bounding rectangle.") // ; // } @@ -100,7 +100,7 @@ // [Fact] // [TestCaseSource(nameof(CreateFromTransformedTestCases))] -// public void CreateFromTransformed(BoundingRectangle boundingRectangle, Matrix2 transformMatrix, +// public void CreateFromTransformed(BoundingRectangle boundingRectangle, Matrix3x2 transformMatrix, // BoundingRectangle expectedBoundingRectangle) // { // var actualBoundingRectangle = BoundingRectangle.Transform(boundingRectangle, ref transformMatrix); diff --git a/tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs b/tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs index e3b40c8c9..44b6b9b72 100644 --- a/tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs +++ b/tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs @@ -10,11 +10,11 @@ public class OrientedRectangleTests [Fact] public void Initializes_oriented_rectangle() { - var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10)); + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix3x2(5, 6, 7, 8, 9, 10)); 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); + Assert.Equal(new Matrix3x2(5, 6, 7, 8, 9, 10), rectangle.Orientation); CollectionAssert.Equal( new List { @@ -31,14 +31,14 @@ public void Initializes_oriented_rectangle() new object[] { "empty compared with empty is true", - new OrientedRectangle(Point2.Zero, Size2.Empty, Matrix2.Identity), - new OrientedRectangle(Point2.Zero, Size2.Empty, Matrix2.Identity) + new OrientedRectangle(Point2.Zero, Size2.Empty, Matrix3x2.Identity), + new OrientedRectangle(Point2.Zero, Size2.Empty, Matrix3x2.Identity) }, new object[] { "initialized compared with initialized true", - new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10)), - new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10)) + new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix3x2(5, 6, 7, 8, 9, 10)), + new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix3x2(5, 6, 7, 8, 9, 10)) } }; @@ -57,8 +57,8 @@ public class Transform [Fact] public void Center_point_is_not_translated() { - var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(), Matrix2.Identity); - var transform = Matrix2.Identity; + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(), Matrix3x2.Identity); + var transform = Matrix3x2.Identity; var result = OrientedRectangle.Transform(rectangle, ref transform); @@ -68,8 +68,8 @@ public void Center_point_is_not_translated() [Fact] public void Center_point_is_translated() { - var rectangle = new OrientedRectangle(new Point2(0, 0), new Size2(), new Matrix2()); - var transform = Matrix2.CreateTranslation(1, 2); + var rectangle = new OrientedRectangle(new Point2(0, 0), new Size2(), new Matrix3x2()); + var transform = Matrix3x2.CreateTranslation(1, 2); var result = OrientedRectangle.Transform(rectangle, ref transform); @@ -79,8 +79,8 @@ public void Center_point_is_translated() [Fact] public void Radii_is_not_changed_by_identity_transform() { - var rectangle = new OrientedRectangle(new Point2(), new Size2(10, 20), new Matrix2()); - var transform = Matrix2.Identity; + var rectangle = new OrientedRectangle(new Point2(), new Size2(10, 20), new Matrix3x2()); + var transform = Matrix3x2.Identity; var result = OrientedRectangle.Transform(rectangle, ref transform); @@ -90,8 +90,8 @@ public void Radii_is_not_changed_by_identity_transform() [Fact] public void Radii_is_not_changed_by_translation() { - var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(10, 20), new Matrix2()); - var transform = Matrix2.CreateTranslation(1, 2); + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(10, 20), new Matrix3x2()); + var transform = Matrix3x2.CreateTranslation(1, 2); var result = OrientedRectangle.Transform(rectangle, ref transform); @@ -101,22 +101,22 @@ public void Radii_is_not_changed_by_translation() [Fact] public void Orientation_is_rotated_45_degrees_left() { - var rectangle = new OrientedRectangle(new Point2(), new Size2(), Matrix2.Identity); - var transform = Matrix2.CreateRotationZ(MathHelper.PiOver4); + var rectangle = new OrientedRectangle(new Point2(), new Size2(), Matrix3x2.Identity); + var transform = Matrix3x2.CreateRotationZ(MathHelper.PiOver4); var result = OrientedRectangle.Transform(rectangle, ref transform); Assert.Equal(new Point2(), result.Center); Assert.Equal(new Vector2(), result.Radii); - Assert.Equal(Matrix2.CreateRotationZ(MathHelper.PiOver4), result.Orientation); + Assert.Equal(Matrix3x2.CreateRotationZ(MathHelper.PiOver4), result.Orientation); } [Fact] public void Orientation_is_rotated_to_45_degrees_from_180() { - var rectangle = new OrientedRectangle(new Point2(), new Size2(), Matrix2.CreateRotationZ(MathHelper.Pi)); - var transform = Matrix2.CreateRotationZ(-3 * MathHelper.PiOver4); - var expectedOrientation = Matrix2.CreateRotationZ(MathHelper.PiOver4); + var rectangle = new OrientedRectangle(new Point2(), new Size2(), Matrix3x2.CreateRotationZ(MathHelper.Pi)); + var transform = Matrix3x2.CreateRotationZ(-3 * MathHelper.PiOver4); + var expectedOrientation = Matrix3x2.CreateRotationZ(MathHelper.PiOver4); var result = OrientedRectangle.Transform(rectangle, ref transform); @@ -133,8 +133,8 @@ public void Orientation_is_rotated_to_45_degrees_from_180() [Fact] public void Points_are_same_as_center() { - var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(), Matrix2.Identity); - var transform = Matrix2.Identity; + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(), Matrix3x2.Identity); + var transform = Matrix3x2.Identity; var result = OrientedRectangle.Transform(rectangle, ref transform); @@ -152,8 +152,8 @@ public void Points_are_same_as_center() [Fact] public void Points_are_translated() { - var rectangle = new OrientedRectangle(new Point2(0, 0), new Size2(2, 4), Matrix2.Identity); - var transform = Matrix2.CreateTranslation(10, 20); + var rectangle = new OrientedRectangle(new Point2(0, 0), new Size2(2, 4), Matrix3x2.Identity); + var transform = Matrix3x2.CreateTranslation(10, 20); var result = OrientedRectangle.Transform(rectangle, ref transform); @@ -207,11 +207,11 @@ public void Applies_rotation_and_translation() * : * : */ - var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(2, 4), Matrix2.Identity); + var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(2, 4), Matrix3x2.Identity); var transform = - Matrix2.CreateRotationZ(MathHelper.PiOver2) + Matrix3x2.CreateRotationZ(MathHelper.PiOver2) * - Matrix2.CreateTranslation(10, 20); + Matrix3x2.CreateTranslation(10, 20); var result = OrientedRectangle.Transform(rectangle, ref transform); @@ -219,7 +219,7 @@ public void Applies_rotation_and_translation() 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); + Assert.Equal(Matrix3x2.CreateRotationZ(MathHelper.PiOver2), result.Orientation); CollectionAssert.Equal( new List { diff --git a/tests/MonoGame.Extended.Tests/Primitives/RectangleFTests.cs b/tests/MonoGame.Extended.Tests/Primitives/RectangleFTests.cs index 9121d6ada..ddc6db756 100644 --- a/tests/MonoGame.Extended.Tests/Primitives/RectangleFTests.cs +++ b/tests/MonoGame.Extended.Tests/Primitives/RectangleFTests.cs @@ -38,7 +38,7 @@ public class Transform public void Center_point_is_not_translated() { var rectangle = new RectangleF(new Point2(0, 0), new Size2(20, 30)); - var transform = Matrix2.Identity; + var transform = Matrix3x2.Identity; var result = RectangleF.Transform(rectangle, ref transform); @@ -49,7 +49,7 @@ public void Center_point_is_not_translated() public void Center_point_is_translated() { var rectangleF = new RectangleF(new Point2(0, 0), new Size2(20, 30)); - var transform = Matrix2.CreateTranslation(1, 2); + var transform = Matrix3x2.CreateTranslation(1, 2); var result = RectangleF.Transform(rectangleF, ref transform); @@ -60,7 +60,7 @@ public void Center_point_is_translated() public void Size_is_not_changed_by_identity_transform() { var rectangle = new RectangleF(new Point2(0, 0), new Size2(20, 30)); - var transform = Matrix2.Identity; + var transform = Matrix3x2.Identity; var result = RectangleF.Transform(rectangle, ref transform); @@ -71,7 +71,7 @@ public void Size_is_not_changed_by_identity_transform() public void Size_is_not_changed_by_translation() { var rectangle = new RectangleF(new Point2(0, 0), new Size2(20, 30)); - var transform = Matrix2.CreateTranslation(1, 2); + var transform = Matrix3x2.CreateTranslation(1, 2); var result = RectangleF.Transform(rectangle, ref transform); @@ -119,9 +119,9 @@ public void Applies_rotation_and_translation() */ var rectangle = new RectangleF(new Point2(0, 0), new Size2(2, 4)); var transform = - Matrix2.CreateRotationZ(MathHelper.PiOver2) + Matrix3x2.CreateRotationZ(MathHelper.PiOver2) * - Matrix2.CreateTranslation(10, 20); + Matrix3x2.CreateTranslation(10, 20); var result = RectangleF.Transform(rectangle, ref transform); diff --git a/tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs b/tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs index 37b2fc4c2..54c3ca8bf 100644 --- a/tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs +++ b/tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs @@ -99,7 +99,7 @@ public void Axis_aligned_rectangle_intersects_circle() * : * : */ - IShapeF rectangle = new OrientedRectangle(Point2.Zero, new Size2(1, 1), Matrix2.Identity); + IShapeF rectangle = new OrientedRectangle(Point2.Zero, new Size2(1, 1), Matrix3x2.Identity); var circle = new CircleF(Point2.Zero, 1); Assert.True(rectangle.Intersects(circle)); @@ -117,7 +117,7 @@ public void Axis_aligned_rectangle_does_not_intersect_circle_in_top_left() * : * : */ - IShapeF rectangle = new OrientedRectangle(Point2.Zero, new Size2(1, 1), Matrix2.Identity); + IShapeF rectangle = new OrientedRectangle(Point2.Zero, new Size2(1, 1), Matrix3x2.Identity); var circle = new CircleF(new Point2(-2, 1), .99f); Assert.False(rectangle.Intersects(circle)); @@ -135,7 +135,7 @@ public void Rectangle_rotated_45_degrees_does_not_intersect_circle_in_bottom_rig * * * * :* * */ - IShapeF rectangle = new OrientedRectangle(new Point2(-1, 1), new Size2(1.42f, 1.42f), Matrix2.CreateRotationZ(-MathHelper.PiOver4)); + IShapeF rectangle = new OrientedRectangle(new Point2(-1, 1), new Size2(1.42f, 1.42f), Matrix3x2.CreateRotationZ(-MathHelper.PiOver4)); var circle = new CircleF(new Point2(1, -1), 1.4f); Assert.False(rectangle.Intersects(circle)); @@ -153,7 +153,7 @@ public void Axis_aligned_rectangle_does_not_intersect_rectangle() * :** * : */ - IShapeF rectangle = new OrientedRectangle(new Point2(-1, 0), new Size2(1, 1), Matrix2.Identity); + IShapeF rectangle = new OrientedRectangle(new Point2(-1, 0), new Size2(1, 1), Matrix3x2.Identity); var rect = new RectangleF(new Point2(0.001f, 0), new Size2(2, 2)); Assert.False(rectangle.Intersects(rect)); @@ -171,7 +171,7 @@ public void Axis_aligned_rectangle_intersects_rectangle() * :** * : */ - IShapeF rectangle = new OrientedRectangle(new Point2(-1, 0), new Size2(1, 1), Matrix2.Identity); + IShapeF rectangle = new OrientedRectangle(new Point2(-1, 0), new Size2(1, 1), Matrix3x2.Identity); var rect = new RectangleF(new Point2(0, 0), new Size2(2, 2)); Assert.True(rectangle.Intersects(rect));