CircleF.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.Serialization;
  4. using Microsoft.Xna.Framework;
  5. namespace MonoGame.Extended
  6. {
  7. // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.3; Bounding Volumes - Spheres. pg 88
  8. /// <summary>
  9. /// A two dimensional circle defined by a centre <see cref="Vector2" /> and a radius <see cref="float" />.
  10. /// </summary>
  11. /// <remarks>
  12. /// <para>
  13. /// An <see cref="CircleF" /> is categorized by the set of all points in a plane that are at equal distance from
  14. /// the
  15. /// centre.
  16. /// </para>
  17. /// </remarks>
  18. /// <seealso cref="IEquatable{T}" />
  19. /// <seealso cref="IEquatableByRef{T}" />
  20. [DataContract]
  21. public struct CircleF : IEquatable<CircleF>, IEquatableByRef<CircleF>, IShapeF
  22. {
  23. /// <summary>
  24. /// The centre position of this <see cref="CircleF" />.
  25. /// </summary>
  26. [DataMember] public Vector2 Center;
  27. /// <summary>
  28. /// The distance from the <see cref="Center" /> point to any point on the boundary of this <see cref="CircleF" />.
  29. /// </summary>
  30. [DataMember] public float Radius;
  31. /// <summary>
  32. /// Gets or sets the position of the circle.
  33. /// </summary>
  34. public Vector2 Position
  35. {
  36. get => Center;
  37. set => Center = value;
  38. }
  39. public RectangleF BoundingRectangle
  40. {
  41. get
  42. {
  43. var minX = Center.X - Radius;
  44. var minY = Center.Y - Radius;
  45. return new RectangleF(minX, minY, Diameter, Diameter);
  46. }
  47. }
  48. /// <summary>
  49. /// Gets the distance from a point to the opposite point, both on the boundary of this <see cref="CircleF" />.
  50. /// </summary>
  51. public float Diameter => 2 * Radius;
  52. /// <summary>
  53. /// Gets the distance around the boundary of this <see cref="CircleF" />.
  54. /// </summary>
  55. public float Circumference => 2 * MathHelper.Pi * Radius;
  56. /// <summary>
  57. /// Initializes a new instance of the <see cref="CircleF" /> structure from the specified centre
  58. /// <see cref="Vector2" /> and the radius <see cref="float" />.
  59. /// </summary>
  60. /// <param name="center">The centre point.</param>
  61. /// <param name="radius">The radius.</param>
  62. public CircleF(Vector2 center, float radius)
  63. {
  64. Center = center;
  65. Radius = radius;
  66. }
  67. /// <summary>
  68. /// Computes the bounding <see cref="CircleF" /> from a minimum <see cref="Vector2" /> and maximum
  69. /// <see cref="Vector2" />.
  70. /// </summary>
  71. /// <param name="minimum">The minimum point.</param>
  72. /// <param name="maximum">The maximum point.</param>
  73. /// <param name="result">The resulting circle.</param>
  74. public static void CreateFrom(Vector2 minimum, Vector2 maximum, out CircleF result)
  75. {
  76. result.Center = new Vector2((maximum.X + minimum.X) * 0.5f, (maximum.Y + minimum.Y) * 0.5f);
  77. var distanceVector = maximum - minimum;
  78. result.Radius = distanceVector.X > distanceVector.Y ? distanceVector.X * 0.5f : distanceVector.Y * 0.5f;
  79. }
  80. /// <summary>
  81. /// Computes the bounding <see cref="CircleF" /> from a minimum <see cref="Vector2" /> and maximum
  82. /// <see cref="Vector2" />.
  83. /// </summary>
  84. /// <param name="minimum">The minimum point.</param>
  85. /// <param name="maximum">The maximum point.</param>
  86. /// <returns>An <see cref="CircleF" />.</returns>
  87. public static CircleF CreateFrom(Vector2 minimum, Vector2 maximum)
  88. {
  89. CircleF result;
  90. CreateFrom(minimum, maximum, out result);
  91. return result;
  92. }
  93. /// <summary>
  94. /// Computes the bounding <see cref="CircleF" /> from a list of <see cref="Vector2" /> structures.
  95. /// </summary>
  96. /// <param name="points">The points.</param>
  97. /// <param name="result">The resulting circle.</param>
  98. public static void CreateFrom(IReadOnlyList<Vector2> points, out CircleF result)
  99. {
  100. // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.3; Bounding Volumes - Spheres. pg 89-90
  101. if (points == null || points.Count == 0)
  102. {
  103. result = default(CircleF);
  104. return;
  105. }
  106. var minimum = new Vector2(float.MaxValue, float.MaxValue);
  107. var maximum = new Vector2(float.MinValue, float.MinValue);
  108. // ReSharper disable once ForCanBeConvertedToForeach
  109. for (var index = points.Count - 1; index >= 0; --index)
  110. {
  111. var point = points[index];
  112. minimum = MathExtended.CalculateMinimumVector2(minimum, point);
  113. maximum = MathExtended.CalculateMaximumVector2(maximum, point);
  114. }
  115. CreateFrom(minimum, maximum, out result);
  116. }
  117. /// <summary>
  118. /// Computes the bounding <see cref="CircleF" /> from a list of <see cref="Vector2" /> structures.
  119. /// </summary>
  120. /// <param name="points">The points.</param>
  121. /// <returns>An <see cref="CircleF" />.</returns>
  122. public static CircleF CreateFrom(IReadOnlyList<Vector2> points)
  123. {
  124. CircleF result;
  125. CreateFrom(points, out result);
  126. return result;
  127. }
  128. /// <summary>
  129. /// Determines whether the two specified <see cref="CircleF" /> structures intersect.
  130. /// </summary>
  131. /// <param name="first">The first circle.</param>
  132. /// <param name="second">The second circle.</param>
  133. /// <returns>
  134. /// <c>true</c> if the <paramref name="first" /> intersects with the <see cref="second" />; otherwise, <c>false</c>.
  135. /// </returns>
  136. public static bool Intersects(ref CircleF first, ref CircleF second)
  137. {
  138. // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.3; Bounding Volumes - Spheres. pg 88
  139. // Calculate squared distance between centers
  140. var distanceVector = first.Center - second.Center;
  141. var distanceSquared = distanceVector.Dot(distanceVector);
  142. var radiusSum = first.Radius + second.Radius;
  143. return distanceSquared <= radiusSum * radiusSum;
  144. }
  145. /// <summary>
  146. /// Determines whether the two specified <see cref="CircleF" /> structures intersect.
  147. /// </summary>
  148. /// <param name="first">The first circle.</param>
  149. /// <param name="second">The second circle.</param>
  150. /// <returns>
  151. /// <c>true</c> if the <paramref name="first" /> intersects with the <see cref="second" />; otherwise, <c>false</c>.
  152. /// </returns>
  153. public static bool Intersects(CircleF first, CircleF second)
  154. {
  155. return Intersects(ref first, ref second);
  156. }
  157. /// <summary>
  158. /// Determines whether the specified <see cref="CircleF" /> intersects with this <see cref="CircleF" />.
  159. /// </summary>
  160. /// <param name="circle">The circle.</param>
  161. /// <returns>
  162. /// <c>true</c> if the <paramref name="circle" /> intersects with this <see cref="CircleF" />; otherwise,
  163. /// <c>false</c>.
  164. /// </returns>
  165. public bool Intersects(ref CircleF circle)
  166. {
  167. return Intersects(ref this, ref circle);
  168. }
  169. /// <summary>
  170. /// Determines whether the specified <see cref="CircleF" /> intersects with this <see cref="CircleF" />.
  171. /// </summary>
  172. /// <param name="circle">The circle.</param>
  173. /// <returns>
  174. /// <c>true</c> if the <paramref name="circle" /> intersects with this <see cref="CircleF" />; otherwise,
  175. /// <c>false</c>.
  176. /// </returns>
  177. public bool Intersects(CircleF circle)
  178. {
  179. return Intersects(ref this, ref circle);
  180. }
  181. /// <summary>
  182. /// Determines whether the specified <see cref="CircleF" /> and <see cref="BoundingRectangle" /> structures intersect.
  183. /// </summary>
  184. /// <param name="circle">The circle.</param>
  185. /// <param name="rectangle">The rectangle.</param>
  186. /// <returns>
  187. /// <c>true</c> if the <paramref name="circle" /> intersects with the <see cref="rectangle" />; otherwise, <c>false</c>
  188. /// .
  189. /// </returns>
  190. public static bool Intersects(ref CircleF circle, ref BoundingRectangle rectangle)
  191. {
  192. // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 5.25; Basic Primitives Test - Testing Sphere Against AABB. pg 165-166
  193. // Compute squared distance between sphere center and AABB boundary
  194. var distanceSquared = rectangle.SquaredDistanceTo(circle.Center);
  195. // Circle and AABB intersect if the (squared) distance between the AABB's boundary and the circle is less than the (squared) circle's radius
  196. return distanceSquared <= circle.Radius * circle.Radius;
  197. }
  198. /// <summary>
  199. /// Determines whether the specified <see cref="CircleF" /> and <see cref="BoundingRectangle" /> structures intersect.
  200. /// </summary>
  201. /// <param name="circle">The circle.</param>
  202. /// <param name="rectangle">The rectangle.</param>
  203. /// <returns>
  204. /// <c>true</c> if the <paramref name="circle" /> intersects with the <see cref="rectangle" />; otherwise, <c>false</c>
  205. /// .
  206. /// </returns>
  207. public static bool Intersects(CircleF circle, BoundingRectangle rectangle)
  208. {
  209. return Intersects(ref circle, ref rectangle);
  210. }
  211. /// <summary>
  212. /// Determines whether the specified <see cref="CircleF" /> intersects with this <see cref="BoundingRectangle" />.
  213. /// </summary>
  214. /// <param name="rectangle">The rectangle.</param>
  215. /// <returns>
  216. /// <c>true</c> if the <paramref name="rectangle" /> intersects with this <see cref="CircleF" />; otherwise,
  217. /// <c>false</c>.
  218. /// </returns>
  219. public bool Intersects(ref BoundingRectangle rectangle)
  220. {
  221. return Intersects(ref this, ref rectangle);
  222. }
  223. /// <summary>
  224. /// Determines whether the specified <see cref="CircleF" /> intersects with this <see cref="BoundingRectangle" />.
  225. /// </summary>
  226. /// <param name="rectangle">The rectangle.</param>
  227. /// <returns>
  228. /// <c>true</c> if the <paramref name="rectangle" /> intersects with this <see cref="CircleF" />; otherwise,
  229. /// <c>false</c>.
  230. /// </returns>
  231. public bool Intersects(BoundingRectangle rectangle)
  232. {
  233. return Intersects(ref this, ref rectangle);
  234. }
  235. /// <summary>
  236. /// Determines whether the specified <see cref="CircleF" /> contains the specified
  237. /// <see cref="Vector2" />.
  238. /// </summary>
  239. /// <param name="circle">The circle.</param>
  240. /// <param name="point">The point.</param>
  241. /// <returns>
  242. /// <c>true</c> if the <paramref name="circle" /> contains the <paramref name="point" />; otherwise,
  243. /// <c>false</c>.
  244. /// </returns>
  245. public static bool Contains(ref CircleF circle, Vector2 point)
  246. {
  247. var dx = circle.Center.X - point.X;
  248. var dy = circle.Center.Y - point.Y;
  249. var d2 = dx * dx + dy * dy;
  250. var r2 = circle.Radius * circle.Radius;
  251. return d2 <= r2;
  252. }
  253. /// <summary>
  254. /// Determines whether the specified <see cref="CircleF" /> contains the specified
  255. /// <see cref="Vector2" />.
  256. /// </summary>
  257. /// <param name="circle">The circle.</param>
  258. /// <param name="point">The point.</param>
  259. /// <returns>
  260. /// <c>true</c> if the <paramref name="circle" /> contains the <paramref name="point" />; otherwise,
  261. /// <c>false</c>.
  262. /// </returns>
  263. public static bool Contains(CircleF circle, Vector2 point)
  264. {
  265. return Contains(ref circle, point);
  266. }
  267. /// <summary>
  268. /// Determines whether this <see cref="CircleF" /> contains the specified <see cref="Vector2" />.
  269. /// </summary>
  270. /// <param name="point">The point.</param>
  271. /// <returns>
  272. /// <c>true</c> if this <see cref="BoundingRectangle" /> contains the <paramref name="point" />; otherwise,
  273. /// <c>false</c>.
  274. /// </returns>
  275. public bool Contains(Vector2 point)
  276. {
  277. return Contains(ref this, point);
  278. }
  279. /// <summary>
  280. /// Computes the closest <see cref="Vector2" /> on this <see cref="CircleF" /> to a specified
  281. /// <see cref="Vector2" />.
  282. /// </summary>
  283. /// <param name="point">The point.</param>
  284. /// <returns>The closest <see cref="Vector2" /> on this <see cref="CircleF" /> to the <paramref name="point" />.</returns>
  285. public Vector2 ClosestPointTo(Vector2 point)
  286. {
  287. var distanceVector = point - Center;
  288. var lengthSquared = distanceVector.Dot(distanceVector);
  289. if (lengthSquared <= Radius * Radius)
  290. return point;
  291. distanceVector.Normalize();
  292. return Center + Radius * distanceVector;
  293. }
  294. /// <summary>
  295. /// Computes the <see cref="Vector2" /> on the boundary of of this <see cref="CircleF" /> using the specified angle.
  296. /// </summary>
  297. /// <param name="angle">The angle in radians.</param>
  298. /// <returns>The <see cref="Vector2" /> on the boundary of this <see cref="CircleF" /> using <paramref name="angle" />.</returns>
  299. public Vector2 BoundaryPointAt(float angle)
  300. {
  301. var direction = new Vector2((float) Math.Cos(angle), (float) Math.Sin(angle));
  302. return Center + Radius * direction;
  303. }
  304. [Obsolete("Circle.GetPointAlongEdge() may be removed in the future. Use BoundaryPointAt() instead.")]
  305. public Vector2 GetPointAlongEdge(float angle)
  306. {
  307. return Center + new Vector2(Radius * (float) Math.Cos(angle), Radius * (float) Math.Sin(angle));
  308. }
  309. /// <summary>
  310. /// Compares two <see cref="CircleF" /> structures. The result specifies whether the values of the
  311. /// <see cref="Center" /> and <see cref="Radius" /> fields of the two <see cref="CircleF" /> structures
  312. /// are equal.
  313. /// </summary>
  314. /// <param name="first">The first circle.</param>
  315. /// <param name="second">The second circle.</param>
  316. /// <returns>
  317. /// <c>true</c> if the <see cref="Center" /> and <see cref="Radius" /> fields of the two
  318. /// <see cref="BoundingRectangle" /> structures are equal; otherwise, <c>false</c>.
  319. /// </returns>
  320. public static bool operator ==(CircleF first, CircleF second)
  321. {
  322. return first.Equals(ref second);
  323. }
  324. /// <summary>
  325. /// Compares two <see cref="CircleF" /> structures. The result specifies whether the values of the
  326. /// <see cref="Center" /> and <see cref="Radius" /> fields of the two <see cref="CircleF" /> structures
  327. /// are unequal.
  328. /// </summary>
  329. /// <param name="first">The first circle.</param>
  330. /// <param name="second">The second circle.</param>
  331. /// <returns>
  332. /// <c>true</c> if the <see cref="Center" /> and <see cref="Radius" /> fields of the two
  333. /// <see cref="CircleF" /> structures are unequal; otherwise, <c>false</c>.
  334. /// </returns>
  335. public static bool operator !=(CircleF first, CircleF second)
  336. {
  337. return !(first == second);
  338. }
  339. /// <summary>
  340. /// Indicates whether this <see cref="CircleF" /> is equal to another <see cref="CircleF" />.
  341. /// </summary>
  342. /// <param name="circle">The circle.</param>
  343. /// <returns>
  344. /// <c>true</c> if this <see cref="CircleF" /> is equal to the <paramref name="circle" />; otherwise, <c>false</c>.
  345. /// </returns>
  346. public bool Equals(CircleF circle)
  347. {
  348. return Equals(ref circle);
  349. }
  350. /// <summary>
  351. /// Indicates whether this <see cref="CircleF" /> is equal to another <see cref="CircleF" />.
  352. /// </summary>
  353. /// <param name="circle">The bounding rectangle.</param>
  354. /// <returns>
  355. /// <c>true</c> if this <see cref="CircleF" /> is equal to the <paramref name="circle" />;
  356. /// otherwise,<c>false</c>.
  357. /// </returns>
  358. public bool Equals(ref CircleF circle)
  359. {
  360. // ReSharper disable once CompareOfFloatsByEqualityOperator
  361. return circle.Center == Center && circle.Radius == Radius;
  362. }
  363. /// <summary>
  364. /// Returns a value indicating whether this <see cref="CircleF" /> is equal to a specified object.
  365. /// </summary>
  366. /// <param name="obj">The object to make the comparison with.</param>
  367. /// <returns>
  368. /// <c>true</c> if this <see cref="CircleF" /> is equal to <paramref name="obj" />; otherwise, <c>false</c>.
  369. /// </returns>
  370. public override bool Equals(object obj)
  371. {
  372. return obj is CircleF && Equals((CircleF) obj);
  373. }
  374. /// <summary>
  375. /// Returns a hash code of this <see cref="CircleF" /> suitable for use in hashing algorithms and data
  376. /// structures like a hash table.
  377. /// </summary>
  378. /// <returns>
  379. /// A hash code of this <see cref="CircleF" />.
  380. /// </returns>
  381. public override int GetHashCode()
  382. {
  383. unchecked
  384. {
  385. return (Center.GetHashCode() * 397) ^ Radius.GetHashCode();
  386. }
  387. }
  388. /// <summary>
  389. /// Performs an explicit conversion from a <see cref="CircleF" /> to a <see cref="Rectangle" />.
  390. /// </summary>
  391. /// <param name="circle">The circle.</param>
  392. /// <returns>
  393. /// The resulting <see cref="Rectangle" />.
  394. /// </returns>
  395. public static explicit operator Rectangle(CircleF circle)
  396. {
  397. var diameter = (int) circle.Diameter;
  398. return new Rectangle((int) (circle.Center.X - circle.Radius), (int) (circle.Center.Y - circle.Radius),
  399. diameter, diameter);
  400. }
  401. /// <summary>
  402. /// Performs a conversion from a specified <see cref="CircleF" /> to a <see cref="Rectangle" />.
  403. /// </summary>
  404. /// <returns>
  405. /// The resulting <see cref="Rectangle" />.
  406. /// </returns>
  407. public Rectangle ToRectangle()
  408. {
  409. return (Rectangle)this;
  410. }
  411. /// <summary>
  412. /// Performs an explicit conversion from a <see cref="Rectangle" /> to a <see cref="CircleF" />.
  413. /// </summary>
  414. /// <param name="rectangle">The rectangle.</param>
  415. /// <returns>
  416. /// The resulting <see cref="CircleF" />.
  417. /// </returns>
  418. public static explicit operator CircleF(Rectangle rectangle)
  419. {
  420. var halfWidth = rectangle.Width / 2;
  421. var halfHeight = rectangle.Height / 2;
  422. return new CircleF(new Vector2(rectangle.X + halfWidth, rectangle.Y + halfHeight),
  423. halfWidth > halfHeight ? halfWidth : halfHeight);
  424. }
  425. /// <summary>
  426. /// Performs an explicit conversion from a <see cref="CircleF" /> to a <see cref="RectangleF" />.
  427. /// </summary>
  428. /// <param name="circle">The circle.</param>
  429. /// <returns>
  430. /// The resulting <see cref="RectangleF" />.
  431. /// </returns>
  432. public static explicit operator RectangleF(CircleF circle)
  433. {
  434. var diameter = circle.Diameter;
  435. return new RectangleF(circle.Center.X - circle.Radius, circle.Center.Y - circle.Radius, diameter, diameter);
  436. }
  437. /// <summary>
  438. /// Performs a conversion from a specified <see cref="CircleF" /> to a <see cref="RectangleF" />.
  439. /// </summary>
  440. /// <returns>
  441. /// The resulting <see cref="RectangleF" />.
  442. /// </returns>
  443. public RectangleF ToRectangleF()
  444. {
  445. return (RectangleF)this;
  446. }
  447. /// <summary>
  448. /// Performs an explicit conversion from a <see cref="RectangleF" /> to a <see cref="CircleF" />.
  449. /// </summary>
  450. /// <param name="rectangle">The rectangle.</param>
  451. /// <returns>
  452. /// The resulting <see cref="CircleF" />.
  453. /// </returns>
  454. public static explicit operator CircleF(RectangleF rectangle)
  455. {
  456. var halfWidth = rectangle.Width * 0.5f;
  457. var halfHeight = rectangle.Height * 0.5f;
  458. return new CircleF(new Vector2(rectangle.X + halfWidth, rectangle.Y + halfHeight),
  459. halfWidth > halfHeight ? halfWidth : halfHeight);
  460. }
  461. /// <summary>
  462. /// Returns a <see cref="string" /> that represents this <see cref="CircleF" />.
  463. /// </summary>
  464. /// <returns>
  465. /// A <see cref="string" /> that represents this <see cref="CircleF" />.
  466. /// </returns>
  467. public override string ToString()
  468. {
  469. return $"Centre: {Center}, Radius: {Radius}";
  470. }
  471. internal string DebugDisplayString => ToString();
  472. }
  473. }