// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; using System.Globalization; using System.Runtime.CompilerServices; namespace FlaxEngine { partial struct Rectangle : IEquatable { /// /// A which represents an empty space. /// public static readonly Rectangle Empty = new Rectangle(Float2.Zero, Float2.Zero); /// /// Gets or sets X coordinate of the left edge of the rectangle /// public float X { get => Location.X; set => Location.X = value; } /// /// Gets or sets Y coordinate of the left edge of the rectangle /// public float Y { get => Location.Y; set => Location.Y = value; } /// /// Gets or sets width of the rectangle /// public float Width { get => Size.X; set => Size.X = value; } /// /// Gets or sets height of the rectangle /// public float Height { get => Size.Y; set => Size.Y = value; } /// /// Gets Y coordinate of the top edge of the rectangle /// public float Top => Location.Y; /// /// Gets Y coordinate of the bottom edge of the rectangle /// public float Bottom => Location.Y + Size.Y; /// /// Gets X coordinate of the left edge of the rectangle /// public float Left => Location.X; /// /// Gets X coordinate of the right edge of the rectangle /// public float Right => Location.X + Size.X; /// /// Gets position of the upper left corner of the rectangle /// public Float2 UpperLeft => Location; /// /// Gets position of the upper right corner of the rectangle /// public Float2 UpperRight => new Float2(Location.X + Size.X, Location.Y); /// /// Gets position of the bottom right corner of the rectangle /// public Float2 BottomRight => Location + Size; /// /// Gets position of the bottom left corner of the rectangle /// public Float2 BottomLeft => new Float2(Location.X, Location.Y + Size.Y); /// /// Gets center position of the rectangle /// public Float2 Center => Location + Size * 0.5f; /// /// Init /// /// X coordinate /// Y coordinate /// Width /// Height public Rectangle(float x, float y, float width, float height) { Location = new Float2(x, y); Size = new Float2(width, height); } /// /// Init /// /// Location of the upper left corner /// Width /// Height public Rectangle(Float2 location, float width, float height) { Location = location; Size = new Float2(width, height); } /// /// Init /// /// X coordinate /// Y coordinate /// Size public Rectangle(float x, float y, Float2 size) { Location = new Float2(x, y); Size = size; } /// /// Init /// /// Location of the upper left corner /// Size public Rectangle(Float2 location, Float2 size) { Location = location; Size = size; } /// /// Checks if rectangle contains given point /// /// Point location to check /// True if point is inside rectangle's area public bool Contains(Float2 location) { return (location.X >= Location.X && location.Y >= Location.Y) && (location.X <= Location.X + Size.X && location.Y <= Location.Y + Size.Y); } /// /// Checks if rectangle contains given point /// /// Point location to check /// True if point is inside rectangle's area public bool Contains(ref Float2 location) { return (location.X >= Location.X && location.Y >= Location.Y) && (location.X <= Location.X + Size.X && location.Y <= Location.Y + Size.Y); } /// /// Determines whether this rectangle entirely contains a specified rectangle /// /// The rectangle to evaluate /// True if this rectangle entirely contains the specified rectangle, or false if not public bool Contains(Rectangle value) { return (Location.X <= value.Location.X) && (value.Right <= Right) && (Location.Y <= value.Location.Y) && (value.Bottom <= Bottom); } /// /// Determines whether this rectangle entirely contains a specified rectangle /// /// The rectangle to evaluate /// True if this rectangle entirely contains the specified rectangle, or false if not public bool Contains(ref Rectangle value) { return (Location.X <= value.Location.X) && (value.Right <= Right) && (Location.Y <= value.Location.Y) && (value.Bottom <= Bottom); } /// /// Determines whether a specified rectangle intersects with this rectangle /// /// The rectangle to evaluate /// True if the specified rectangle intersects with this one, otherwise false public bool Intersects(Rectangle value) { return (value.Location.X <= Right) && (Location.X <= value.Right) && (value.Location.Y <= Bottom) && (Location.Y <= value.Bottom); } /// /// Determines whether a specified rectangle intersects with this rectangle /// /// The rectangle to evaluate /// True if the specified rectangle intersects with this one, otherwise false public bool Intersects(ref Rectangle value) { return (value.Location.X <= Right) && (Location.X <= value.Right) && (value.Location.Y <= Bottom) && (Location.Y <= value.Bottom); } /// /// Offset rectangle position /// /// X coordinate offset /// Y coordinate offset public void Offset(float x, float y) { X += x; Y += y; } /// /// Offset rectangle position /// /// X and Y coordinate offset public void Offset(Float2 offset) { Location += offset; } /// /// Make offseted rectangle /// /// X coordinate offset /// Y coordinate offset /// Offseted rectangle public Rectangle MakeOffsetted(float x, float y) { return new Rectangle(Location + new Float2(x, y), Size); } /// /// Make offseted rectangle /// /// X and Y coordinate offset /// Offseted rectangle public Rectangle MakeOffsetted(Float2 offset) { return new Rectangle(Location + offset, Size); } /// /// Expand rectangle area in all directions by given amount /// /// Amount of units to expand a rectangle public void Expand(float toExpand) { Location -= toExpand * 0.5f; Size += toExpand; } /// /// Make expanded rectangle area in all directions by given amount /// /// Amount of units to expand a rectangle /// Expanded rectangle public Rectangle MakeExpanded(float toExpand) { return new Rectangle(Location - toExpand * 0.5f, Size + toExpand); } /// /// Scale rectangle area in all directions by given amount /// /// Scale value to expand a rectangle public void Scale(float scale) { Float2 toExpand = Size * (scale - 1.0f) * 0.5f; Location -= toExpand * 0.5f; Size += toExpand; } /// /// Make scaled rectangle area in all directions by given amount /// /// Scale value to expand a rectangle /// Scaled rectangle public Rectangle MakeScaled(float scale) { Float2 toExpand = Size * (scale - 1.0f) * 0.5f; return new Rectangle(Location - toExpand * 0.5f, Size + toExpand); } /// /// Computed nearest distance between 2 rectangles. /// /// First rectangle /// Second rectangle /// Resulting distance, 0 if overlapping public static float Distance(Rectangle a, Rectangle b) { return Float2.Max(Float2.Zero, Float2.Abs(a.Center - b.Center) - ((a.Size + b.Size) * 0.5f)).Length; } /// /// Computed distance between rectangle and the point. /// /// The rectangle. /// The point. /// The resulting distance, 0 if point is inside the rectangle. public static float Distance(Rectangle rect, Float2 p) { var max = rect.Location + rect.Size; var dx = Mathf.Max(Mathf.Max(rect.Location.X - p.X, p.X - max.X), 0); var dy = Mathf.Max(Math.Max(rect.Location.Y - p.Y, p.Y - max.Y), 0); return Mathf.Sqrt(dx * dx + dy * dy); } /// /// Calculates a rectangle that includes the margins (inside). /// /// The rectangle. /// The margin to apply to the rectangle. /// Rectangle inside the given rectangle after applying margins inside it. public static Rectangle Margin(Rectangle value, GUI.Margin margin) { value.Location += margin.Location; value.Size -= margin.Size; return value; } /// /// Calculates a rectangle that contains the union of a and b rectangles /// /// The first rectangle. /// The second rectangle. /// Rectangle that contains both a and b rectangles public static Rectangle Union(Rectangle a, Rectangle b) { float left = Mathf.Min(a.Left, b.Left); float right = Mathf.Max(a.Right, b.Right); float top = Mathf.Min(a.Top, b.Top); float bottom = Mathf.Max(a.Bottom, b.Bottom); return new Rectangle(left, top, Mathf.Max(right - left, 0.0f), Mathf.Max(bottom - top, 0.0f)); } /// /// Calculates a rectangle that contains the union of a and b rectangles /// /// First rectangle /// Second rectangle /// When the method completes, contains the rectangle that both a and b rectangles. public static void Union(ref Rectangle a, ref Rectangle b, out Rectangle result) { float left = Mathf.Min(a.Left, b.Left); float right = Mathf.Max(a.Right, b.Right); float top = Mathf.Min(a.Top, b.Top); float bottom = Mathf.Max(a.Bottom, b.Bottom); result = new Rectangle(left, top, Mathf.Max(right - left, 0.0f), Mathf.Max(bottom - top, 0.0f)); } /// /// Calculates a rectangle that contains the shared part of a and b rectangles. /// /// The first rectangle. /// The second rectangle. /// Rectangle that contains shared part of a and b rectangles. public static Rectangle Shared(Rectangle a, Rectangle b) { float left = Mathf.Max(a.Left, b.Left); float right = Mathf.Min(a.Right, b.Right); float top = Mathf.Max(a.Top, b.Top); float bottom = Mathf.Min(a.Bottom, b.Bottom); return new Rectangle(left, top, Mathf.Max(right - left, 0.0f), Mathf.Max(bottom - top, 0.0f)); } /// /// Calculates a rectangle that contains the shared part of a and b rectangles. /// /// The first rectangle. /// The second rectangle. /// When the method completes, contains the rectangle that shared part of a and b rectangles. public static void Shared(ref Rectangle a, ref Rectangle b, out Rectangle result) { float left = Mathf.Max(a.Left, b.Left); float right = Mathf.Min(a.Right, b.Right); float top = Mathf.Max(a.Top, b.Top); float bottom = Mathf.Min(a.Bottom, b.Bottom); result = new Rectangle(left, top, Mathf.Max(right - left, 0.0f), Mathf.Max(bottom - top, 0.0f)); } /// /// Creates rectangle from two points. /// /// First point /// Second point /// Rectangle that contains both p1 and p2 public static Rectangle FromPoints(Float2 p1, Float2 p2) { Float2.Min(ref p1, ref p2, out var upperLeft); Float2.Max(ref p1, ref p2, out var rightBottom); return new Rectangle(upperLeft, Float2.Max(rightBottom - upperLeft, Float2.Zero)); } /// /// Creates rectangle from two points. /// /// First point /// Second point /// Rectangle that contains both p1 and p2 /// When the method completes, contains the rectangle that contains both p1 and p2 points. public static void FromPoints(ref Float2 p1, ref Float2 p2, out Rectangle result) { Float2.Min(ref p1, ref p2, out var upperLeft); Float2.Max(ref p1, ref p2, out var rightBottom); result = new Rectangle(upperLeft, Float2.Max(rightBottom - upperLeft, Float2.Zero)); } #region Operators /// /// Implements the operator +. /// /// The rectangle. /// The offset. /// The result of the operator. public static Rectangle operator +(Rectangle rectangle, Float2 offset) { return new Rectangle(rectangle.Location + offset, rectangle.Size); } /// /// Implements the operator -. /// /// The rectangle. /// The offset. /// The result of the operator. public static Rectangle operator -(Rectangle rectangle, Float2 offset) { return new Rectangle(rectangle.Location - offset, rectangle.Size); } /// /// Implements the operator *. /// /// The rectangle. /// The scale. /// The result of the operator. public static Rectangle operator *(Rectangle rectangle, float scale) { return rectangle.MakeScaled(scale); } /// /// Tests for equality between two objects. /// /// The first value to compare. /// The second value to compare. /// true if has the same value as ; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rectangle left, Rectangle right) { return left.Equals(ref right); } /// /// Tests for inequality between two objects. /// /// The first value to compare. /// The second value to compare. /// true if has a different value than ; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rectangle left, Rectangle right) { return !left.Equals(ref right); } #endregion /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Rectangle other) { return Location.Equals(ref other.Location) && Size.Equals(ref other.Size); } /// public bool Equals(Rectangle other) { return Location.Equals(ref other.Location) && Size.Equals(ref other.Size); } /// public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; return obj is Rectangle && Equals((Rectangle)obj); } /// public override int GetHashCode() { unchecked { return (Location.GetHashCode() * 397) ^ Size.GetHashCode(); } } /// public override string ToString() { return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Width:{2} Height:{3}", X, Y, Width, Height); } } }