Vector2Extensions.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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. /// <summary>
  51. /// Rotates a vector by the specified angle.
  52. /// </summary>
  53. /// <param name="vector">The vector to rotate.</param>
  54. /// <param name="angle">The rotation angle in radians (counter-clockwise).</param>
  55. /// <returns>The rotated vector.</returns>
  56. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  57. public static Vector2 Rotate(this Vector2 vector, float angle)
  58. {
  59. float cos = (float) Math.Cos(angle);
  60. float sin = (float) Math.Sin(angle);
  61. return new Vector2(
  62. vector.X * cos - vector.Y * sin,
  63. vector.X * sin + vector.Y * cos
  64. );
  65. }
  66. #endif
  67. /// <summary>
  68. /// Rotates a vector by the negative of the specified angle (inverse rotation).
  69. /// </summary>
  70. /// <param name="vector">The vector to rotate.</param>
  71. /// <param name="angle">The rotation angle in radians (the inverse will be applied).</param>
  72. /// <returns>The inversely rotated vector.</returns>
  73. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  74. public static Vector2 RotateInverse(this Vector2 vector, float angle)
  75. {
  76. float cos = MathF.Cos(-angle);
  77. float sin = MathF.Sin(-angle);
  78. return new Vector2(
  79. vector.X * cos - vector.Y * sin,
  80. vector.X * sin + vector.Y * cos
  81. );
  82. }
  83. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  84. public static Vector2 NormalizedCopy(this Vector2 value)
  85. {
  86. var newVector2 = new Vector2(value.X, value.Y);
  87. newVector2.Normalize();
  88. return newVector2;
  89. }
  90. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  91. public static Vector2 PerpendicularClockwise(this Vector2 value) => new Vector2(value.Y, -value.X);
  92. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  93. public static Vector2 PerpendicularCounterClockwise(this Vector2 value) => new Vector2(-value.Y, value.X);
  94. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  95. public static Vector2 Truncate(this Vector2 value, float maxLength)
  96. {
  97. if (value.LengthSquared() > maxLength*maxLength)
  98. return value.NormalizedCopy()*maxLength;
  99. return value;
  100. }
  101. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  102. public static bool IsNaN(this Vector2 value) => float.IsNaN(value.X) || float.IsNaN(value.Y);
  103. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  104. public static float ToAngle(this Vector2 value) => (float) Math.Atan2(value.X, -value.Y);
  105. /// <summary>
  106. /// Calculates the dot product of two vectors. If the two vectors are unit vectors, the dot product returns a floating
  107. /// point value between -1 and 1 that can be used to determine some properties of the angle between two vectors. For
  108. /// example, it can show whether the vectors are orthogonal, parallel, or have an acute or obtuse angle between them.
  109. /// </summary>
  110. /// <param name="vector1">The first vector.</param>
  111. /// <param name="vector2">The second vector.</param>
  112. /// <returns>The dot product of the two vectors.</returns>
  113. /// <remarks>
  114. /// <para>The dot product is also known as the inner product.</para>
  115. /// <para>
  116. /// For any two vectors, the dot product is defined as: <c>(vector1.X * vector2.X) + (vector1.Y * vector2.Y).</c>
  117. /// The result of this calculation, plus or minus some margin to account for floating point error, is equal to:
  118. /// <c>Length(vector1) * Length(vector2) * System.Math.Cos(theta)</c>, where <c>theta</c> is the angle between the
  119. /// two vectors.
  120. /// </para>
  121. /// <para>
  122. /// If <paramref name="vector1" /> and <paramref name="vector2" /> are unit vectors, the length of each
  123. /// vector will be equal to 1. So, when <paramref name="vector1" /> and <paramref name="vector2" /> are unit
  124. /// vectors, the dot product is simply equal to the cosine of the angle between the two vectors. For example, both
  125. /// <c>cos</c> values in the following calcuations would be equal in value:
  126. /// <c>vector1.Normalize(); vector2.Normalize(); var cos = vector1.Dot(vector2)</c>,
  127. /// <c>var cos = System.Math.Cos(theta)</c>, where <c>theta</c> is angle in radians betwen the two vectors.
  128. /// </para>
  129. /// <para>
  130. /// If <paramref name="vector1" /> and <paramref name="vector2" /> are unit vectors, without knowing the value of
  131. /// <c>theta</c> or using a potentially processor-intensive trigonometric function, the value of the dot product
  132. /// can tell us the
  133. /// following things:
  134. /// <list type="bullet">
  135. /// <item>
  136. /// <description>
  137. /// If <c>vector1.Dot(vector2) &gt; 0</c>, the angle between the two vectors
  138. /// is less than 90 degrees.
  139. /// </description>
  140. /// </item>
  141. /// <item>
  142. /// <description>
  143. /// If <c>vector1.Dot(vector2) &lt; 0</c>, the angle between the two vectors
  144. /// is more than 90 degrees.
  145. /// </description>
  146. /// </item>
  147. /// <item>
  148. /// <description>
  149. /// If <c>vector1.Dot(vector2) == 0</c>, the angle between the two vectors
  150. /// is 90 degrees; that is, the vectors are othogonal.
  151. /// </description>
  152. /// </item>
  153. /// <item>
  154. /// <description>
  155. /// If <c>vector1.Dot(vector2) == 1</c>, the angle between the two vectors
  156. /// is 0 degrees; that is, the vectors point in the same direction and are parallel.
  157. /// </description>
  158. /// </item>
  159. /// <item>
  160. /// <description>
  161. /// If <c>vector1.Dot(vector2) == -1</c>, the angle between the two vectors
  162. /// is 180 degrees; that is, the vectors point in opposite directions and are parallel.
  163. /// </description>
  164. /// </item>
  165. /// </list>
  166. /// </para>
  167. /// <note type="caution">
  168. /// Because of floating point error, two orthogonal vectors may not return a dot product that is exactly zero. It
  169. /// might be zero plus some amount of floating point error. In your code, you will want to determine what amount of
  170. /// error is acceptable in your calculation, and take that into account when you do your comparisons.
  171. /// </note>
  172. /// </remarks>
  173. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  174. public static float Dot(this Vector2 vector1, Vector2 vector2)
  175. {
  176. return vector1.X*vector2.X + vector1.Y*vector2.Y;
  177. }
  178. /// <summary>
  179. /// Calculates the scalar projection of one vector onto another. The scalar projection returns the length of the
  180. /// orthogonal projection of the first vector onto a straight line parallel to the second vector, with a negative value
  181. /// if the projection has an opposite direction with respect to the second vector.
  182. /// </summary>
  183. /// <param name="vector1">The first vector.</param>
  184. /// <param name="vector2">The second vector.</param>
  185. /// <returns>The scalar projection of <paramref name="vector1" /> onto <paramref name="vector2" />.</returns>
  186. /// <remarks>
  187. /// <para>
  188. /// The scalar projection is also known as the scalar resolute of the first vector in the direction of the second
  189. /// vector.
  190. /// </para>
  191. /// <para>
  192. /// For any two vectors, the scalar projection is defined as: <c>vector1.Dot(vector2) / Length(vector2)</c>. The
  193. /// result of this calculation, plus or minus some margin to account for floating point error, is equal to:
  194. /// <c>Length(vector1) * System.Math.Cos(theta)</c>, where <c>theta</c> is the angle in radians between
  195. /// <paramref name="vector1" /> and <paramref name="vector2" />.
  196. /// </para>
  197. /// <para>
  198. /// The value of the scalar projection can tell us the following things:
  199. /// <list type="bullet">
  200. /// <item>
  201. /// <description>
  202. /// If <c>vector1.ScalarProjectOnto(vector2) &gt;= 0</c>, the angle between <paramref name="vector1" />
  203. /// and <paramref name="vector2" /> is between 0 degrees (exclusive) and 90 degrees (inclusive).
  204. /// </description>
  205. /// </item>
  206. /// <item>
  207. /// <description>
  208. /// If <c>vector1.ScalarProjectOnto(vector2) &lt; 0</c>, the angle between <paramref name="vector1" />
  209. /// and <paramref name="vector2" /> is between 90 degrees (exclusive) and 180 degrees (inclusive).
  210. /// </description>
  211. /// </item>
  212. /// </list>
  213. /// </para>
  214. /// </remarks>
  215. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  216. public static float ScalarProjectOnto(this Vector2 vector1, Vector2 vector2)
  217. {
  218. var dotNumerator = vector1.X*vector2.X + vector1.Y*vector2.Y;
  219. var lengthSquaredDenominator = vector2.X*vector2.X + vector2.Y*vector2.Y;
  220. return dotNumerator/(float) Math.Sqrt(lengthSquaredDenominator);
  221. }
  222. /// <summary>
  223. /// Calculates the vector projection of one vector onto another. The vector projection returns the orthogonal
  224. /// projection of the first vector onto a straight line parallel to the second vector.
  225. /// </summary>
  226. /// <param name="vector1">The first vector.</param>
  227. /// <param name="vector2">The second vector.</param>
  228. /// <returns>The vector projection of <paramref name="vector1" /> onto <paramref name="vector2" />.</returns>
  229. /// <remarks>
  230. /// <para>
  231. /// The vector projection is also known as the vector component or vector resolute of the first vector in the
  232. /// direction of the second vector.
  233. /// </para>
  234. /// <para>
  235. /// For any two vectors, the vector projection is defined as:
  236. /// <c>( vector1.Dot(vector2) / Length(vector2)^2 ) * vector2</c>.
  237. /// The
  238. /// result of this calculation, plus or minus some margin to account for floating point error, is equal to:
  239. /// <c>( Length(vector1) * System.Math.Cos(theta) ) * vector2 / Length(vector2)</c>, where <c>theta</c> is the
  240. /// angle in radians between <paramref name="vector1" /> and <paramref name="vector2" />.
  241. /// </para>
  242. /// <para>
  243. /// This function is easier to compute than <see cref="ScalarProjectOnto" /> since it does not use a square root.
  244. /// When the vector projection and the scalar projection is required, consider using this function; the scalar
  245. /// projection can be obtained by taking the length of the projection vector.
  246. /// </para>
  247. /// </remarks>
  248. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  249. public static Vector2 ProjectOnto(this Vector2 vector1, Vector2 vector2)
  250. {
  251. var dotNumerator = vector1.X*vector2.X + vector1.Y*vector2.Y;
  252. var lengthSquaredDenominator = vector2.X*vector2.X + vector2.Y*vector2.Y;
  253. return dotNumerator/lengthSquaredDenominator*vector2;
  254. }
  255. #if FNA
  256. // MomoGame compatibility layer
  257. /// <summary>
  258. /// Gets a Point representation for this Vector2.
  259. /// </summary>
  260. /// <returns>A Point representation for this Vector2.</returns>
  261. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  262. public static Point ToPoint(this Vector2 value)
  263. {
  264. return new Point((int)value.X, (int)value.Y);
  265. }
  266. /// <summary>
  267. /// Gets a Vector2 representation for this Point.
  268. /// </summary>
  269. /// <returns>A Vector2 representation for this Point.</returns>
  270. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  271. public static Vector2 ToVector2(this Point value)
  272. {
  273. return new Vector2(value.X, value.Y);
  274. }
  275. #endif
  276. }
  277. }