Segment2.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. using System;
  2. using Microsoft.Xna.Framework;
  3. namespace MonoGame.Extended
  4. {
  5. // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 3.5; A Math and Geometry Primer - Lines, Rays, and Segments. pg 53-54
  6. /// <summary>
  7. /// A two dimensional line segment defined by two <see cref="Vector2" /> structures, a starting point and an ending
  8. /// point.
  9. /// </summary>
  10. /// <seealso cref="IEquatable{T}" />
  11. /// <seealso cref="IEquatableByRef{Segment2}" />
  12. public struct Segment2 : IEquatable<Segment2>, IEquatableByRef<Segment2>
  13. {
  14. /// <summary>
  15. /// The starting <see cref="Vector2" /> of this <see cref="Segment2" />.
  16. /// </summary>
  17. public Vector2 Start;
  18. /// <summary>
  19. /// The ending <see cref="Vector2" /> of this <see cref="Segment2" />.
  20. /// </summary>
  21. public Vector2 End;
  22. /// <summary>
  23. /// Initializes a new instance of the <see cref="Segment2" /> structure from the specified starting and ending
  24. /// <see cref="Vector2" /> structures.
  25. /// </summary>
  26. /// <param name="start">The starting point.</param>
  27. /// <param name="end">The ending point.</param>
  28. public Segment2(Vector2 start, Vector2 end)
  29. {
  30. Start = start;
  31. End = end;
  32. }
  33. /// <summary>
  34. /// Initializes a new instance of the <see cref="Segment2" /> structure.
  35. /// </summary>
  36. /// <param name="x1">The starting x-coordinate.</param>
  37. /// <param name="y1">The starting y-coordinate.</param>
  38. /// <param name="x2">The ending x-coordinate.</param>
  39. /// <param name="y2">The ending y-coordinate.</param>
  40. public Segment2(float x1, float y1, float x2, float y2)
  41. : this(new Vector2(x1, y1), new Vector2(x2, y2))
  42. {
  43. }
  44. // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 5.1.2; Basic Primitive Tests - Closest Point on Line Segment to Point. pg 127-130
  45. /// <summary>
  46. /// Computes the closest <see cref="Vector2" /> on this <see cref="Segment2" /> to a specified <see cref="Vector2" />.
  47. /// </summary>
  48. /// <param name="point">The point.</param>
  49. /// <returns>The closest <see cref="Vector2" /> on this <see cref="Segment2" /> to the <paramref name="point" />.</returns>
  50. public Vector2 ClosestPointTo(Vector2 point)
  51. {
  52. // Computes the parameterized position: d(t) = Start + t * (End – Start)
  53. var startToEnd = End - Start;
  54. var startToPoint = point - Start;
  55. // Project arbitrary point onto the line segment, deferring the division
  56. var t = startToEnd.Dot(startToPoint);
  57. // If outside segment, clamp t (and therefore d) to the closest endpoint
  58. if (t <= 0)
  59. return Start;
  60. // Always nonnegative since denom = (||vector||)^2
  61. var denominator = startToEnd.Dot(startToEnd);
  62. if (t >= denominator)
  63. return End;
  64. // The point projects inside the [Start, End] interval, must do deferred division now
  65. t /= denominator;
  66. startToEnd *= t;
  67. return new Vector2(Start.X + startToEnd.X, Start.Y + startToEnd.Y);
  68. }
  69. // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 5.1.2.1; Basic Primitive Tests - Distance of Point to Segment. pg 127-130
  70. /// <summary>
  71. /// Computes the squared distance from this <see cref="Segment2" /> to a specified <see cref="Vector2" />.
  72. /// </summary>
  73. /// <param name="point">The point.</param>
  74. /// <returns>The squared distance from this <see cref="Segment2" /> to a specified <see cref="Vector2" />.</returns>
  75. public float SquaredDistanceTo(Vector2 point)
  76. {
  77. var startToEnd = End - Start;
  78. var startToPoint = point - Start;
  79. var endToPoint = point - End;
  80. // Handle cases where the point projects outside the line segment
  81. var dot = startToPoint.Dot(startToEnd);
  82. var startToPointDistanceSquared = startToPoint.Dot(startToPoint);
  83. if (dot <= 0.0f)
  84. return startToPointDistanceSquared;
  85. var startToEndDistanceSquared = startToEnd.Dot(startToEnd);
  86. if (dot >= startToEndDistanceSquared)
  87. endToPoint.Dot(endToPoint);
  88. // Handle the case where the point projects onto the line segment
  89. return startToPointDistanceSquared - dot*dot/startToEndDistanceSquared;
  90. }
  91. /// <summary>
  92. /// Computes the distance from this <see cref="Segment2" /> to a specified <see cref="Vector2" />.
  93. /// </summary>
  94. /// <param name="point">The point.</param>
  95. /// <returns>The distance from this <see cref="Segment2" /> to a specified <see cref="Vector2" />.</returns>
  96. public float DistanceTo(Vector2 point)
  97. {
  98. return (float) Math.Sqrt(SquaredDistanceTo(point));
  99. }
  100. /// <summary>
  101. /// Determines whether this <see cref="Segment2" /> intersects with the specified <see cref="BoundingRectangle" />.
  102. /// </summary>
  103. /// <param name="rectangle">The bounding box.</param>
  104. /// <param name="intersectionPoint">
  105. /// When this method returns, contains the <see cref="Vector2" /> of intersection, if an
  106. /// intersection was found; otherwise, the <see cref="Vector2.NaN" />. This parameter is passed uninitialized.
  107. /// </param>
  108. /// <returns>
  109. /// <c>true</c> if this <see cref="Segment2" /> intersects with <paramref name="rectangle" />; otherwise,
  110. /// <c>false</c>.
  111. /// </returns>
  112. public bool Intersects(RectangleF rectangle, out Vector2 intersectionPoint)
  113. {
  114. // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 5.3; Basic Primitive Tests - Intersecting Lines, Rays, and (Directed Segments). pg 179-181
  115. var minimumPoint = rectangle.TopLeft;
  116. var maximumPoint = rectangle.BottomRight;
  117. var minimumDistance = float.MinValue;
  118. var maximumDistance = float.MaxValue;
  119. var direction = End - Start;
  120. if (
  121. !PrimitivesHelper.IntersectsSlab(Start.X, direction.X, minimumPoint.X, maximumPoint.X, ref minimumDistance,
  122. ref maximumDistance))
  123. {
  124. intersectionPoint = new Vector2(float.NaN);
  125. return false;
  126. }
  127. if (
  128. !PrimitivesHelper.IntersectsSlab(Start.Y, direction.Y, minimumPoint.Y, maximumPoint.Y, ref minimumDistance,
  129. ref maximumDistance))
  130. {
  131. intersectionPoint = new Vector2(float.NaN);
  132. return false;
  133. }
  134. // Segment intersects the 2 slabs.
  135. if (minimumDistance <= 0)
  136. intersectionPoint = Start;
  137. else
  138. {
  139. intersectionPoint = minimumDistance * direction;
  140. intersectionPoint.X += Start.X;
  141. intersectionPoint.Y += Start.Y;
  142. }
  143. return true;
  144. }
  145. /// <summary>
  146. /// Determines whether this <see cref="Segment2" /> intersects with the specified <see cref="BoundingRectangle" />.
  147. /// </summary>
  148. /// <param name="boundingRectangle">The bounding box.</param>
  149. /// <param name="intersectionPoint">
  150. /// When this method returns, contains the <see cref="Vector2" /> of intersection, if an
  151. /// intersection was found; otherwise, the <see cref="Vector2.NaN" />. This parameter is passed uninitialized.
  152. /// </param>
  153. /// <returns>
  154. /// <c>true</c> if this <see cref="Segment2" /> intersects with <paramref name="boundingRectangle" />; otherwise,
  155. /// <c>false</c>.
  156. /// </returns>
  157. public bool Intersects(BoundingRectangle boundingRectangle, out Vector2 intersectionPoint)
  158. {
  159. // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 5.3; Basic Primitive Tests - Intersecting Lines, Rays, and (Directed Segments). pg 179-181
  160. var minimumPoint = boundingRectangle.Center - boundingRectangle.HalfExtents;
  161. var maximumPoint = boundingRectangle.Center + boundingRectangle.HalfExtents;
  162. var minimumDistance = float.MinValue;
  163. var maximumDistance = float.MaxValue;
  164. var direction = End - Start;
  165. if (
  166. !PrimitivesHelper.IntersectsSlab(Start.X, direction.X, minimumPoint.X, maximumPoint.X, ref minimumDistance,
  167. ref maximumDistance))
  168. {
  169. intersectionPoint = new Vector2(float.NaN);
  170. return false;
  171. }
  172. if (
  173. !PrimitivesHelper.IntersectsSlab(Start.Y, direction.Y, minimumPoint.Y, maximumPoint.Y, ref minimumDistance,
  174. ref maximumDistance))
  175. {
  176. intersectionPoint = new Vector2(float.NaN);
  177. return false;
  178. }
  179. // Segment intersects the 2 slabs.
  180. if (minimumDistance <= 0)
  181. intersectionPoint = Start;
  182. else
  183. {
  184. intersectionPoint = minimumDistance*direction;
  185. intersectionPoint.X += Start.X;
  186. intersectionPoint.Y += Start.Y;
  187. }
  188. return true;
  189. }
  190. /// <summary>
  191. /// Compares two <see cref="Segment2" /> structures. The result specifies
  192. /// whether the values of the <see cref="Start" /> and <see cref="End" />
  193. /// fields of the two <see cref='Segment2' />
  194. /// structures are equal.
  195. /// </summary>
  196. /// <param name="first">The first segment.</param>
  197. /// <param name="second">The second segment.</param>
  198. /// <returns>
  199. /// <c>true</c> if the <see cref="Start" /> and <see cref="End" />
  200. /// fields of the two <see cref="Segment2" />
  201. /// structures are equal; otherwise, <c>false</c>.
  202. /// </returns>
  203. public static bool operator ==(Segment2 first, Segment2 second)
  204. {
  205. return first.Equals(ref second);
  206. }
  207. /// <summary>
  208. /// Indicates whether this <see cref="Segment2" /> is equal to another <see cref="Segment2" />.
  209. /// </summary>
  210. /// <param name="segment">The segment.</param>
  211. /// <returns>
  212. /// <c>true</c> if this <see cref="Segment2" /> is equal to the <paramref name="segment" />; otherwise, <c>false</c>.
  213. /// </returns>
  214. public bool Equals(Segment2 segment)
  215. {
  216. return Equals(ref segment);
  217. }
  218. /// <summary>
  219. /// Indicates whether this <see cref="Segment2" /> is equal to another <see cref="Segment2" />.
  220. /// </summary>
  221. /// <param name="segment">The segment.</param>
  222. /// <returns>
  223. /// <c>true</c> if this <see cref="Segment2" /> is equal to the <paramref name="segment" /> parameter; otherwise,
  224. /// <c>false</c>.
  225. /// </returns>
  226. public bool Equals(ref Segment2 segment)
  227. {
  228. return (Start == segment.Start) && (End == segment.End);
  229. }
  230. /// <summary>
  231. /// Returns a value indicating whether this <see cref="Segment2" /> is equal to a specified object.
  232. /// </summary>
  233. /// <param name="obj">The object to make the comparison with.</param>
  234. /// <returns>
  235. /// <c>true</c> if this <see cref="Segment2" /> is equal to <paramref name="obj" />; otherwise, <c>false</c>.
  236. /// </returns>
  237. public override bool Equals(object obj)
  238. {
  239. if (obj is Segment2)
  240. return Equals((Segment2) obj);
  241. return false;
  242. }
  243. /// <summary>
  244. /// Compares two <see cref="Segment2" /> structures. The result specifies
  245. /// whether the values of the <see cref="Start" /> and <see cref="End" />
  246. /// fields of the two <see cref="Segment2" />
  247. /// structures are unequal.
  248. /// </summary>
  249. /// <param name="first">The first point.</param>
  250. /// <param name="second">The second point.</param>
  251. /// <returns>
  252. /// <c>true</c> if the <see cref="Start" /> and <see cref="End" />
  253. /// fields of the two <see cref="Segment2" />
  254. /// structures are unequal; otherwise, <c>false</c>.
  255. /// </returns>
  256. public static bool operator !=(Segment2 first, Segment2 second)
  257. {
  258. return !(first == second);
  259. }
  260. /// <summary>
  261. /// Returns a hash code of this <see cref="Segment2" /> suitable for use in hashing algorithms and data
  262. /// structures like a hash table.
  263. /// </summary>
  264. /// <returns>
  265. /// A hash code of this <see cref="Segment2" />.
  266. /// </returns>
  267. public override int GetHashCode()
  268. {
  269. unchecked
  270. {
  271. return (Start.GetHashCode()*397) ^ End.GetHashCode();
  272. }
  273. }
  274. /// <summary>
  275. /// Returns a <see cref="string" /> that represents this <see cref="Segment2" />.
  276. /// </summary>
  277. /// <returns>
  278. /// A <see cref="string" /> that represents this <see cref="Segment2" />.
  279. /// </returns>
  280. public override string ToString()
  281. {
  282. return $"{Start} -> {End}";
  283. }
  284. internal string DebugDisplayString => ToString();
  285. }
  286. }