#if REAL_T_IS_DOUBLE using real_t = System.Double; #else using real_t = System.Single; #endif using System; using System.Runtime.InteropServices; namespace Godot { /// /// 2D axis-aligned bounding box. Rect2 consists of a position, a size, and /// several utility functions. It is typically used for fast overlap tests. /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Rect2 : IEquatable { private Vector2 _position; private Vector2 _size; /// /// Beginning corner. Typically has values lower than End. /// /// Directly uses a private field. public Vector2 Position { get { return _position; } set { _position = value; } } /// /// Size from Position to End. Typically all components are positive. /// If the size is negative, you can use to fix it. /// /// Directly uses a private field. public Vector2 Size { get { return _size; } set { _size = value; } } /// /// Ending corner. This is calculated as plus /// . Setting this value will change the size. /// /// Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`. public Vector2 End { get { return _position + _size; } set { _size = value - _position; } } /// /// The area of this Rect2. /// /// Equivalent to . public real_t Area { get { return GetArea(); } } /// /// Returns a Rect2 with equivalent position and size, modified so that /// the top-left corner is the origin and width and height are positive. /// /// The modified Rect2. public Rect2 Abs() { Vector2 end = End; Vector2 topLeft = new Vector2(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y)); return new Rect2(topLeft, _size.Abs()); } /// /// Returns the intersection of this Rect2 and `b`. /// If the rectangles do not intersect, an empty Rect2 is returned. /// /// The other Rect2. /// The intersection of this Rect2 and `b`, or an empty Rect2 if they do not intersect. public Rect2 Intersection(Rect2 b) { var newRect = b; if (!Intersects(newRect)) { return new Rect2(); } newRect._position.x = Mathf.Max(b._position.x, _position.x); newRect._position.y = Mathf.Max(b._position.y, _position.y); Vector2 bEnd = b._position + b._size; Vector2 end = _position + _size; newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x; newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y; return newRect; } /// /// Returns true if this Rect2 completely encloses another one. /// /// The other Rect2 that may be enclosed. /// A bool for whether or not this Rect2 encloses `b`. public bool Encloses(Rect2 b) { return b._position.x >= _position.x && b._position.y >= _position.y && b._position.x + b._size.x < _position.x + _size.x && b._position.y + b._size.y < _position.y + _size.y; } /// /// Returns this Rect2 expanded to include a given point. /// /// The point to include. /// The expanded Rect2. public Rect2 Expand(Vector2 to) { var expanded = this; Vector2 begin = expanded._position; Vector2 end = expanded._position + expanded._size; if (to.x < begin.x) { begin.x = to.x; } if (to.y < begin.y) { begin.y = to.y; } if (to.x > end.x) { end.x = to.x; } if (to.y > end.y) { end.y = to.y; } expanded._position = begin; expanded._size = end - begin; return expanded; } /// /// Returns the area of the Rect2. /// /// The area. public real_t GetArea() { return _size.x * _size.y; } /// /// Returns a copy of the Rect2 grown by the specified amount on all sides. /// /// The amount to grow by. /// The grown Rect2. public Rect2 Grow(real_t by) { var g = this; g._position.x -= by; g._position.y -= by; g._size.x += by * 2; g._size.y += by * 2; return g; } /// /// Returns a copy of the Rect2 grown by the specified amount on each side individually. /// /// The amount to grow by on the left side. /// The amount to grow by on the top side. /// The amount to grow by on the right side. /// The amount to grow by on the bottom side. /// The grown Rect2. public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom) { var g = this; g._position.x -= left; g._position.y -= top; g._size.x += left + right; g._size.y += top + bottom; return g; } /// /// Returns a copy of the Rect2 grown by the specified amount on the specified Side. /// /// The side to grow. /// The amount to grow by. /// The grown Rect2. public Rect2 GrowSide(Side side, real_t by) { var g = this; g = g.GrowIndividual(Side.Left == side ? by : 0, Side.Top == side ? by : 0, Side.Right == side ? by : 0, Side.Bottom == side ? by : 0); return g; } /// /// Returns true if the Rect2 is flat or empty, or false otherwise. /// /// A bool for whether or not the Rect2 has area. public bool HasNoArea() { return _size.x <= 0 || _size.y <= 0; } /// /// Returns true if the Rect2 contains a point, or false otherwise. /// /// The point to check. /// A bool for whether or not the Rect2 contains `point`. public bool HasPoint(Vector2 point) { if (point.x < _position.x) return false; if (point.y < _position.y) return false; if (point.x >= _position.x + _size.x) return false; if (point.y >= _position.y + _size.y) return false; return true; } /// /// Returns true if the Rect2 overlaps with `b` /// (i.e. they have at least one point in common). /// /// If `includeBorders` is true, they will also be considered overlapping /// if their borders touch, even without intersection. /// /// The other Rect2 to check for intersections with. /// Whether or not to consider borders. /// A bool for whether or not they are intersecting. public bool Intersects(Rect2 b, bool includeBorders = false) { if (includeBorders) { if (_position.x > b._position.x + b._size.x) { return false; } if (_position.x + _size.x < b._position.x) { return false; } if (_position.y > b._position.y + b._size.y) { return false; } if (_position.y + _size.y < b._position.y) { return false; } } else { if (_position.x >= b._position.x + b._size.x) { return false; } if (_position.x + _size.x <= b._position.x) { return false; } if (_position.y >= b._position.y + b._size.y) { return false; } if (_position.y + _size.y <= b._position.y) { return false; } } return true; } /// /// Returns a larger Rect2 that contains this Rect2 and `b`. /// /// The other Rect2. /// The merged Rect2. public Rect2 Merge(Rect2 b) { Rect2 newRect; newRect._position.x = Mathf.Min(b._position.x, _position.x); newRect._position.y = Mathf.Min(b._position.y, _position.y); newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x); newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y); newRect._size -= newRect._position; // Make relative again return newRect; } /// /// Constructs a Rect2 from a position and size. /// /// The position. /// The size. public Rect2(Vector2 position, Vector2 size) { _position = position; _size = size; } /// /// Constructs a Rect2 from a position, width, and height. /// /// The position. /// The width. /// The height. public Rect2(Vector2 position, real_t width, real_t height) { _position = position; _size = new Vector2(width, height); } /// /// Constructs a Rect2 from x, y, and size. /// /// The position's X coordinate. /// The position's Y coordinate. /// The size. public Rect2(real_t x, real_t y, Vector2 size) { _position = new Vector2(x, y); _size = size; } /// /// Constructs a Rect2 from x, y, width, and height. /// /// The position's X coordinate. /// The position's Y coordinate. /// The width. /// The height. public Rect2(real_t x, real_t y, real_t width, real_t height) { _position = new Vector2(x, y); _size = new Vector2(width, height); } public static bool operator ==(Rect2 left, Rect2 right) { return left.Equals(right); } public static bool operator !=(Rect2 left, Rect2 right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is Rect2) { return Equals((Rect2)obj); } return false; } public bool Equals(Rect2 other) { return _position.Equals(other._position) && _size.Equals(other._size); } /// /// Returns true if this Rect2 and `other` are approximately equal, by running /// on each component. /// /// The other Rect2 to compare. /// Whether or not the Rect2s are approximately equal. public bool IsEqualApprox(Rect2 other) { return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size); } public override int GetHashCode() { return _position.GetHashCode() ^ _size.GetHashCode(); } public override string ToString() { return String.Format("{0}, {1}", new object[] { _position.ToString(), _size.ToString() }); } public string ToString(string format) { return String.Format("{0}, {1}", new object[] { _position.ToString(format), _size.ToString(format) }); } } }