123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- #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
- {
- /// <summary>
- /// 2D axis-aligned bounding box. Rect2 consists of a position, a size, and
- /// several utility functions. It is typically used for fast overlap tests.
- /// </summary>
- [Serializable]
- [StructLayout(LayoutKind.Sequential)]
- public struct Rect2 : IEquatable<Rect2>
- {
- private Vector2 _position;
- private Vector2 _size;
- /// <summary>
- /// Beginning corner. Typically has values lower than End.
- /// </summary>
- /// <value>Directly uses a private field.</value>
- public Vector2 Position
- {
- get { return _position; }
- set { _position = value; }
- }
- /// <summary>
- /// Size from Position to End. Typically all components are positive.
- /// If the size is negative, you can use <see cref="Abs"/> to fix it.
- /// </summary>
- /// <value>Directly uses a private field.</value>
- public Vector2 Size
- {
- get { return _size; }
- set { _size = value; }
- }
- /// <summary>
- /// Ending corner. This is calculated as <see cref="Position"/> plus
- /// <see cref="Size"/>. Setting this value will change the size.
- /// </summary>
- /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
- public Vector2 End
- {
- get { return _position + _size; }
- set { _size = value - _position; }
- }
- /// <summary>
- /// The area of this Rect2.
- /// </summary>
- /// <value>Equivalent to <see cref="GetArea()"/>.</value>
- public real_t Area
- {
- get { return GetArea(); }
- }
- /// <summary>
- /// Returns a Rect2 with equivalent position and size, modified so that
- /// the top-left corner is the origin and width and height are positive.
- /// </summary>
- /// <returns>The modified Rect2.</returns>
- 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());
- }
- /// <summary>
- /// Returns the intersection of this Rect2 and `b`.
- /// If the rectangles do not intersect, an empty Rect2 is returned.
- /// </summary>
- /// <param name="b">The other Rect2.</param>
- /// <returns>The intersection of this Rect2 and `b`, or an empty Rect2 if they do not intersect.</returns>
- 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;
- }
- /// <summary>
- /// Returns true if this Rect2 completely encloses another one.
- /// </summary>
- /// <param name="b">The other Rect2 that may be enclosed.</param>
- /// <returns>A bool for whether or not this Rect2 encloses `b`.</returns>
- 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;
- }
- /// <summary>
- /// Returns this Rect2 expanded to include a given point.
- /// </summary>
- /// <param name="to">The point to include.</param>
- /// <returns>The expanded Rect2.</returns>
- 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;
- }
- /// <summary>
- /// Returns the area of the Rect2.
- /// </summary>
- /// <returns>The area.</returns>
- public real_t GetArea()
- {
- return _size.x * _size.y;
- }
- /// <summary>
- /// Returns a copy of the Rect2 grown by the specified amount on all sides.
- /// </summary>
- /// <param name="by">The amount to grow by.</param>
- /// <returns>The grown Rect2.</returns>
- 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;
- }
- /// <summary>
- /// Returns a copy of the Rect2 grown by the specified amount on each side individually.
- /// </summary>
- /// <param name="left">The amount to grow by on the left side.</param>
- /// <param name="top">The amount to grow by on the top side.</param>
- /// <param name="right">The amount to grow by on the right side.</param>
- /// <param name="bottom">The amount to grow by on the bottom side.</param>
- /// <returns>The grown Rect2.</returns>
- 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;
- }
- /// <summary>
- /// Returns a copy of the Rect2 grown by the specified amount on the specified Side.
- /// </summary>
- /// <param name="side">The side to grow.</param>
- /// <param name="by">The amount to grow by.</param>
- /// <returns>The grown Rect2.</returns>
- 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;
- }
- /// <summary>
- /// Returns true if the Rect2 is flat or empty, or false otherwise.
- /// </summary>
- /// <returns>A bool for whether or not the Rect2 has area.</returns>
- public bool HasNoArea()
- {
- return _size.x <= 0 || _size.y <= 0;
- }
- /// <summary>
- /// Returns true if the Rect2 contains a point, or false otherwise.
- /// </summary>
- /// <param name="point">The point to check.</param>
- /// <returns>A bool for whether or not the Rect2 contains `point`.</returns>
- 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;
- }
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="b">The other Rect2 to check for intersections with.</param>
- /// <param name="includeBorders">Whether or not to consider borders.</param>
- /// <returns>A bool for whether or not they are intersecting.</returns>
- 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;
- }
- /// <summary>
- /// Returns a larger Rect2 that contains this Rect2 and `b`.
- /// </summary>
- /// <param name="b">The other Rect2.</param>
- /// <returns>The merged Rect2.</returns>
- 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;
- }
- /// <summary>
- /// Constructs a Rect2 from a position and size.
- /// </summary>
- /// <param name="position">The position.</param>
- /// <param name="size">The size.</param>
- public Rect2(Vector2 position, Vector2 size)
- {
- _position = position;
- _size = size;
- }
- /// <summary>
- /// Constructs a Rect2 from a position, width, and height.
- /// </summary>
- /// <param name="position">The position.</param>
- /// <param name="width">The width.</param>
- /// <param name="height">The height.</param>
- public Rect2(Vector2 position, real_t width, real_t height)
- {
- _position = position;
- _size = new Vector2(width, height);
- }
- /// <summary>
- /// Constructs a Rect2 from x, y, and size.
- /// </summary>
- /// <param name="x">The position's X coordinate.</param>
- /// <param name="y">The position's Y coordinate.</param>
- /// <param name="size">The size.</param>
- public Rect2(real_t x, real_t y, Vector2 size)
- {
- _position = new Vector2(x, y);
- _size = size;
- }
- /// <summary>
- /// Constructs a Rect2 from x, y, width, and height.
- /// </summary>
- /// <param name="x">The position's X coordinate.</param>
- /// <param name="y">The position's Y coordinate.</param>
- /// <param name="width">The width.</param>
- /// <param name="height">The height.</param>
- 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);
- }
- /// <summary>
- /// Returns true if this Rect2 and `other` are approximately equal, by running
- /// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
- /// </summary>
- /// <param name="other">The other Rect2 to compare.</param>
- /// <returns>Whether or not the Rect2s are approximately equal.</returns>
- 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 $"{_position}, {_size}";
- }
- public string ToString(string format)
- {
- return $"{_position.ToString(format)}, {_size.ToString(format)}";
- }
- }
- }
|