BoundingVolumeTransform.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using System;
  2. using Microsoft.Xna.Framework;
  3. namespace MonoGame.Extended.Collisions;
  4. /// <summary>
  5. /// Provides methods for transforming bounding volumes by matrices, rotations, and translations.
  6. /// </summary>
  7. public static class BoundingVolumeTransform
  8. {
  9. #region Circle Transformations
  10. /// <summary>
  11. /// Transforms a bounding circle by a 2D transformation matrix.
  12. /// </summary>
  13. /// <param name="circle">The circle to transform.</param>
  14. /// <param name="transform">The transformation matrix.</param>
  15. /// <returns>The transformed circle.</returns>
  16. /// <remarks>
  17. /// Circles are rotationally invariant; other the translation component is applied. <br/>
  18. /// If uniform scaling is needed, scale the radius separately after transformation. <br/>
  19. /// Non-uniform scaling is not supported (would produce an ellipse).
  20. /// </remarks>
  21. public static BoundingCircle Transform(in BoundingCircle circle, in Matrix3x2 transform)
  22. {
  23. // Extract translation from matrix
  24. Vector2 translation = new Vector2(transform.M31, transform.M32);
  25. return new BoundingCircle(circle.Center + translation, circle.Radius);
  26. }
  27. /// <summary>
  28. /// Translates a bounding circle by a vector.
  29. /// </summary>
  30. /// <param name="circle">The circle to translate.</param>
  31. /// <param name="translation">The translation vector.</param>
  32. /// <returns>The translated circle.</returns>
  33. public static BoundingCircle Translate(in BoundingCircle circle, Vector2 translation)
  34. {
  35. return new BoundingCircle(circle.Center + translation, circle.Radius);
  36. }
  37. #endregion
  38. #region AABB Transformations
  39. /// <summary>
  40. /// Transforms an AABB by a 2D transformation matrix, producing a new axis-aligned rectangle that bounds
  41. /// the transformed original.
  42. /// </summary>
  43. /// <param name="aabb">The AABB to transform.</param>
  44. /// <param name="transform">The transformation matrix.</param>
  45. /// <returns>A new AABB that contains the transformed original.</returns>
  46. /// <remarks>
  47. /// Rotation causes AABBs to lose tightness. the result is conservative (larger). <br/>
  48. /// For frequently updated AABBs, cache the local-space AABB and recompute from it rather than from the previous
  49. /// world-space AABB to avoid accumulating growth.
  50. /// </remarks>
  51. public static BoundingRectangle Transform(in BoundingRectangle aabb, in Matrix3x2 transform)
  52. {
  53. Vector2 center = aabb.Center;
  54. Vector2 halfExtents = aabb.HalfExtents;
  55. Vector2 transformedCenter = Vector2.Transform(center, transform);
  56. // For each axis, sum the absolute values of the transformed half-extents
  57. // This gives us the new AABB that bounds the rotated original AABB
  58. float newHalfWidth = MathF.Abs(transform.M11) * halfExtents.X
  59. + MathF.Abs(transform.M12) * halfExtents.Y;
  60. float newHalfHeight = MathF.Abs(transform.M21) * halfExtents.X
  61. + MathF.Abs(transform.M22) * halfExtents.Y;
  62. Vector2 newHalfExtents = new Vector2(newHalfWidth, newHalfHeight);
  63. return BoundingRectangle.FromCenterAndExtents(transformedCenter, newHalfExtents);
  64. }
  65. /// <summary>
  66. /// Transforms an AABB by separate rotation and translation components.
  67. /// </summary>
  68. /// <param name="aabb">The local-space AABB.</param>
  69. /// <param name="rotation">The rotation angle in radians.</param>
  70. /// <param name="translation">The translation vector.</param>
  71. /// <returns>The transformed AABB in world space.</returns>
  72. public static BoundingRectangle TransformFromRotation(in BoundingRectangle aabb, float rotation, Vector2 translation)
  73. {
  74. float cos = MathF.Cos(rotation);
  75. float sin = MathF.Sin(rotation);
  76. Matrix3x2 transform = new Matrix3x2(
  77. cos, sin,
  78. -sin, cos,
  79. translation.X, translation.Y
  80. );
  81. return Transform(aabb, transform);
  82. }
  83. /// <summary>
  84. /// Translates an AABB by a vector.
  85. /// </summary>
  86. /// <param name="aabb">The AABB to translate.</param>
  87. /// <param name="translation">The translation vector.</param>
  88. /// <returns>The translated AABB.</returns>
  89. public static BoundingRectangle Translate(in BoundingRectangle aabb, Vector2 translation)
  90. {
  91. return new BoundingRectangle(aabb.Min + translation, aabb.Max + translation);
  92. }
  93. #endregion
  94. #region OBB Transformations
  95. /// <summary>
  96. /// Transforms an oriented bounding rectangle by a 2D transformation matrix.
  97. /// </summary>
  98. /// <param name="obb">The OBB to transform.</param>
  99. /// <param name="transform">The transformation matrix.</param>
  100. /// <returns>The transformed OBB</returns>
  101. /// <remarks>
  102. /// Transforms center point and rotates the local axes. <br/>
  103. /// OBBs maintain tightness under transformation (unlike AABBs).
  104. /// </remarks>
  105. public static OrientedBoundingRectangle Transform(in OrientedBoundingRectangle obb, in Matrix3x2 transform)
  106. {
  107. Vector2 transformedCenter = Vector2.Transform(obb.Center, transform);
  108. // Transform axes (rotation only, no translation)
  109. Vector2 transformedAxisX = Vector2.TransformNormal(obb.AxisX, transform);
  110. Vector2 transformedAxisY = Vector2.TransformNormal(obb.AxisY, transform);
  111. transformedAxisX = GeometricPrimitives.SafeNormalize(transformedAxisX);
  112. transformedAxisY = GeometricPrimitives.SafeNormalize(transformedAxisY);
  113. return new OrientedBoundingRectangle(transformedCenter, transformedAxisX, transformedAxisY, obb.HalfExtents);
  114. }
  115. /// <summary>
  116. /// Rotates an obb by an angle in radians.
  117. /// </summary>
  118. /// <param name="obb">The OBB to rotate.</param>
  119. /// <param name="radians">The rotation angle in radians, measured counter-clockwise.</param>
  120. /// <returns>The rotated OBB.</returns>
  121. public static OrientedBoundingRectangle Rotate(in OrientedBoundingRectangle obb, float radians)
  122. {
  123. float cos = MathF.Cos(radians);
  124. float sin = MathF.Sin(radians);
  125. // Rotate axes
  126. Vector2 newAxisX = new Vector2(
  127. cos * obb.AxisX.X - sin * obb.AxisX.Y,
  128. sin * obb.AxisX.X + cos * obb.AxisX.Y
  129. );
  130. Vector2 newAxisY = new Vector2(
  131. cos * obb.AxisY.X - sin * obb.AxisY.Y,
  132. sin * obb.AxisY.X + cos * obb.AxisY.Y
  133. );
  134. newAxisX = GeometricPrimitives.SafeNormalize(newAxisX);
  135. newAxisY = GeometricPrimitives.SafeNormalize(newAxisY);
  136. return new OrientedBoundingRectangle(obb.Center, newAxisX, newAxisY, obb.HalfExtents);
  137. }
  138. /// <summary>
  139. /// Translates an OBB by a vector.
  140. /// </summary>
  141. /// <param name="obb">The OBB to translate.</param>
  142. /// <param name="translation">The translation vector.</param>
  143. /// <returns>The translated OBB.</returns>
  144. public static OrientedBoundingRectangle Translate(in OrientedBoundingRectangle obb, Vector2 translation)
  145. {
  146. return new OrientedBoundingRectangle(obb.Center + translation, obb.AxisX, obb.AxisY, obb.HalfExtents);
  147. }
  148. #endregion
  149. }