using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace RhythmBase.Components { /// /// Represents a rotated rectangle with non-nullable float coordinates. /// [DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] public struct RDRotatedRectN(RDPointN location, RDSizeN size, RDPointN pivot, float angle = 0) : IEquatable { /// /// Gets or sets the location of the rectangle. /// public RDPointN Location { get; set; } = location; /// /// Gets or sets the size of the rectangle. /// public RDSizeN Size { get; set; } = size; /// /// Gets or sets the pivot point of the rotation. /// public RDPointN Pivot { get; set; } = pivot; /// /// Gets or sets the angle of rotation in radians. /// public float Angle { get; set; } = angle; /// /// Gets the top-left point of the rotated rectangle. /// public readonly RDPointN LeftTop => (Location - (RDSizeN)Pivot + new RDSizeN(0, Size.Height)).Rotate(Location, Angle); /// /// Gets the top-right point of the rotated rectangle. /// public readonly RDPointN RightTop => (Location - (RDSizeN)Pivot + Size).Rotate(Location, Angle); /// /// Gets the bottom-left point of the rotated rectangle. /// public readonly RDPointN LeftBottom => (Location - (RDSizeN)Pivot).Rotate(Location, Angle); /// /// Gets the bottom-right point of the rotated rectangle. /// public readonly RDPointN RightBottom => (Location - (RDSizeN)Pivot + new RDSizeN(Size.Width, 0)).Rotate(Location, Angle); /// /// Gets the rectangle without rotation. /// public readonly RDRectN WithoutRotate => new(Location - (RDSizeN)Pivot, Size); /// /// Initializes a new instance of the struct. /// /// The rectangle. /// The pivot point. /// The angle of rotation. public RDRotatedRectN(RDRectN rect, RDPointN pivot, float angle) : this(rect.Location, rect.Size, pivot, angle) { } /// /// Initializes a new instance of the struct. /// /// The rectangle. public RDRotatedRectN(RDRectN rect) : this(rect.Location, rect.Size, default, 0f) { } /// /// Inflates the specified rectangle by the specified size. /// /// The rectangle to inflate. /// The size to inflate by. /// The inflated rectangle. public static RDRotatedRectN Inflate(RDRotatedRectN rect, RDSizeN size) { RDRotatedRectN result = rect; result.Inflate(size); return result; } /// /// Inflates the specified rectangle by the specified width and height. /// /// The rectangle to inflate. /// The width to inflate by. /// The height to inflate by. /// The inflated rectangle. public static RDRotatedRectN Inflate(RDRotatedRectN rect, int x, int y) { RDRotatedRectN result = rect; result.Inflate(x, y); return result; } /// /// Offsets the rectangle by the specified x and y values. /// /// The x value to offset by. /// The y value to offset by. public void Offset(float x, float y) => Location += (RDSizeN)new RDPointN(x, y); /// /// Offsets the rectangle by the specified point. /// /// The point to offset by. public void Offset(RDPointN p) => Offset(p.X, p.Y); /// /// Inflates the rectangle by the specified size. /// /// The size to inflate by. public void Inflate(RDSizeN size) { Size += new RDSizeN(size.Width * 2, size.Height * 2); Pivot -= (RDSizeN)new RDPointN(size.Width, size.Height); } /// /// Inflates the rectangle by the specified width and height. /// /// The width to inflate by. /// The height to inflate by. public void Inflate(float width, float height) { Size += new RDSizeN(width * 2, height * 2); Pivot -= (RDSizeN)new RDPointN(width, height); } /// /// Determines whether the rectangle contains the specified point. /// /// The x coordinate of the point. /// The y coordinate of the point. /// true if the rectangle contains the point; otherwise, false. public readonly bool Contains(float x, float y) => WithoutRotate.Contains(new RDPointN(x, y).Rotate(-Angle)); /// /// Determines whether the rectangle contains the specified point. /// /// The point. /// true if the rectangle contains the point; otherwise, false. public readonly bool Contains(RDPointN p) => WithoutRotate.Contains(p.Rotate(-Angle)); /// /// Determines whether the rectangle contains the specified rotated rectangle. /// /// The rotated rectangle. /// true if the rectangle contains the rotated rectangle; otherwise, false. public readonly bool Contains(RDRotatedRectN rect) => Contains(rect.LeftTop) && Contains(rect.RightTop) && Contains(rect.LeftBottom) && Contains(rect.RightBottom); /// /// Determines whether the rectangle intersects with the specified rotated rectangle. /// /// The rotated rectangle. /// true if the rectangle intersects with the rotated rectangle; otherwise, false. public readonly bool IntersectsWith(RDRotatedRectN rect) => Contains(rect.LeftTop) || Contains(rect.RightTop) || Contains(rect.LeftBottom) || Contains(rect.RightBottom); /// /// Determines whether two rotated rectangles are equal. /// /// The first rotated rectangle. /// The second rotated rectangle. /// true if the rectangles are equal; otherwise, false. public static bool operator ==(RDRotatedRectN rect1, RDRotatedRectN rect2) => rect1.Equals(rect2); /// /// Determines whether two rotated rectangles are not equal. /// /// The first rotated rectangle. /// The second rotated rectangle. /// true if the rectangles are not equal; otherwise, false. public static bool operator !=(RDRotatedRectN rect1, RDRotatedRectN rect2) => !rect1.Equals(rect2); /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is RDRotatedRectN e && Equals(e); /// /// Serves as the default hash function. /// /// A hash code for the current object. public override readonly int GetHashCode() => HashCode.Combine(Location, Size, Pivot, Angle); /// /// Returns a string that represents the current object. /// /// A string that represents the current object. public override readonly string ToString() => $"{{Location=[{Location}],Size=[{Size}],Pivot[{Pivot}],Angle={Angle}}}"; /// /// Determines whether the specified is equal to the current . /// /// The to compare with the current . /// true if the specified is equal to the current ; otherwise, false. public readonly bool Equals(RDRotatedRectN other) => Location == other.Location && Size == other.Size && Pivot == other.Pivot && Angle == other.Angle; private readonly string GetDebuggerDisplay() => ToString(); } }