Vector2Extensions.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using Microsoft.Xna.Framework;
  4. namespace MonoGame.Extended
  5. {
  6. public static class Vector2Extensions
  7. {
  8. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  9. public static Vector2 SetX(this Vector2 vector2, float x) => new Vector2(x, vector2.Y);
  10. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  11. public static Vector2 SetY(this Vector2 vector2, float y) => new Vector2(vector2.X, y);
  12. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  13. public static Vector2 Translate(this Vector2 vector2, float x, float y) => new Vector2(vector2.X + x, vector2.Y + y);
  14. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  15. public static SizeF ToSize(this Vector2 value) => new SizeF(value.X, value.Y);
  16. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  17. public static SizeF ToAbsoluteSize(this Vector2 value)
  18. {
  19. var x = Math.Abs(value.X);
  20. var y = Math.Abs(value.Y);
  21. return new SizeF(x, y);
  22. }
  23. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  24. public static Vector2 Round(this Vector2 value, int digits, MidpointRounding mode)
  25. {
  26. var x = (float)Math.Round(value.X, digits, mode);
  27. var y = (float)Math.Round(value.Y, digits, mode);
  28. return new Vector2(x, y);
  29. }
  30. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  31. public static Vector2 Round(this Vector2 value, int digits)
  32. {
  33. var x = (float)Math.Round(value.X, digits);
  34. var y = (float)Math.Round(value.Y, digits);
  35. return new Vector2(x, y);
  36. }
  37. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  38. public static Vector2 Round(this Vector2 value)
  39. {
  40. var x = (float)Math.Round(value.X);
  41. var y = (float)Math.Round(value.Y);
  42. return new Vector2(x, y);
  43. }
  44. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  45. public static bool EqualsWithTolerence(this Vector2 value, Vector2 otherValue, float tolerance = 0.00001f)
  46. {
  47. return Math.Abs(value.X - otherValue.X) <= tolerance && (Math.Abs(value.Y - otherValue.Y) <= tolerance);
  48. }
  49. #if FNA || KNI
  50. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  51. #else
  52. [Obsolete("Use native Vector2.Rotate provided by MonoGame instead. This will be removed in a future release.", false)]
  53. #endif
  54. public static Vector2 Rotate(this Vector2 value, float radians)
  55. {
  56. var cos = (float) Math.Cos(radians);
  57. var sin = (float) Math.Sin(radians);
  58. return new Vector2(value.X*cos - value.Y*sin, value.X*sin + value.Y*cos);
  59. }
  60. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  61. public static Vector2 NormalizedCopy(this Vector2 value)
  62. {
  63. var newVector2 = new Vector2(value.X, value.Y);
  64. newVector2.Normalize();
  65. return newVector2;
  66. }
  67. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  68. public static Vector2 PerpendicularClockwise(this Vector2 value) => new Vector2(value.Y, -value.X);
  69. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  70. public static Vector2 PerpendicularCounterClockwise(this Vector2 value) => new Vector2(-value.Y, value.X);
  71. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  72. public static Vector2 Truncate(this Vector2 value, float maxLength)
  73. {
  74. if (value.LengthSquared() > maxLength*maxLength)
  75. return value.NormalizedCopy()*maxLength;
  76. return value;
  77. }
  78. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  79. public static bool IsNaN(this Vector2 value) => float.IsNaN(value.X) || float.IsNaN(value.Y);
  80. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  81. public static float ToAngle(this Vector2 value) => (float) Math.Atan2(value.X, -value.Y);
  82. /// <summary>
  83. /// Calculates the dot product of two vectors. If the two vectors are unit vectors, the dot product returns a floating
  84. /// point value between -1 and 1 that can be used to determine some properties of the angle between two vectors. For
  85. /// example, it can show whether the vectors are orthogonal, parallel, or have an acute or obtuse angle between them.
  86. /// </summary>
  87. /// <param name="vector1">The first vector.</param>
  88. /// <param name="vector2">The second vector.</param>
  89. /// <returns>The dot product of the two vectors.</returns>
  90. /// <remarks>
  91. /// <para>The dot product is also known as the inner product.</para>
  92. /// <para>
  93. /// For any two vectors, the dot product is defined as: <c>(vector1.X * vector2.X) + (vector1.Y * vector2.Y).</c>
  94. /// The result of this calculation, plus or minus some margin to account for floating point error, is equal to:
  95. /// <c>Length(vector1) * Length(vector2) * System.Math.Cos(theta)</c>, where <c>theta</c> is the angle between the
  96. /// two vectors.
  97. /// </para>
  98. /// <para>
  99. /// If <paramref name="vector1" /> and <paramref name="vector2" /> are unit vectors, the length of each
  100. /// vector will be equal to 1. So, when <paramref name="vector1" /> and <paramref name="vector2" /> are unit
  101. /// vectors, the dot product is simply equal to the cosine of the angle between the two vectors. For example, both
  102. /// <c>cos</c> values in the following calcuations would be equal in value:
  103. /// <c>vector1.Normalize(); vector2.Normalize(); var cos = vector1.Dot(vector2)</c>,
  104. /// <c>var cos = System.Math.Cos(theta)</c>, where <c>theta</c> is angle in radians betwen the two vectors.
  105. /// </para>
  106. /// <para>
  107. /// If <paramref name="vector1" /> and <paramref name="vector2" /> are unit vectors, without knowing the value of
  108. /// <c>theta</c> or using a potentially processor-intensive trigonometric function, the value of the dot product
  109. /// can tell us the
  110. /// following things:
  111. /// <list type="bullet">
  112. /// <item>
  113. /// <description>
  114. /// If <c>vector1.Dot(vector2) &gt; 0</c>, the angle between the two vectors
  115. /// is less than 90 degrees.
  116. /// </description>
  117. /// </item>
  118. /// <item>
  119. /// <description>
  120. /// If <c>vector1.Dot(vector2) &lt; 0</c>, the angle between the two vectors
  121. /// is more than 90 degrees.
  122. /// </description>
  123. /// </item>
  124. /// <item>
  125. /// <description>
  126. /// If <c>vector1.Dot(vector2) == 0</c>, the angle between the two vectors
  127. /// is 90 degrees; that is, the vectors are othogonal.
  128. /// </description>
  129. /// </item>
  130. /// <item>
  131. /// <description>
  132. /// If <c>vector1.Dot(vector2) == 1</c>, the angle between the two vectors
  133. /// is 0 degrees; that is, the vectors point in the same direction and are parallel.
  134. /// </description>
  135. /// </item>
  136. /// <item>
  137. /// <description>
  138. /// If <c>vector1.Dot(vector2) == -1</c>, the angle between the two vectors
  139. /// is 180 degrees; that is, the vectors point in opposite directions and are parallel.
  140. /// </description>
  141. /// </item>
  142. /// </list>
  143. /// </para>
  144. /// <note type="caution">
  145. /// Because of floating point error, two orthogonal vectors may not return a dot product that is exactly zero. It
  146. /// might be zero plus some amount of floating point error. In your code, you will want to determine what amount of
  147. /// error is acceptable in your calculation, and take that into account when you do your comparisons.
  148. /// </note>
  149. /// </remarks>
  150. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  151. public static float Dot(this Vector2 vector1, Vector2 vector2)
  152. {
  153. return vector1.X*vector2.X + vector1.Y*vector2.Y;
  154. }
  155. /// <summary>
  156. /// Calculates the scalar projection of one vector onto another. The scalar projection returns the length of the
  157. /// orthogonal projection of the first vector onto a straight line parallel to the second vector, with a negative value
  158. /// if the projection has an opposite direction with respect to the second vector.
  159. /// </summary>
  160. /// <param name="vector1">The first vector.</param>
  161. /// <param name="vector2">The second vector.</param>
  162. /// <returns>The scalar projection of <paramref name="vector1" /> onto <paramref name="vector2" />.</returns>
  163. /// <remarks>
  164. /// <para>
  165. /// The scalar projection is also known as the scalar resolute of the first vector in the direction of the second
  166. /// vector.
  167. /// </para>
  168. /// <para>
  169. /// For any two vectors, the scalar projection is defined as: <c>vector1.Dot(vector2) / Length(vector2)</c>. The
  170. /// result of this calculation, plus or minus some margin to account for floating point error, is equal to:
  171. /// <c>Length(vector1) * System.Math.Cos(theta)</c>, where <c>theta</c> is the angle in radians between
  172. /// <paramref name="vector1" /> and <paramref name="vector2" />.
  173. /// </para>
  174. /// <para>
  175. /// The value of the scalar projection can tell us the following things:
  176. /// <list type="bullet">
  177. /// <item>
  178. /// <description>
  179. /// If <c>vector1.ScalarProjectOnto(vector2) &gt;= 0</c>, the angle between <paramref name="vector1" />
  180. /// and <paramref name="vector2" /> is between 0 degrees (exclusive) and 90 degrees (inclusive).
  181. /// </description>
  182. /// </item>
  183. /// <item>
  184. /// <description>
  185. /// If <c>vector1.ScalarProjectOnto(vector2) &lt; 0</c>, the angle between <paramref name="vector1" />
  186. /// and <paramref name="vector2" /> is between 90 degrees (exclusive) and 180 degrees (inclusive).
  187. /// </description>
  188. /// </item>
  189. /// </list>
  190. /// </para>
  191. /// </remarks>
  192. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  193. public static float ScalarProjectOnto(this Vector2 vector1, Vector2 vector2)
  194. {
  195. var dotNumerator = vector1.X*vector2.X + vector1.Y*vector2.Y;
  196. var lengthSquaredDenominator = vector2.X*vector2.X + vector2.Y*vector2.Y;
  197. return dotNumerator/(float) Math.Sqrt(lengthSquaredDenominator);
  198. }
  199. /// <summary>
  200. /// Calculates the vector projection of one vector onto another. The vector projection returns the orthogonal
  201. /// projection of the first vector onto a straight line parallel to the second vector.
  202. /// </summary>
  203. /// <param name="vector1">The first vector.</param>
  204. /// <param name="vector2">The second vector.</param>
  205. /// <returns>The vector projection of <paramref name="vector1" /> onto <paramref name="vector2" />.</returns>
  206. /// <remarks>
  207. /// <para>
  208. /// The vector projection is also known as the vector component or vector resolute of the first vector in the
  209. /// direction of the second vector.
  210. /// </para>
  211. /// <para>
  212. /// For any two vectors, the vector projection is defined as:
  213. /// <c>( vector1.Dot(vector2) / Length(vector2)^2 ) * vector2</c>.
  214. /// The
  215. /// result of this calculation, plus or minus some margin to account for floating point error, is equal to:
  216. /// <c>( Length(vector1) * System.Math.Cos(theta) ) * vector2 / Length(vector2)</c>, where <c>theta</c> is the
  217. /// angle in radians between <paramref name="vector1" /> and <paramref name="vector2" />.
  218. /// </para>
  219. /// <para>
  220. /// This function is easier to compute than <see cref="ScalarProjectOnto" /> since it does not use a square root.
  221. /// When the vector projection and the scalar projection is required, consider using this function; the scalar
  222. /// projection can be obtained by taking the length of the projection vector.
  223. /// </para>
  224. /// </remarks>
  225. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  226. public static Vector2 ProjectOnto(this Vector2 vector1, Vector2 vector2)
  227. {
  228. var dotNumerator = vector1.X*vector2.X + vector1.Y*vector2.Y;
  229. var lengthSquaredDenominator = vector2.X*vector2.X + vector2.Y*vector2.Y;
  230. return dotNumerator/lengthSquaredDenominator*vector2;
  231. }
  232. #if FNA
  233. // MomoGame compatibility layer
  234. /// <summary>
  235. /// Gets a Point representation for this Vector2.
  236. /// </summary>
  237. /// <returns>A Point representation for this Vector2.</returns>
  238. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  239. public static Point ToPoint(this Vector2 value)
  240. {
  241. return new Point((int)value.X, (int)value.Y);
  242. }
  243. /// <summary>
  244. /// Gets a Vector2 representation for this Point.
  245. /// </summary>
  246. /// <returns>A Vector2 representation for this Point.</returns>
  247. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  248. public static Vector2 ToVector2(this Point value)
  249. {
  250. return new Vector2(value.X, value.Y);
  251. }
  252. #endif
  253. }
  254. }