using System; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; namespace Robust.Shared.Maths { /// /// This type contains a and a rotation in world space. /// [Serializable] public struct Box2Rotated : IEquatable { public Box2 Box; public Angle Rotation; /// /// The point about which the rotation occurs. /// public Vector2 Origin; /// /// A 1x1 unit box with the origin centered and identity rotation. /// public static readonly Box2Rotated UnitCentered = new(Box2.UnitCentered, Angle.Zero, Vector2.Zero); public readonly Vector2 BottomRight => Origin + Rotation.RotateVec(Box.BottomRight - Origin); public readonly Vector2 TopLeft => Origin + Rotation.RotateVec(Box.TopLeft - Origin); public readonly Vector2 TopRight => Origin + Rotation.RotateVec(Box.TopRight - Origin); public readonly Vector2 BottomLeft => Origin + Rotation.RotateVec(Box.BottomLeft - Origin); public Box2Rotated(Vector2 bottomLeft, Vector2 topRight) : this(new Box2(bottomLeft, topRight)) { } public Box2Rotated(Box2 box) : this(box, 0) { } public Box2Rotated(Box2 box, Angle rotation) : this(box, rotation, Vector2.Zero) { } public Box2Rotated(Box2 box, Angle rotation, Vector2 origin) { Box = box; Rotation = rotation; Origin = origin; } /// /// calculates the smallest AABB that will encompass the rotated box. The AABB is in local space. /// public readonly Box2 CalcBoundingBox() { if (Sse.IsSupported && NumericsHelpers.Enabled) { return CalcBoundingBoxSse(); } return CalcBoundingBoxSlow(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal readonly unsafe Box2 CalcBoundingBoxSse() { Vector128 boxVec; fixed (float* lPtr = &Box.Left) { boxVec = Sse.LoadVector128(lPtr); } var originX = Vector128.Create(Origin.X); var originY = Vector128.Create(Origin.Y); var cos = Vector128.Create((float) Math.Cos(Rotation)); var sin = Vector128.Create((float) Math.Sin(Rotation)); var allX = Sse.Shuffle(boxVec, boxVec, 0b10_10_00_00); var allY = Sse.Shuffle(boxVec, boxVec, 0b01_11_11_01); allX = Sse.Subtract(allX, originX); allY = Sse.Subtract(allY, originY); var modX = Sse.Subtract(Sse.Multiply(allX, cos), Sse.Multiply(allY, sin)); var modY = Sse.Add(Sse.Multiply(allX, sin), Sse.Multiply(allY, cos)); allX = Sse.Add(modX, originX); allY = Sse.Add(modY, originY); var l = SimdHelpers.MinHorizontalSse(allX); var b = SimdHelpers.MinHorizontalSse(allY); var r = SimdHelpers.MaxHorizontalSse(allX); var t = SimdHelpers.MaxHorizontalSse(allY); var lb = Sse.UnpackLow(l, b); var rt = Sse.UnpackLow(r, t); var lbrt = Sse.Shuffle(lb, rt, 0b11_10_01_00); return Unsafe.As, Box2>(ref lbrt); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal readonly unsafe Box2 CalcBoundingBoxSlow() { Span allX = stackalloc float[4]; Span allY = stackalloc float[4]; (allX[0], allY[0]) = BottomLeft; (allX[1], allY[1]) = TopRight; (allX[2], allY[2]) = TopLeft; (allX[3], allY[3]) = BottomRight; var X0 = allX[0]; var X1 = allX[0]; for (int i = 1; i < allX.Length; i++) { if (allX[i] > X1) { X1 = allX[i]; continue; } if (allX[i] < X0) { X0 = allX[i]; } } var Y0 = allY[0]; var Y1 = allY[0]; for (int i = 1; i < allY.Length; i++) { if (allY[i] > Y1) { Y1 = allY[i]; continue; } if (allY[i] < Y0) { Y0 = allY[i]; } } return new Box2(X0, Y0, X1, Y1); } public bool Contains(Vector2 worldPoint) { // Get the worldpoint in our frame of reference so we can do a faster AABB check. var localPoint = GetLocalPoint(worldPoint); return Box.Contains(localPoint); } /// /// Convert a point in world-space coordinates to our local coordinates. /// private Vector2 GetLocalPoint(Vector2 point) { return Origin + (-Rotation).RotateVec(point - Origin); } #region Equality /// public readonly bool Equals(Box2Rotated other) { return Box.Equals(other.Box) && Rotation.Equals(other.Rotation); } /// public override readonly bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; return obj is Box2Rotated other && Equals(other); } /// public override readonly int GetHashCode() { unchecked { return (Box.GetHashCode() * 397) ^ Rotation.GetHashCode(); } } /// /// Check for equality by value between two . /// public static bool operator ==(Box2Rotated a, Box2Rotated b) { return a.Equals(b); } /// /// Check for inequality by value between two . /// public static bool operator !=(Box2Rotated a, Box2Rotated b) { return !a.Equals(b); } #endregion /// /// Returns a string representation of this type. /// public override readonly string ToString() { return $"{Box.ToString()}, {Rotation.ToString()}"; } } }