Rect2.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. #if REAL_T_IS_DOUBLE
  2. using real_t = System.Double;
  3. #else
  4. using real_t = System.Single;
  5. #endif
  6. using System;
  7. using System.Runtime.InteropServices;
  8. namespace Godot
  9. {
  10. /// <summary>
  11. /// 2D axis-aligned bounding box. Rect2 consists of a position, a size, and
  12. /// several utility functions. It is typically used for fast overlap tests.
  13. /// </summary>
  14. [Serializable]
  15. [StructLayout(LayoutKind.Sequential)]
  16. public struct Rect2 : IEquatable<Rect2>
  17. {
  18. private Vector2 _position;
  19. private Vector2 _size;
  20. /// <summary>
  21. /// Beginning corner. Typically has values lower than End.
  22. /// </summary>
  23. /// <value>Directly uses a private field.</value>
  24. public Vector2 Position
  25. {
  26. get { return _position; }
  27. set { _position = value; }
  28. }
  29. /// <summary>
  30. /// Size from Position to End. Typically all components are positive.
  31. /// If the size is negative, you can use <see cref="Abs"/> to fix it.
  32. /// </summary>
  33. /// <value>Directly uses a private field.</value>
  34. public Vector2 Size
  35. {
  36. get { return _size; }
  37. set { _size = value; }
  38. }
  39. /// <summary>
  40. /// Ending corner. This is calculated as <see cref="Position"/> plus
  41. /// <see cref="Size"/>. Setting this value will change the size.
  42. /// </summary>
  43. /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
  44. public Vector2 End
  45. {
  46. get { return _position + _size; }
  47. set { _size = value - _position; }
  48. }
  49. /// <summary>
  50. /// The area of this Rect2.
  51. /// </summary>
  52. /// <value>Equivalent to <see cref="GetArea()"/>.</value>
  53. public real_t Area
  54. {
  55. get { return GetArea(); }
  56. }
  57. /// <summary>
  58. /// Returns a Rect2 with equivalent position and size, modified so that
  59. /// the top-left corner is the origin and width and height are positive.
  60. /// </summary>
  61. /// <returns>The modified Rect2.</returns>
  62. public Rect2 Abs()
  63. {
  64. Vector2 end = End;
  65. Vector2 topLeft = new Vector2(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y));
  66. return new Rect2(topLeft, _size.Abs());
  67. }
  68. /// <summary>
  69. /// Returns the intersection of this Rect2 and `b`.
  70. /// If the rectangles do not intersect, an empty Rect2 is returned.
  71. /// </summary>
  72. /// <param name="b">The other Rect2.</param>
  73. /// <returns>The intersection of this Rect2 and `b`, or an empty Rect2 if they do not intersect.</returns>
  74. public Rect2 Intersection(Rect2 b)
  75. {
  76. var newRect = b;
  77. if (!Intersects(newRect))
  78. {
  79. return new Rect2();
  80. }
  81. newRect._position.x = Mathf.Max(b._position.x, _position.x);
  82. newRect._position.y = Mathf.Max(b._position.y, _position.y);
  83. Vector2 bEnd = b._position + b._size;
  84. Vector2 end = _position + _size;
  85. newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x;
  86. newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y;
  87. return newRect;
  88. }
  89. /// <summary>
  90. /// Returns true if this Rect2 completely encloses another one.
  91. /// </summary>
  92. /// <param name="b">The other Rect2 that may be enclosed.</param>
  93. /// <returns>A bool for whether or not this Rect2 encloses `b`.</returns>
  94. public bool Encloses(Rect2 b)
  95. {
  96. return b._position.x >= _position.x && b._position.y >= _position.y &&
  97. b._position.x + b._size.x < _position.x + _size.x &&
  98. b._position.y + b._size.y < _position.y + _size.y;
  99. }
  100. /// <summary>
  101. /// Returns this Rect2 expanded to include a given point.
  102. /// </summary>
  103. /// <param name="to">The point to include.</param>
  104. /// <returns>The expanded Rect2.</returns>
  105. public Rect2 Expand(Vector2 to)
  106. {
  107. var expanded = this;
  108. Vector2 begin = expanded._position;
  109. Vector2 end = expanded._position + expanded._size;
  110. if (to.x < begin.x)
  111. {
  112. begin.x = to.x;
  113. }
  114. if (to.y < begin.y)
  115. {
  116. begin.y = to.y;
  117. }
  118. if (to.x > end.x)
  119. {
  120. end.x = to.x;
  121. }
  122. if (to.y > end.y)
  123. {
  124. end.y = to.y;
  125. }
  126. expanded._position = begin;
  127. expanded._size = end - begin;
  128. return expanded;
  129. }
  130. /// <summary>
  131. /// Returns the area of the Rect2.
  132. /// </summary>
  133. /// <returns>The area.</returns>
  134. public real_t GetArea()
  135. {
  136. return _size.x * _size.y;
  137. }
  138. /// <summary>
  139. /// Returns a copy of the Rect2 grown by the specified amount on all sides.
  140. /// </summary>
  141. /// <param name="by">The amount to grow by.</param>
  142. /// <returns>The grown Rect2.</returns>
  143. public Rect2 Grow(real_t by)
  144. {
  145. var g = this;
  146. g._position.x -= by;
  147. g._position.y -= by;
  148. g._size.x += by * 2;
  149. g._size.y += by * 2;
  150. return g;
  151. }
  152. /// <summary>
  153. /// Returns a copy of the Rect2 grown by the specified amount on each side individually.
  154. /// </summary>
  155. /// <param name="left">The amount to grow by on the left side.</param>
  156. /// <param name="top">The amount to grow by on the top side.</param>
  157. /// <param name="right">The amount to grow by on the right side.</param>
  158. /// <param name="bottom">The amount to grow by on the bottom side.</param>
  159. /// <returns>The grown Rect2.</returns>
  160. public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom)
  161. {
  162. var g = this;
  163. g._position.x -= left;
  164. g._position.y -= top;
  165. g._size.x += left + right;
  166. g._size.y += top + bottom;
  167. return g;
  168. }
  169. /// <summary>
  170. /// Returns a copy of the Rect2 grown by the specified amount on the specified Side.
  171. /// </summary>
  172. /// <param name="side">The side to grow.</param>
  173. /// <param name="by">The amount to grow by.</param>
  174. /// <returns>The grown Rect2.</returns>
  175. public Rect2 GrowSide(Side side, real_t by)
  176. {
  177. var g = this;
  178. g = g.GrowIndividual(Side.Left == side ? by : 0,
  179. Side.Top == side ? by : 0,
  180. Side.Right == side ? by : 0,
  181. Side.Bottom == side ? by : 0);
  182. return g;
  183. }
  184. /// <summary>
  185. /// Returns true if the Rect2 is flat or empty, or false otherwise.
  186. /// </summary>
  187. /// <returns>A bool for whether or not the Rect2 has area.</returns>
  188. public bool HasNoArea()
  189. {
  190. return _size.x <= 0 || _size.y <= 0;
  191. }
  192. /// <summary>
  193. /// Returns true if the Rect2 contains a point, or false otherwise.
  194. /// </summary>
  195. /// <param name="point">The point to check.</param>
  196. /// <returns>A bool for whether or not the Rect2 contains `point`.</returns>
  197. public bool HasPoint(Vector2 point)
  198. {
  199. if (point.x < _position.x)
  200. return false;
  201. if (point.y < _position.y)
  202. return false;
  203. if (point.x >= _position.x + _size.x)
  204. return false;
  205. if (point.y >= _position.y + _size.y)
  206. return false;
  207. return true;
  208. }
  209. /// <summary>
  210. /// Returns true if the Rect2 overlaps with `b`
  211. /// (i.e. they have at least one point in common).
  212. ///
  213. /// If `includeBorders` is true, they will also be considered overlapping
  214. /// if their borders touch, even without intersection.
  215. /// </summary>
  216. /// <param name="b">The other Rect2 to check for intersections with.</param>
  217. /// <param name="includeBorders">Whether or not to consider borders.</param>
  218. /// <returns>A bool for whether or not they are intersecting.</returns>
  219. public bool Intersects(Rect2 b, bool includeBorders = false)
  220. {
  221. if (includeBorders)
  222. {
  223. if (_position.x > b._position.x + b._size.x)
  224. {
  225. return false;
  226. }
  227. if (_position.x + _size.x < b._position.x)
  228. {
  229. return false;
  230. }
  231. if (_position.y > b._position.y + b._size.y)
  232. {
  233. return false;
  234. }
  235. if (_position.y + _size.y < b._position.y)
  236. {
  237. return false;
  238. }
  239. }
  240. else
  241. {
  242. if (_position.x >= b._position.x + b._size.x)
  243. {
  244. return false;
  245. }
  246. if (_position.x + _size.x <= b._position.x)
  247. {
  248. return false;
  249. }
  250. if (_position.y >= b._position.y + b._size.y)
  251. {
  252. return false;
  253. }
  254. if (_position.y + _size.y <= b._position.y)
  255. {
  256. return false;
  257. }
  258. }
  259. return true;
  260. }
  261. /// <summary>
  262. /// Returns a larger Rect2 that contains this Rect2 and `b`.
  263. /// </summary>
  264. /// <param name="b">The other Rect2.</param>
  265. /// <returns>The merged Rect2.</returns>
  266. public Rect2 Merge(Rect2 b)
  267. {
  268. Rect2 newRect;
  269. newRect._position.x = Mathf.Min(b._position.x, _position.x);
  270. newRect._position.y = Mathf.Min(b._position.y, _position.y);
  271. newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x);
  272. newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y);
  273. newRect._size -= newRect._position; // Make relative again
  274. return newRect;
  275. }
  276. /// <summary>
  277. /// Constructs a Rect2 from a position and size.
  278. /// </summary>
  279. /// <param name="position">The position.</param>
  280. /// <param name="size">The size.</param>
  281. public Rect2(Vector2 position, Vector2 size)
  282. {
  283. _position = position;
  284. _size = size;
  285. }
  286. /// <summary>
  287. /// Constructs a Rect2 from a position, width, and height.
  288. /// </summary>
  289. /// <param name="position">The position.</param>
  290. /// <param name="width">The width.</param>
  291. /// <param name="height">The height.</param>
  292. public Rect2(Vector2 position, real_t width, real_t height)
  293. {
  294. _position = position;
  295. _size = new Vector2(width, height);
  296. }
  297. /// <summary>
  298. /// Constructs a Rect2 from x, y, and size.
  299. /// </summary>
  300. /// <param name="x">The position's X coordinate.</param>
  301. /// <param name="y">The position's Y coordinate.</param>
  302. /// <param name="size">The size.</param>
  303. public Rect2(real_t x, real_t y, Vector2 size)
  304. {
  305. _position = new Vector2(x, y);
  306. _size = size;
  307. }
  308. /// <summary>
  309. /// Constructs a Rect2 from x, y, width, and height.
  310. /// </summary>
  311. /// <param name="x">The position's X coordinate.</param>
  312. /// <param name="y">The position's Y coordinate.</param>
  313. /// <param name="width">The width.</param>
  314. /// <param name="height">The height.</param>
  315. public Rect2(real_t x, real_t y, real_t width, real_t height)
  316. {
  317. _position = new Vector2(x, y);
  318. _size = new Vector2(width, height);
  319. }
  320. public static bool operator ==(Rect2 left, Rect2 right)
  321. {
  322. return left.Equals(right);
  323. }
  324. public static bool operator !=(Rect2 left, Rect2 right)
  325. {
  326. return !left.Equals(right);
  327. }
  328. public override bool Equals(object obj)
  329. {
  330. if (obj is Rect2)
  331. {
  332. return Equals((Rect2)obj);
  333. }
  334. return false;
  335. }
  336. public bool Equals(Rect2 other)
  337. {
  338. return _position.Equals(other._position) && _size.Equals(other._size);
  339. }
  340. /// <summary>
  341. /// Returns true if this Rect2 and `other` are approximately equal, by running
  342. /// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
  343. /// </summary>
  344. /// <param name="other">The other Rect2 to compare.</param>
  345. /// <returns>Whether or not the Rect2s are approximately equal.</returns>
  346. public bool IsEqualApprox(Rect2 other)
  347. {
  348. return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size);
  349. }
  350. public override int GetHashCode()
  351. {
  352. return _position.GetHashCode() ^ _size.GetHashCode();
  353. }
  354. public override string ToString()
  355. {
  356. return $"{_position}, {_size}";
  357. }
  358. public string ToString(string format)
  359. {
  360. return $"{_position.ToString(format)}, {_size.ToString(format)}";
  361. }
  362. }
  363. }