PageRenderTime 28ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/Utilities/Datatypes/Vector.cs

#
C# | 1899 lines | 1064 code | 156 blank | 679 comment | 23 complexity | dbc9c7a13489d6a920ce13d23073319b MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. using Delta.Utilities.Helpers;
  7. using Delta.Utilities.Profiling;
  8. using NUnit.Framework;
  9. // Disable warnings for some tests where we don't use created values
  10. #pragma warning disable 219
  11. namespace Delta.Utilities.Datatypes
  12. {
  13. /// <summary>
  14. /// Vector class, will be automatically merged with XNA, OpenTK and SlimDx
  15. /// vector classes by the build system for quick access and optimized code
  16. /// paths (as it turns out however it seems most of our own methods are
  17. /// faster than those from XNA, OpenTK or SlimDx, see results below in the
  18. /// VectorPerformance class or more importantly MatrixPerformance).
  19. /// </summary>
  20. [Serializable]
  21. [StructLayout(LayoutKind.Explicit)]
  22. [DebuggerDisplay("Vector=({X}, {Y}, {Z})")]
  23. [Description("Expand to edit this Vector")]
  24. [TypeConverter(typeof(ExpandableObjectConverter))]
  25. public struct Vector : ISaveLoadBinary, IEquatable<Vector>
  26. {
  27. #region Constants
  28. /// <summary>
  29. /// Represents the size in bytes of a Vector (3 * 4 = 12 bytes).
  30. /// </summary>
  31. public const int DataSize = 3 * 4;
  32. /// <summary>
  33. /// Returns a Vector with every value set to 0
  34. /// </summary>
  35. public static readonly Vector Zero =
  36. new Vector(0, 0, 0);
  37. /// <summary>
  38. /// Returns a Vector with every value set to 0.5
  39. /// </summary>
  40. public static readonly Vector Half =
  41. new Vector(0.5f, 0.5f, 0.5f);
  42. /// <summary>
  43. /// Returns a Vector with every value set to 1
  44. /// </summary>
  45. public static readonly Vector One =
  46. new Vector(1, 1, 1);
  47. /// <summary>
  48. /// Returns a unit vector on the X axis (1, 0, 0)
  49. /// </summary>
  50. public static readonly Vector UnitX =
  51. new Vector(1, 0, 0);
  52. /// <summary>
  53. /// Returns a unit vector on the Y axis (0, 1, 0)
  54. /// </summary>
  55. public static readonly Vector UnitY =
  56. new Vector(0, 1, 0);
  57. /// <summary>
  58. /// Returns a unit vector on the Z axis (0, 0, 1)
  59. /// </summary>
  60. public static readonly Vector UnitZ =
  61. new Vector(0, 0, 1);
  62. #endregion
  63. #region Add (Static)
  64. /// <summary>
  65. /// Add the components two vectors
  66. /// </summary>
  67. /// <param name="value1">Vector 1</param>
  68. /// <param name="value2">Vector 2</param>
  69. /// <returns>
  70. /// New vector with X, Y and Z added from value1 and value2.
  71. /// </returns>
  72. public static Vector Add(Vector value1, Vector value2)
  73. {
  74. return new Vector(value1.X + value2.X, value1.Y + value2.Y,
  75. value1.Z + value2.Z);
  76. }
  77. /// <summary>
  78. /// Add the components two vectors
  79. /// </summary>
  80. /// <param name="value1">Vector 1</param>
  81. /// <param name="value2">Vector 2</param>
  82. /// <param name="result">
  83. /// New vector with X, Y and Z added from value1 and value2.
  84. /// </param>
  85. public static void Add(ref Vector value1, ref Vector value2,
  86. out Vector result)
  87. {
  88. result = new Vector
  89. {
  90. X = value1.X + value2.X,
  91. Y = value1.Y + value2.Y,
  92. Z = value1.Z + value2.Z
  93. };
  94. }
  95. #endregion
  96. #region Subtract (Static)
  97. /// <summary>
  98. /// Subtract
  99. /// </summary>
  100. /// <param name="value1">Vector 1</param>
  101. /// <param name="value2">Vector 2</param>
  102. /// <param name="result">
  103. /// New vector with X, Y and Z value2 values subtracted from value1.
  104. /// </param>
  105. public static void Subtract(ref Vector value1, ref Vector value2,
  106. out Vector result)
  107. {
  108. result = new Vector
  109. {
  110. X = value1.X - value2.X,
  111. Y = value1.Y - value2.Y,
  112. Z = value1.Z - value2.Z
  113. };
  114. }
  115. #endregion
  116. #region Multiply (Static)
  117. /// <summary>
  118. /// Multiply the components of a vector with the specified factor.
  119. /// </summary>
  120. /// <param name="scaleFactor">scale factor</param>
  121. /// <param name="value1">value 1</param>
  122. /// <returns>Multiplied vector</returns>
  123. public static Vector Multiply(Vector value1, float scaleFactor)
  124. {
  125. return new Vector
  126. {
  127. X = value1.X * scaleFactor,
  128. Y = value1.Y * scaleFactor,
  129. Z = value1.Z * scaleFactor
  130. };
  131. }
  132. /// <summary>
  133. /// Multiply the components of a vector with the specified factor.
  134. /// </summary>
  135. /// <param name="value">Vector value</param>
  136. /// <param name="scaleFactor">scale factor</param>
  137. /// <param name="result">Multiplied vector</param>
  138. public static void Multiply(ref Vector value, float scaleFactor,
  139. ref Vector result)
  140. {
  141. result.X = value.X * scaleFactor;
  142. result.Y = value.Y * scaleFactor;
  143. result.Z = value.Z * scaleFactor;
  144. }
  145. /// <summary>
  146. /// Multiply the components of two vectors
  147. /// </summary>
  148. /// <param name="value1">value 1</param>
  149. /// <param name="value2">value 2</param>
  150. /// <returns>Multiplied vector</returns>
  151. public static Vector Multiply(Vector value1, Vector value2)
  152. {
  153. return new Vector(value1.X * value2.X, value1.Y * value2.Y,
  154. value1.Z * value2.Z);
  155. }
  156. #endregion
  157. #region Divide (Static)
  158. /// <summary>
  159. /// Divide vector through a value
  160. /// </summary>
  161. /// <param name="value1">value 1</param>
  162. /// <param name="value2">value 2</param>
  163. /// <param name="result">Divided vector</param>
  164. public static void Divide(ref Vector value1, float value2,
  165. ref Vector result)
  166. {
  167. float num = 1.0f / value2;
  168. result.X = value1.X * num;
  169. result.Y = value1.Y * num;
  170. result.Z = value1.Z * num;
  171. }
  172. #endregion
  173. #region Negate (Static)
  174. /// <summary>
  175. /// Negate
  176. /// </summary>
  177. /// <param name="value">Vector value to negate</param>
  178. /// <param name="result">Negated vector</param>
  179. public static void Negate(ref Vector value, ref Vector result)
  180. {
  181. result.X = -value.X;
  182. result.Y = -value.Y;
  183. result.Z = -value.Z;
  184. }
  185. #endregion
  186. #region Min (Static)
  187. /// <summary>
  188. /// Return minimum values from 2 vectors (x, y, and z are checked
  189. /// separately). If you use XNA, SlimDX or Delta's fallback code than a
  190. /// vector containing the smallest values will be returned.
  191. /// OpenTK would return the vector with the smallest DistanceSquared,
  192. /// which is wrong for us and won't be used!
  193. /// </summary>
  194. /// <param name="value1">value 1</param>
  195. /// <param name="value2">value 2</param>
  196. /// <returns>minimum values from 2 vectors</returns>
  197. public static Vector Min(Vector value1, Vector value2)
  198. {
  199. return new Vector(
  200. value1.X < value2.X
  201. ? value1.X
  202. : value2.X,
  203. value1.Y < value2.Y
  204. ? value1.Y
  205. : value2.Y,
  206. value1.Z < value2.Z
  207. ? value1.Z
  208. : value2.Z);
  209. }
  210. /// <summary>
  211. /// Minimum
  212. /// </summary>
  213. /// <param name="result">result</param>
  214. /// <param name="value1">value 1</param>
  215. /// <param name="value2">value 2</param>
  216. public static void Min(ref Vector value1, ref Vector value2,
  217. ref Vector result)
  218. {
  219. result.X = (value1.X < value2.X)
  220. ? value1.X
  221. : value2.X;
  222. result.Y = (value1.Y < value2.Y)
  223. ? value1.Y
  224. : value2.Y;
  225. result.Z = (value1.Z < value2.Z)
  226. ? value1.Z
  227. : value2.Z;
  228. }
  229. #endregion
  230. #region Max (Static)
  231. /// <summary>
  232. /// Return maximum values from 2 vectors (largest x, y and z values).
  233. /// If you use XNA, SlimDX or Delta's fallback code than a vector
  234. /// containing the largest values will be returned.
  235. /// OpenTK would return the vector with the biggest DistanceSquared,
  236. /// which is wrong for us and won't be used!
  237. /// </summary>
  238. /// <param name="value1">value 1</param>
  239. /// <param name="value2">value 2</param>
  240. /// <returns>maximum values from 2 vectors</returns>
  241. public static Vector Max(Vector value1, Vector value2)
  242. {
  243. return new Vector(
  244. value1.X > value2.X
  245. ? value1.X
  246. : value2.X,
  247. value1.Y > value2.Y
  248. ? value1.Y
  249. : value2.Y,
  250. value1.Z > value2.Z
  251. ? value1.Z
  252. : value2.Z);
  253. }
  254. /// <summary>
  255. /// Maximum
  256. /// </summary>
  257. /// <param name="result">result</param>
  258. /// <param name="value1">value 1</param>
  259. /// <param name="value2">value 2</param>
  260. public static void Max(ref Vector value1, ref Vector value2,
  261. ref Vector result)
  262. {
  263. result.X = (value1.X > value2.X)
  264. ? value1.X
  265. : value2.X;
  266. result.Y = (value1.Y > value2.Y)
  267. ? value1.Y
  268. : value2.Y;
  269. result.Z = (value1.Z > value2.Z)
  270. ? value1.Z
  271. : value2.Z;
  272. }
  273. #endregion
  274. #region Dot (Static)
  275. /// <summary>
  276. /// Dot product of 2 vectors, will return 1 if vectors are equal,
  277. /// and 0 if vectors are orthogonal (90 degrees) and -1 if vectors
  278. /// pointing into opposite directions.
  279. /// </summary>
  280. /// <param name="vector1">Vector 1</param>
  281. /// <param name="vector2">Vector 2</param>
  282. /// <returns>Dot product</returns>
  283. public static float Dot(Vector vector1, Vector vector2)
  284. {
  285. return
  286. vector1.X * vector2.X +
  287. vector1.Y * vector2.Y +
  288. vector1.Z * vector2.Z;
  289. }
  290. /// <summary>
  291. /// Dot product of 2 vectors, will return 1 if vectors are equal,
  292. /// and 0 if vectors are orthogonal (90 degrees) and -1 if vectors
  293. /// pointing into opposite directions.
  294. /// </summary>
  295. /// <param name="vector1">Vector 1</param>
  296. /// <param name="vector2">Vector 2</param>
  297. /// <param name="result">Dot product</param>
  298. public static void Dot(ref Vector vector1, ref Vector vector2,
  299. out float result)
  300. {
  301. result = (vector1.X * vector2.X) + (vector1.Y * vector2.Y) +
  302. (vector1.Z * vector2.Z);
  303. }
  304. #endregion
  305. #region Clamp (Static)
  306. /// <summary>
  307. /// Clamp. Computing the closest point in an bounding box to a point.
  308. /// Notice that if the point is already inside the box, then this code
  309. /// returns the original point.
  310. /// </summary>
  311. /// <param name="value1">Vector value to clamp</param>
  312. /// <param name="max">
  313. /// Maximum vector (each component is checked individually)
  314. /// </param>
  315. /// <param name="min">
  316. /// Minimum vector (each component is checked individually)
  317. /// </param>
  318. /// <returns>
  319. /// Clamped vector that has all components between min and max.
  320. /// </returns>
  321. public static Vector Clamp(Vector value1, Vector min, Vector max)
  322. {
  323. float x = value1.X;
  324. x = (x > max.X)
  325. ? max.X
  326. : x;
  327. x = (x < min.X)
  328. ? min.X
  329. : x;
  330. float y = value1.Y;
  331. y = (y > max.Y)
  332. ? max.Y
  333. : y;
  334. y = (y < min.Y)
  335. ? min.Y
  336. : y;
  337. float z = value1.Z;
  338. z = (z > max.Z)
  339. ? max.Z
  340. : z;
  341. z = (z < min.Z)
  342. ? min.Z
  343. : z;
  344. return new Vector(x, y, z);
  345. }
  346. /// <summary>
  347. /// Clamp. Computing the closest point in an bounding box to a point.
  348. /// Notice that if the point is already inside the box, then this code
  349. /// returns the original point.
  350. /// </summary>
  351. /// <param name="value1">Vector value to clamp</param>
  352. /// <param name="max">
  353. /// Maximum vector (each component is checked individually)
  354. /// </param>
  355. /// <param name="min">
  356. /// Minimum vector (each component is checked individually)
  357. /// </param>
  358. /// <param name="result">
  359. /// Clamped vector that has all components between min and max.
  360. /// </param>
  361. public static void Clamp(ref Vector value1, ref Vector min,
  362. ref Vector max, ref Vector result)
  363. {
  364. float x = value1.X;
  365. x = (x > max.X)
  366. ? max.X
  367. : x;
  368. x = (x < min.X)
  369. ? min.X
  370. : x;
  371. float y = value1.Y;
  372. y = (y > max.Y)
  373. ? max.Y
  374. : y;
  375. y = (y < min.Y)
  376. ? min.Y
  377. : y;
  378. float z = value1.Z;
  379. z = (z > max.Z)
  380. ? max.Z
  381. : z;
  382. z = (z < min.Z)
  383. ? min.Z
  384. : z;
  385. result.X = x;
  386. result.Y = y;
  387. result.Z = z;
  388. }
  389. #endregion
  390. #region Cross (Static)
  391. /// <summary>
  392. /// Cross product of vector1 and vector2. Please note that if your vectors
  393. /// are not normalized or they are not orthogonal to each other, you should
  394. /// normalize the result if it is used for other calculations requiring
  395. /// normalized vectors (e.g. camera code or for billboards).
  396. /// </summary>
  397. /// <param name="vector1">Vector 1</param>
  398. /// <param name="vector2">Vector 2</param>
  399. /// <returns>Cross product between vector 1 and 2</returns>
  400. public static Vector Cross(Vector vector1, Vector vector2)
  401. {
  402. return new Vector(
  403. vector1.Y * vector2.Z - vector1.Z * vector2.Y,
  404. vector1.Z * vector2.X - vector1.X * vector2.Z,
  405. vector1.X * vector2.Y - vector1.Y * vector2.X);
  406. }
  407. /// <summary>
  408. /// Cross product of vector1 and vector2. Please note that if your vectors
  409. /// are not normalized or they are not orthogonal to each other, you should
  410. /// normalize the result if it is used for other calculations requiring
  411. /// normalized vectors (e.g. camera code or for billboards).
  412. /// </summary>
  413. /// <param name="vector1">Vector 1</param>
  414. /// <param name="vector2">Vector 2</param>
  415. /// <param name="result">Cross product between vector 1 and 2</param>
  416. public static void Cross(ref Vector vector1, ref Vector vector2,
  417. ref Vector result)
  418. {
  419. result.X = (vector1.Y * vector2.Z) - (vector1.Z * vector2.Y);
  420. result.Y = (vector1.Z * vector2.X) - (vector1.X * vector2.Z);
  421. result.Z = (vector1.X * vector2.Y) - (vector1.Y * vector2.X);
  422. }
  423. #endregion
  424. #region Distance (Static)
  425. /// <summary>
  426. /// Distance between two points (DistanceSquared is faster)
  427. /// </summary>
  428. /// <param name="value1">Vector 1</param>
  429. /// <param name="value2">Vector 2</param>
  430. /// <returns>Distance between vectors</returns>
  431. public static float Distance(Vector value1, Vector value2)
  432. {
  433. float distX = value1.X - value2.X;
  434. float distY = value1.Y - value2.Y;
  435. float distZ = value1.Z - value2.Z;
  436. float distSquared = distX * distX + distY * distY + distZ * distZ;
  437. return MathHelper.Sqrt(distSquared);
  438. }
  439. /// <summary>
  440. /// Distance between two points (DistanceSquared is faster)
  441. /// </summary>
  442. /// <param name="value1">Vector 1</param>
  443. /// <param name="value2">Vector 2</param>
  444. /// <param name="result">Distance between vectors</param>
  445. public static void Distance(ref Vector value1, ref Vector value2,
  446. out float result)
  447. {
  448. float distX = value1.X - value2.X;
  449. float distY = value1.Y - value2.Y;
  450. float distZ = value1.Z - value2.Z;
  451. float distSquared = distX * distX + distY * distY + distZ * distZ;
  452. result = MathHelper.Sqrt(distSquared);
  453. }
  454. #endregion
  455. #region DistanceSquared (Static)
  456. /// <summary>
  457. /// Distance squared
  458. /// </summary>
  459. /// <param name="value1">Vector 1</param>
  460. /// <param name="value2">Vector 2</param>
  461. /// <returns>Squared distance between vectors</returns>
  462. public static float DistanceSquared(Vector value1, Vector value2)
  463. {
  464. float distX = value1.X - value2.X;
  465. float distY = value1.Y - value2.Y;
  466. float distZ = value1.Z - value2.Z;
  467. return distX * distX + distY * distY + distZ * distZ;
  468. }
  469. /// <summary>
  470. /// Distance squared
  471. /// </summary>
  472. /// <param name="value1">Vector 1</param>
  473. /// <param name="value2">Vector 2</param>
  474. /// <param name="result">Squared distance between vectors</param>
  475. public static void DistanceSquared(ref Vector value1, ref Vector value2,
  476. out float result)
  477. {
  478. float distX = value1.X - value2.X;
  479. float distY = value1.Y - value2.Y;
  480. float distZ = value1.Z - value2.Z;
  481. result = (distX * distX) + (distY * distY) + (distZ * distZ);
  482. }
  483. #endregion
  484. #region Normalize (Static)
  485. /// <summary>
  486. /// Normalize the given vector and return the normalized version of it.
  487. /// </summary>
  488. /// <param name="value">Vector to normalize</param>
  489. /// <returns>Normalized vector</returns>
  490. public static Vector Normalize(Vector value)
  491. {
  492. float lengthSquared = value.LengthSquared;
  493. if (lengthSquared == 0)
  494. {
  495. return value;
  496. }
  497. float distanceInverse = 1.0f / MathHelper.Sqrt(lengthSquared);
  498. return new Vector(
  499. value.X * distanceInverse,
  500. value.Y * distanceInverse,
  501. value.Z * distanceInverse);
  502. }
  503. /// <summary>
  504. /// Normalize the given vector.
  505. /// </summary>
  506. /// <param name="value">
  507. /// Vector to normalize, will be normalized after calling this method.
  508. /// </param>
  509. public static void Normalize(ref Vector value)
  510. {
  511. float distanceSquared = value.LengthSquared;
  512. if (distanceSquared != 0.0f)
  513. {
  514. float distanceInverse = 1.0f / MathHelper.Sqrt(distanceSquared);
  515. value.X *= distanceInverse;
  516. value.Y *= distanceInverse;
  517. value.Z *= distanceInverse;
  518. }
  519. }
  520. #endregion
  521. #region TransformNormal (Static)
  522. /// <summary>
  523. /// Transform normal (a Vector3 version of Transform, that won't use the
  524. /// translation part of the matrix).
  525. /// </summary>
  526. /// <param name="normal">Normal to transform</param>
  527. /// <param name="matrix">Matrix for the transformation</param>
  528. /// <returns>Transformed normal vector</returns>
  529. public static Vector TransformNormal(Vector normal, Matrix matrix)
  530. {
  531. //Check performance difference again:
  532. return new Vector(
  533. // X
  534. (normal.X * matrix.M11) +
  535. (normal.Y * matrix.M21) +
  536. (normal.Z * matrix.M31),
  537. // Y
  538. (normal.X * matrix.M12) +
  539. (normal.Y * matrix.M22) +
  540. (normal.Z * matrix.M32),
  541. // Z
  542. (normal.X * matrix.M13) +
  543. (normal.Y * matrix.M23) +
  544. (normal.Z * matrix.M33));
  545. }
  546. /// <summary>
  547. /// Transform normal (a Vector3 version of Transform, that won't use the
  548. /// translation part of the matrix).
  549. /// </summary>
  550. /// <param name="normal">
  551. /// The normal vector which will be transformed by the matrix.
  552. /// </param>
  553. /// <param name="matrix">
  554. /// The matrix used for transforming the provided vector.
  555. /// </param>
  556. /// <param name="result">The resulting transformed normal vector.</param>
  557. public static void TransformNormal(ref Vector normal, ref Matrix matrix,
  558. ref Vector result)
  559. {
  560. //Check performance difference again:
  561. result.X =
  562. (normal.X * matrix.M11) +
  563. (normal.Y * matrix.M21) +
  564. (normal.Z * matrix.M31);
  565. result.Y =
  566. (normal.X * matrix.M12) +
  567. (normal.Y * matrix.M22) +
  568. (normal.Z * matrix.M32);
  569. result.Z =
  570. (normal.X * matrix.M13) +
  571. (normal.Y * matrix.M23) +
  572. (normal.Z * matrix.M33);
  573. }
  574. #endregion
  575. #region Transform (Static)
  576. /// <summary>
  577. /// Transform the given vector by the matrix. Note: This method is slower
  578. /// than the ref version, which should be used for performance critical
  579. /// code!
  580. /// </summary>
  581. /// <param name="position">Position vector to transform</param>
  582. /// <param name="matrix">Matrix for the transformation</param>
  583. /// <returns>Transformed vector</returns>
  584. public static Vector Transform(Vector position, Matrix matrix)
  585. {
  586. //Check performance difference again:
  587. return new Vector(
  588. // X
  589. (position.X * matrix.M11) + (position.Y * matrix.M21) +
  590. (position.Z * matrix.M31) + matrix.M41,
  591. // Y
  592. (position.X * matrix.M12) + (position.Y * matrix.M22) +
  593. (position.Z * matrix.M32) + matrix.M42,
  594. // Z
  595. (position.X * matrix.M13) + (position.Y * matrix.M23) +
  596. (position.Z * matrix.M33) + matrix.M43);
  597. }
  598. /// <summary>
  599. /// Transform the given vector by the matrix (faster ref version).
  600. /// </summary>
  601. /// <param name="position">Position vector to transform</param>
  602. /// <param name="matrix">Matrix for the transformation</param>
  603. /// <param name="result">Transformed vector</param>
  604. public static void Transform(ref Vector position, ref Matrix matrix,
  605. out Vector result)
  606. {
  607. //Check performance difference again:
  608. result.X =
  609. (position.X * matrix.M11) +
  610. (position.Y * matrix.M21) +
  611. (position.Z * matrix.M31) +
  612. matrix.M41;
  613. result.Y =
  614. (position.X * matrix.M12) +
  615. (position.Y * matrix.M22) +
  616. (position.Z * matrix.M32) +
  617. matrix.M42;
  618. result.Z =
  619. (position.X * matrix.M13) +
  620. (position.Y * matrix.M23) +
  621. (position.Z * matrix.M33) +
  622. matrix.M43;
  623. }
  624. #endregion
  625. #region AngleBetweenVectors (Static)
  626. /// <summary>
  627. /// Angle between vectors in degrees.
  628. /// http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
  629. /// RadiansToDegrees(atan2(a.y,a.x) - atan2(b.y,b.x)) would only give
  630. /// you 0-180 degrees, but we want full 0-360 degrees with Acos :)
  631. /// <para />
  632. /// Note: If one of the vectors is zero the method we will return 0.0f.
  633. /// </summary>
  634. /// <param name="a">First vector.</param>
  635. /// <param name="b">Second vector.</param>
  636. /// <returns>Angle between the two vectors in the range [0, 360]</returns>
  637. public static float AngleBetweenVectors(Vector a, Vector b)
  638. {
  639. #region Validation
  640. // Having a single zero vector in this method will cause the calculation
  641. // to return 90 degrees which is not right. So we simply return 0f.
  642. if (a == Zero ||
  643. b == Zero)
  644. {
  645. return 0f;
  646. }
  647. #endregion
  648. // We need to normalize the vectors so we get the cos from 0 to 1
  649. // the cos is the dot product of the vectors a and b
  650. float cos = Dot(Normalize(a), Normalize(b));
  651. cos = MathHelper.Clamp(cos, -1.0f, 1.0f);
  652. // NOTE: Special way for 2D vector handling of this
  653. // RadiansToDegrees(atan2(a.y,a.x) - atan2(b.y,b.x))
  654. Vector cross = Cross(a, b);
  655. return cross.Z < 0.0f
  656. ? // cross products directory is upwards
  657. 360 - MathHelper.Acos(cos)
  658. : // else
  659. MathHelper.Acos(cos);
  660. }
  661. #endregion
  662. #region Lerp (Static)
  663. /// <summary>
  664. /// Performs a linear interpolation between two vectors.
  665. /// </summary>
  666. /// <param name="value1">Vector 1</param>
  667. /// <param name="value2">Vector 2</param>
  668. /// <param name="amount">Interpolation amount</param>
  669. /// <returns>Interpolated vector between vector 1 and 2</returns>
  670. public static Vector Lerp(Vector value1, Vector value2, float amount)
  671. {
  672. return new Vector(
  673. MathHelper.Lerp(value1.X, value2.X, amount),
  674. MathHelper.Lerp(value1.Y, value2.Y, amount),
  675. MathHelper.Lerp(value1.Z, value2.Z, amount));
  676. }
  677. /// <summary>
  678. /// Performs a linear interpolation between two vectors.
  679. /// </summary>
  680. /// <param name="value1">Vector 1</param>
  681. /// <param name="value2">Vector 2</param>
  682. /// <param name="amount">Interpolation amount</param>
  683. /// <param name="result">Interpolated vector between vector 1 and 2</param>
  684. public static void Lerp(ref Vector value1, ref Vector value2,
  685. float amount, out Vector result)
  686. {
  687. result = new Vector(
  688. MathHelper.Lerp(value1.X, value2.X, amount),
  689. MathHelper.Lerp(value1.Y, value2.Y, amount),
  690. MathHelper.Lerp(value1.Z, value2.Z, amount));
  691. }
  692. #endregion
  693. #region GetByIndex (Static)
  694. /// <summary>
  695. /// Get a vector side (X, Y or Z) by index (0, 1 or 2).
  696. /// </summary>
  697. /// <param name="index">
  698. /// Index, 0 for X, 1 for Y, 2 for Z, all other values will thrown an
  699. /// IndexOutOfRangeException
  700. /// </param>
  701. /// <param name="vec">Vector for the X, Y or Z values.</param>
  702. /// <exception cref="IndexOutOfRangeException">
  703. /// If index is outside of 0-2.
  704. /// </exception>
  705. /// <returns>X, Y or Z value depending on the index.</returns>
  706. /// <exception cref="IndexOutOfRangeException">
  707. /// Throws index out of range exception if index is not 0, 1 or 2.
  708. /// </exception>
  709. public static float GetByIndex(ref Vector vec, int index)
  710. {
  711. switch (index)
  712. {
  713. case 0:
  714. return vec.X;
  715. case 1:
  716. return vec.Y;
  717. case 2:
  718. return vec.Z;
  719. default:
  720. throw new IndexOutOfRangeException();
  721. }
  722. }
  723. #endregion
  724. #region FromString (Static)
  725. /// <summary>
  726. /// Convert a string to a Vector. The expected format is (x.x, y.y, z.z)
  727. /// </summary>
  728. /// <param name="vectorString">The string containing the values in the
  729. /// correct format.</param>
  730. /// <returns>Vector from string if possible, otherwise Zero</returns>
  731. public static Vector FromString(string vectorString)
  732. {
  733. // Remove the brackets and split the string up into separate values.
  734. vectorString = vectorString.Replace("(", "");
  735. vectorString = vectorString.Replace(")", "");
  736. string[] vectorStrings = vectorString.Split(new[]
  737. {
  738. ',', ' '
  739. },
  740. StringSplitOptions.RemoveEmptyEntries);
  741. // Then check if the length is 3 for 3 values and return the new vector.
  742. // If the length is not 3 than return Vector.Zero.
  743. if (vectorStrings.Length == 3)
  744. {
  745. return new Vector(
  746. vectorStrings[0].FromInvariantString(0.0f),
  747. vectorStrings[1].FromInvariantString(0.0f),
  748. vectorStrings[2].FromInvariantString(0.0f));
  749. }
  750. return Zero;
  751. }
  752. #endregion
  753. #region Framework Union Defines (Public)
  754. #endregion
  755. #region X (Public)
  756. /// <summary>
  757. /// X coordinate. FieldOffset means that we use the defined float in our
  758. /// union vector and value 0 means the first float
  759. /// </summary>
  760. [FieldOffset(0)]
  761. public float X;
  762. #endregion
  763. #region Y (Public)
  764. /// <summary>
  765. /// Y coordinate. FieldOffset means that we use the defined float in our
  766. /// union vector and value 4 means the second float (4 bytes per float).
  767. /// </summary>
  768. [FieldOffset(4)]
  769. public float Y;
  770. #endregion
  771. #region Z (Public)
  772. /// <summary>
  773. /// Z coordinate. FieldOffset means that we use the defined float in our
  774. /// union vector and value 8 means the third float (4 bytes per float).
  775. /// </summary>
  776. [FieldOffset(8)]
  777. public float Z;
  778. #endregion
  779. #region XProperty (Public)
  780. /// <summary>
  781. /// Property-wrapper for using the X field in the editor.
  782. /// </summary>
  783. [Browsable(true)]
  784. [DisplayName("X")]
  785. public float XProperty
  786. {
  787. get
  788. {
  789. return X;
  790. }
  791. set
  792. {
  793. X = value;
  794. }
  795. }
  796. #endregion
  797. #region YProperty (Public)
  798. /// <summary>
  799. /// Property-wrapper for using the Y field in the editor
  800. /// </summary>
  801. [Browsable(true)]
  802. [DisplayName("Y")]
  803. public float YProperty
  804. {
  805. get
  806. {
  807. return Y;
  808. }
  809. set
  810. {
  811. Y = value;
  812. }
  813. }
  814. #endregion
  815. #region ZProperty (Public)
  816. /// <summary>
  817. /// Property-wrapper for using the Z field in the editor
  818. /// </summary>
  819. [Browsable(true)]
  820. [DisplayName("Z")]
  821. public float ZProperty
  822. {
  823. get
  824. {
  825. return Z;
  826. }
  827. set
  828. {
  829. Z = value;
  830. }
  831. }
  832. #endregion
  833. #region Length (Public)
  834. /// <summary>
  835. /// The length of the vector. This takes the square root and thus is
  836. /// slower than using LengthSquared.
  837. /// </summary>
  838. [Browsable(false)]
  839. public float Length
  840. {
  841. get
  842. {
  843. return MathHelper.Sqrt(X * X + Y * Y + Z * Z);
  844. }
  845. }
  846. #endregion
  847. #region LengthSquared (Public)
  848. /// <summary>
  849. /// Length squared, much faster than using Length because we do not
  850. /// have to take the square root.
  851. /// </summary>
  852. [Browsable(false)]
  853. public float LengthSquared
  854. {
  855. get
  856. {
  857. return X * X + Y * Y + Z * Z;
  858. }
  859. }
  860. #endregion
  861. #region IsNormalized (Public)
  862. /// <summary>
  863. /// Is normalized? Will return true if the vector length is 1.0
  864. /// </summary>
  865. [Browsable(false)]
  866. public bool IsNormalized
  867. {
  868. get
  869. {
  870. return MathHelper.Abs(LengthSquared - 1.0f) <
  871. MathHelper.Epsilon * MathHelper.Epsilon;
  872. }
  873. }
  874. #endregion
  875. #region Constructors
  876. /// <summary>
  877. /// Create vector
  878. /// </summary>
  879. /// <param name="value">value</param>
  880. /// <param name="z">z</param>
  881. public Vector(Point value, float z)
  882. : this()
  883. {
  884. X = value.X;
  885. Y = value.Y;
  886. Z = z;
  887. }
  888. /// <summary>
  889. /// Create vector
  890. /// </summary>
  891. /// <param name="setX">Set x</param>
  892. /// <param name="setY">Set y</param>
  893. /// <param name="setZ">Set z</param>
  894. public Vector(float setX, float setY, float setZ)
  895. : this()
  896. {
  897. X = setX;
  898. Y = setY;
  899. Z = setZ;
  900. }
  901. /// <summary>
  902. /// Create vector
  903. /// </summary>
  904. /// <param name="reader">reader</param>
  905. public Vector(BinaryReader reader)
  906. : this()
  907. {
  908. Load(reader);
  909. }
  910. #endregion
  911. #region IEquatable<Vector> Members
  912. /// <summary>
  913. /// Equals
  914. /// </summary>
  915. /// <param name="other">Other</param>
  916. /// <returns>Value indicating the equality of two vectors</returns>
  917. public bool Equals(Vector other)
  918. {
  919. return X == other.X &&
  920. Y == other.Y &&
  921. Z == other.Z;
  922. }
  923. #endregion
  924. #region ISaveLoadBinary Members
  925. /// <summary>
  926. /// Load all vector values from a stream (reads 12 bytes, 3 floats)
  927. /// </summary>
  928. /// <param name="reader">The stream that will be used.</param>
  929. public void Load(BinaryReader reader)
  930. {
  931. X = reader.ReadSingle();
  932. Y = reader.ReadSingle();
  933. Z = reader.ReadSingle();
  934. }
  935. /// <summary>
  936. /// Saves this vector to a stream (12 bytes, 3 floats).
  937. /// </summary>
  938. /// <param name="writer">The stream that will be used.</param>
  939. public void Save(BinaryWriter writer)
  940. {
  941. writer.Write(X);
  942. writer.Write(Y);
  943. writer.Write(Z);
  944. }
  945. #endregion
  946. #region op_Equality (Operator)
  947. /// <summary>
  948. /// Check for equality
  949. /// </summary>
  950. /// <param name="value1">Vector 1</param>
  951. /// <param name="value2">Vector 2</param>
  952. /// <returns>True if the vectors are equal</returns>
  953. public static bool operator ==(Vector value1, Vector value2)
  954. {
  955. return
  956. value1.X == value2.X &&
  957. value1.Y == value2.Y &&
  958. value1.Z == value2.Z;
  959. }
  960. #endregion
  961. #region op_Inequality (Operator)
  962. /// <summary>
  963. /// Check for inequality
  964. /// </summary>
  965. /// <param name="value1">Vector 1</param>
  966. /// <param name="value2">Vector 2</param>
  967. /// <returns>True if the vectors are not equal.</returns>
  968. public static bool operator !=(Vector value1, Vector value2)
  969. {
  970. return
  971. value1.X != value2.X ||
  972. value1.Y != value2.Y ||
  973. value1.Z != value2.Z;
  974. }
  975. #endregion
  976. #region op_Addition (Operator)
  977. /// <summary>
  978. /// Operator for addition
  979. /// </summary>
  980. /// <param name="value1">Vector 1</param>
  981. /// <param name="value2">Vector 2</param>
  982. /// <returns>
  983. /// New vector with X, Y and Z values added from value1 and value2.
  984. /// </returns>
  985. public static Vector operator +(Vector value1, Vector value2)
  986. {
  987. return new Vector(
  988. value1.X + value2.X,
  989. value1.Y + value2.Y,
  990. value1.Z + value2.Z);
  991. }
  992. #endregion
  993. #region op_UnaryNegation (Operator)
  994. /// <summary>
  995. /// Operator for unary negation
  996. /// </summary>
  997. /// <param name="value">Vector value</param>
  998. /// <returns>Negated vector</returns>
  999. public static Vector operator -(Vector value)
  1000. {
  1001. return new Vector(-value.X, -value.Y, -value.Z);
  1002. }
  1003. #endregion
  1004. #region op_Subtraction (Operator)
  1005. /// <summary>
  1006. /// Operator for subtraction
  1007. /// </summary>
  1008. /// <param name="value1">Vector 1</param>
  1009. /// <param name="value2">Vector 2</param>
  1010. /// <returns>X, Y and Z of value2 subtracted from value1</returns>
  1011. public static Vector operator -(Vector value1, Vector value2)
  1012. {
  1013. return new Vector(
  1014. value1.X - value2.X,
  1015. value1.Y - value2.Y,
  1016. value1.Z - value2.Z);
  1017. }
  1018. #endregion
  1019. #region op_Multiply (Operator)
  1020. /// <summary>
  1021. /// Operator for multiplication
  1022. /// </summary>
  1023. /// <param name="value1">Vector 1</param>
  1024. /// <param name="value2">Vector 2</param>
  1025. /// <returns>Dot product, which is the multiplication result</returns>
  1026. public static float operator *(Vector value1, Vector value2)
  1027. {
  1028. float result;
  1029. Dot(ref value1, ref value2, out result);
  1030. return result;
  1031. }
  1032. /// <summary>
  1033. /// Operator for multiplication
  1034. /// </summary>
  1035. /// <param name="value">Vector value</param>
  1036. /// <param name="scaleFactor">Scale factor</param>
  1037. /// <returns>Multiplication result</returns>
  1038. public static Vector operator *(Vector value, float scaleFactor)
  1039. {
  1040. return new Vector(
  1041. value.X * scaleFactor,
  1042. value.Y * scaleFactor,
  1043. value.Z * scaleFactor);
  1044. }
  1045. /// <summary>
  1046. /// Operator for multiplication
  1047. /// </summary>
  1048. /// <param name="scaleFactor">Scale factor</param>
  1049. /// <param name="value">Vector value</param>
  1050. /// <returns>Multiplication result</returns>
  1051. public static Vector operator *(float scaleFactor, Vector value)
  1052. {
  1053. return value * scaleFactor;
  1054. }
  1055. /// <summary>
  1056. /// Operator for multiplication
  1057. /// </summary>
  1058. /// <param name="value">Vector value</param>
  1059. /// <param name="transformMatrix">Transformation matrix</param>
  1060. /// <returns>Multiplication result</returns>
  1061. public static Vector operator *(Vector value, Matrix transformMatrix)
  1062. {
  1063. Vector rotatedVector = TransformNormal(value, transformMatrix);
  1064. return transformMatrix.Translation + rotatedVector;
  1065. }
  1066. #endregion
  1067. #region op_Division (Operator)
  1068. /// <summary>
  1069. /// Operator for division
  1070. /// </summary>
  1071. /// <param name="value">Vector value</param>
  1072. /// <param name="scaleFactor">Scale factor</param>
  1073. /// <returns>Division result</returns>
  1074. public static Vector operator /(Vector value, float scaleFactor)
  1075. {
  1076. return new Vector(
  1077. value.X / scaleFactor,
  1078. value.Y / scaleFactor,
  1079. value.Z / scaleFactor);
  1080. }
  1081. /// <summary>
  1082. /// Op multiply
  1083. /// </summary>
  1084. /// <param name="scaleFactor">Scale factor</param>
  1085. /// <param name="value">Vector value</param>
  1086. /// <returns>Division result</returns>
  1087. public static Vector operator /(float scaleFactor, Vector value)
  1088. {
  1089. return value / scaleFactor;
  1090. }
  1091. #endregion
  1092. #region Normalize (Public)
  1093. /// <summary>
  1094. /// Normalize this vector.
  1095. /// </summary>
  1096. public void Normalize()
  1097. {
  1098. float distanceSquared = LengthSquared;
  1099. if (distanceSquared != 0)
  1100. {
  1101. float distanceInverse = 1.0f / MathHelper.Sqrt(distanceSquared);
  1102. X *= distanceInverse;
  1103. Y *= distanceInverse;
  1104. Z *= distanceInverse;
  1105. }
  1106. }
  1107. #endregion
  1108. #region GetByIndex (Public)
  1109. /// <summary>
  1110. /// Get a vector side (X, Y or Z) by index (0, 1 or 2).
  1111. /// </summary>
  1112. /// <param name="index">
  1113. /// Index, 0 for X, 1 for Y, 2 for Z, all other values will thrown an
  1114. /// IndexOutOfRangeException
  1115. /// </param>
  1116. /// <exception cref="IndexOutOfRangeException">
  1117. /// If index is outside of 0-2.
  1118. /// </exception>
  1119. /// <returns>X, Y or Z value depending on the index.</returns>
  1120. /// <exception cref="IndexOutOfRangeException">Unsupported index</exception>
  1121. public float GetByIndex(int index)
  1122. {
  1123. switch (index)
  1124. {
  1125. case 0:
  1126. return X;
  1127. case 1:
  1128. return Y;
  1129. case 2:
  1130. return Z;
  1131. default:
  1132. throw new IndexOutOfRangeException();
  1133. }
  1134. }
  1135. #endregion
  1136. #region GetHashCode (Public)
  1137. /// <summary>
  1138. /// Get hash code
  1139. /// </summary>
  1140. /// <returns>Hash code from X, Y and Z</returns>
  1141. public override int GetHashCode()
  1142. {
  1143. return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
  1144. }
  1145. #endregion
  1146. #region NearlyEquals (Public)
  1147. /// <summary>
  1148. /// Equals
  1149. /// </summary>
  1150. /// <param name="other">Other</param>
  1151. /// <param name="epsilon">Epsilon difference we allow for
  1152. /// floating imprecission</param>
  1153. /// <returns>Value indicating the equality of two vectors</returns>
  1154. public bool NearlyEquals(Vector other, float epsilon)
  1155. {
  1156. return
  1157. // Allow a difference of the Epsilon (in both directions)
  1158. // for the X value range
  1159. X - epsilon <= other.X &&
  1160. X + epsilon >= other.X &&
  1161. // and Y value range
  1162. Y - epsilon <= other.Y &&
  1163. Y + epsilon >= other.Y &&
  1164. // and Z value range
  1165. Z - epsilon <= other.Z &&
  1166. Z + epsilon >= other.Z;
  1167. }
  1168. #endregion
  1169. #region Equals (Public)
  1170. /// <summary>
  1171. /// Equals
  1172. /// </summary>
  1173. /// <param name="obj">Object to check against</param>
  1174. /// <returns>True if obj is a Vector and is equal to this vector.</returns>
  1175. public override bool Equals(object obj)
  1176. {
  1177. if (obj is Vector)
  1178. {
  1179. return Equals((Vector)obj);
  1180. }
  1181. return base.Equals(obj);
  1182. }
  1183. #endregion
  1184. #region ToPoint (Public)
  1185. /// <summary>
  1186. /// Creates a Point from the X and Y values
  1187. /// </summary>
  1188. /// <returns>Point created from X and Y values of this vector.</returns>
  1189. public Point ToPoint()
  1190. {
  1191. return new Point(X, Y);
  1192. }
  1193. #endregion
  1194. #region ToArray (Public)
  1195. /// <summary>
  1196. /// Returns the vector as float array (X, Y, Z)
  1197. /// </summary>
  1198. /// <returns>
  1199. /// Array with just X, Y and Z float values created from this Vector.
  1200. /// </returns>
  1201. public float[] ToArray()
  1202. {
  1203. return new[]
  1204. {
  1205. X, Y, Z
  1206. };
  1207. }
  1208. #endregion
  1209. #region ToString (Public)
  1210. /// <summary>
  1211. /// To string, also used for the old ToColladaString method, this
  1212. /// is precise enough to be used for saving collada files.
  1213. /// </summary>
  1214. /// <returns>
  1215. /// Text string with the vector in braces, e.g. "(0.4, 2.8)"
  1216. /// </returns>
  1217. public override string ToString()
  1218. {
  1219. return ToString("(", ")");
  1220. }
  1221. /// <summary>
  1222. /// To string, also used for the old ToColladaString method, this
  1223. /// is precise enough to be used for saving collada files.
  1224. /// </summary>
  1225. /// <param name="openBrace">Add open brace string, e.g. "("</param>
  1226. /// <param name="closeBrace">Add close brace string, e.g. ")"</param>
  1227. /// <returns>String with X, Y and Z values with the format 0.0000</returns>
  1228. public string ToString(string openBrace, string closeBrace)
  1229. {
  1230. return
  1231. openBrace + X.ToInvariantString("0.0000") +
  1232. ", " + Y.ToInvariantString("0.0000") +
  1233. ", " + Z.ToInvariantString("0.0000") + closeBrace;
  1234. }
  1235. #endregion
  1236. /// <summary>
  1237. /// Tests
  1238. /// </summary>
  1239. internal class VectorTests
  1240. {
  1241. #region SizeOf (Static)
  1242. /// <summary>
  1243. /// Checks if the size of Point is exactly 8 bytes (2 floats: X and Y)
  1244. /// </summary>
  1245. [Test]
  1246. public static void SizeOf()
  1247. {
  1248. // Vector consists of 3 floats: X, Y and Z
  1249. Assert.Equal(3 * 4, Marshal.SizeOf(typeof(Vector)));
  1250. }
  1251. #endregion
  1252. #region Length (Static)
  1253. /// <summary>
  1254. /// Length
  1255. /// </summary>
  1256. [Test]
  1257. public static void Length()
  1258. {
  1259. Assert.Equal(5, new Vector(0, 0, 5).Length);
  1260. Assert.Equal(1, new Vector(0, 1, 0).Length);
  1261. Assert.Equal(4, new Vector(4, 0, 0).Length);
  1262. Assert.Equal(MathHelper.Sqrt(18), new Vector(3, 3, 0).Length);
  1263. }
  1264. #endregion
  1265. #region LengthSquared (Static)
  1266. /// <summary>
  1267. /// Length squared
  1268. /// </summary>
  1269. [Test]
  1270. public static void LengthSquared()
  1271. {
  1272. Assert.Equal(25, new Vector(0, 0, 5).LengthSquared);
  1273. Assert.Equal(1, new Vector(0, 1, 0).LengthSquared);
  1274. Assert.Equal(16, new Vector(4, 0, 0).LengthSquared);
  1275. Assert.Equal(18, new Vector(3, 3, 0).LengthSquared);
  1276. }
  1277. #endregion
  1278. #region AngleBetweenVectors (Static)
  1279. /// <summary>
  1280. /// Angle between vectors
  1281. /// </summary>
  1282. [Test]
  1283. public static void AngleBetweenVectors()
  1284. {
  1285. Assert.NearlyEqual(0,
  1286. Vector.AngleBetweenVectors(UnitX, UnitX));
  1287. Assert.NearlyEqual(180,
  1288. Vector.AngleBetweenVectors(UnitX, -UnitX));
  1289. Assert.NearlyEqual(90,
  1290. Vector.AngleBetweenVectors(UnitX, UnitY));
  1291. Assert.NearlyEqual(270,
  1292. Vector.AngleBetweenVectors(UnitX, -UnitY));
  1293. Assert.NearlyEqual(225,
  1294. Vector.AngleBetweenVectors(UnitX, new Vector(-1, -1, 0)));
  1295. // Special cases with zero vectors!
  1296. Assert.NearlyEqual(0,
  1297. Vector.AngleBetweenVectors(Zero, UnitX));
  1298. Assert.NearlyEqual(0,
  1299. Vector.AngleBetweenVectors(Zero, Zero));
  1300. }
  1301. #endregion
  1302. #region NearlyEqual (Static)
  1303. /// <summary>
  1304. /// Nearly equal
  1305. /// </summary>
  1306. [Test]
  1307. public static void NearlyEqual()
  1308. {
  1309. // We need here for testing a smaller epsilon, because of the float
  1310. // incorrectness
  1311. const float testEpsilon = MathHelper.Epsilon * 0.01f;
  1312. Vector testVector = new Vector(5, 12, 5);
  1313. //test the equality normally
  1314. Assert.True(new Vector(5, 12, 5).Equals(testVector));
  1315. //test the vector as "object"
  1316. Assert.True(new Vector(5, 12, 5).Equals((object)testVector));
  1317. //check the vectors with epsilon as allowed difference
  1318. Assert.True(testVector.NearlyEquals(
  1319. new Vector(5 + testEpsilon, 12, 5 - testEpsilon), testEpsilon));
  1320. //check the bad case
  1321. Assert.False(testVector.NearlyEquals(
  1322. new Vector(5 + (2 * testEpsilon), 12, 5 - testEpsilon),
  1323. testEpsilon));
  1324. }
  1325. #endregion
  1326. #region ToPoint (Static)
  1327. /// <summary>
  1328. /// To string
  1329. /// </summary>
  1330. [Test]
  1331. public static void ToPoint()
  1332. {
  1333. Assert.Equal(new Point(23, 45),
  1334. new Vector(23, 45, 14).ToPoint());
  1335. }
  1336. #endregion
  1337. #region Equality
  1338. /// <summary>
  1339. /// Equality
  1340. /// </summary>
  1341. [Test]
  1342. public void Equality()
  1343. {
  1344. Vector testVector = new Vector(32, 65, 32);
  1345. Assert.Equal(new Vector(32, 65, 32), testVector);
  1346. Assert.NotEqual(testVector, new Vector(0, 65, 32));
  1347. }
  1348. #endregion
  1349. #region Addition
  1350. /// <summary>
  1351. /// Addition
  1352. /// </summary>
  1353. [Test]
  1354. public void Addition()
  1355. {
  1356. Vector testVector = new Vector(32, 65, 32);
  1357. Assert.Equal(new Vector(64, 130, 64), testVector + testVector);
  1358. Assert.Equal(new Vector(32, 66.45f, 34),
  1359. testVector + new Vector(0, 1.45f, 2f));
  1360. }
  1361. #endregion
  1362. #region Substraction
  1363. /// <summary>
  1364. /// Substraction
  1365. /// </summary>
  1366. [Test]
  1367. public void Substraction()
  1368. {
  1369. Vector testVector = new Vector(32, 65, 32);
  1370. Assert.Equal(new Vector(0, 0, 0), testVector - testVector);
  1371. Assert.Equal(new Vector(32, 63.55f, 30),
  1372. testVector - new Vector(0, 1.45f, 2f));
  1373. }
  1374. #endregion
  1375. #region Multiplication
  1376. /// <summary>
  1377. /// Multiplication
  1378. /// </summary>
  1379. [Test]
  1380. public void Multiplication()
  1381. {
  1382. Assert.Equal(Vector.Dot(new Vector(5, 5, 5), new Vector(2, 2.5f, 4)),
  1383. new Vector(5, 5, 5) * new Vector(2, 2.5f, 4));
  1384. //check with vector and scale factor
  1385. Assert.Equal(new Vector(5, 2.5f, 16), new Vector(10, 5, 32) * 0.5f);
  1386. //check with vector and scale factor
  1387. Assert.Equal(new Vector(5, 2.5f, 16), 0.5f * new Vector(10, 5, 32));
  1388. }
  1389. #endregion
  1390. #region Division
  1391. /// <summary>
  1392. /// Multiplication
  1393. /// </summary>
  1394. [Test]
  1395. public void Division()
  1396. {
  1397. //check with vector and scale factor
  1398. Assert.Equal(new Vector(20, 10, 64), new Vector(10, 5, 32) / 0.5f);
  1399. //check with vector and scale factor
  1400. Assert.Equal(new Vector(20, 10, 64), 0.5f / new Vector(10, 5, 32));
  1401. }
  1402. #endregion
  1403. #region Min
  1404. /// <summary>
  1405. /// Minimum
  1406. /// </summary>
  1407. [Test]
  1408. public void Min()
  1409. {
  1410. Assert.Equal(new Vector(43, 0, 32),
  1411. Vector.Min(new Vector(43, 3, 32), new Vector(50, 0, 40)));
  1412. }
  1413. #endregion
  1414. #region Max
  1415. /// <summary>
  1416. /// Maximum
  1417. /// </summary>
  1418. [Test]
  1419. public void Max()
  1420. {
  1421. Assert.Equal(new Vector(50, 3, 40),
  1422. Vector.Max(new Vector(43, 3, 32), new Vector(50, 0, 40)));
  1423. }
  1424. #endregion
  1425. #region Dot
  1426. /// <summary>
  1427. /// Dot
  1428. /// </summary>
  1429. [Test]
  1430. public void Dot()
  1431. {
  1432. Assert.Equal(42.5f,
  1433. Vector.Dot(new Vector(5, 5, 5), new Vector(2, 2.5f, 4)));
  1434. }
  1435. #endregion
  1436. #region Clamp
  1437. /// <summary>
  1438. /// Clamp
  1439. /// </summary>
  1440. [Test]
  1441. public void Clamp()
  1442. {
  1443. Assert.Equal(new Vector(6, 2.6f, 86),
  1444. Vector.Clamp(new Vector(5, 3, 86), new Vector(6, 2, 50),
  1445. new Vector(10, 2.6f, 86)));
  1446. }
  1447. #endregion
  1448. #region Cross
  1449. /// <summary>
  1450. /// Cross
  1451. /// </summary>
  1452. [Test]
  1453. public void Cross()
  1454. {
  1455. Assert.Equal(new Vector(-11, -7, 20),
  1456. Vector.Cross(new Vector(4, 8, 5), new Vector(1, 7, 3)));
  1457. }
  1458. #endregion
  1459. #region Distance
  1460. /// <summary>
  1461. /// Distance
  1462. /// </summary>
  1463. [Test]
  1464. public void Distance()
  1465. {
  1466. Vector testVector1 = new Vector(5, 10, 4);
  1467. Vector testVector2 = new Vector(1, 5, 3);
  1468. Assert.Equal(MathHelper.Sqrt(42), Vector.Distance(testVector1, testVector2));
  1469. }
  1470. #endregion
  1471. #region DistanceSquared
  1472. /// <summary>
  1473. /// Distance squared
  1474. /// </summary>
  1475. [Test]
  1476. public void DistanceSquared()
  1477. {
  1478. Vector testVector1 = new Vector(5, 10, 4);
  1479. Vector testVector2 = new Vector(1, 5, 3);
  1480. Assert.Equal(42, Vector.DistanceSquared(testVector1, testVector2));
  1481. }
  1482. #endregion
  1483. #region Normalize
  1484. /// <summary>
  1485. /// Normalize
  1486. /// </summary>
  1487. [Test]
  1488. public void Normalize()
  1489. {
  1490. Vector testVector = new Vector(1, 5, 3);
  1491. testVector.Normalize();
  1492. Assert.NotEqual(new Vector(1, 5, 3), testVector);
  1493. testVector = new Vector(0, 0, -100);
  1494. testVector.Normalize();
  1495. Assert.Equal(new Vector(0, 0, -1), testVector);
  1496. }
  1497. #endregion
  1498. #region TransformNormal
  1499. /// <summary>
  1500. /// Transform normal
  1501. /// </summary>
  1502. [Test]
  1503. public void TransformNormal()
  1504. {
  1505. Matrix testMatrix = new Matrix(
  1506. 4, 7, 2, 0,
  1507. 4, 3, 8, 5,
  1508. 4, 2, 8, 4,
  1509. 2, 1, 8, 4);
  1510. Vector testVector = new Vector(4, 8, 1);
  1511. Assert.Equal(new Vector(52, 54, 80),
  1512. Vector.TransformNormal(testVector, testMatrix));
  1513. }
  1514. #endregion
  1515. #region VectorToString
  1516. /// <summary>
  1517. /// To string
  1518. /// </summary>
  1519. [Test]
  1520. public void VectorToString()
  1521. {
  1522. Assert.Equal("(23.0000, 45.0000, 5.0000)",
  1523. new Vector(23, 45, 5).ToString());
  1524. }
  1525. #endregion
  1526. }
  1527. /// <summary>
  1528. /// Vector performance class to figure out performance differences between
  1529. /// different implementations of Vector methods available on different
  1530. /// platforms and in different frameworks.
  1531. /// </summary>
  1532. [NUnit.Framework.Category("LongRunning")]
  1533. public class VectorPerformance
  1534. {
  1535. #region TestLength Performance
  1536. /// <summary>
  1537. /// Test the length property of the Vector class.
  1538. /// </summary>
  1539. [Test]
  1540. public static void TestLength()
  1541. {
  1542. Vector testVector = new Vector(10, 20, 30);
  1543. // Results (Release)
  1544. // -------
  1545. // 2010-04-08
  1546. // Delta: x ms
  1547. // XNA: 350 ms
  1548. // OpenTK: x ms
  1549. // SlimDx: x ms
  1550. float length = 0;
  1551. PerformanceTester.Profile10MilionTimes("Vector.Length", delegate
  1552. {
  1553. length = testVector.Length;
  1554. });
  1555. Assert.Equal(length, testVector.Length);
  1556. // Results (Release)
  1557. // -------
  1558. // 2010-04-08
  1559. // Delta: x ms
  1560. // XNA: 350 ms
  1561. // OpenTK: x ms
  1562. // SlimDx: x ms
  1563. float lengthSquared = 0;
  1564. PerformanceTester.Profile10MilionTimes("Vector.LengthSquared", delegate
  1565. {
  1566. lengthSquared = testVector.LengthSquared;
  1567. });
  1568. Assert.Equal(lengthSquared, testVector.LengthSquared);
  1569. }
  1570. #endregion
  1571. #region TestDot Performance
  1572. /// <summary>
  1573. /// Test the dot method of the vector struct.
  1574. /// </summary>
  1575. [Test]
  1576. public static void TestDot()
  1577. {
  1578. Vector testVector1 = new Vector(10, 20, 30);
  1579. Vector testVector2 = new Vector(3, 2, 1);
  1580. // Results (Release)
  1581. // -------
  1582. // 2010-04-08
  1583. // Delta: x ms
  1584. // XNA: 350 ms
  1585. // OpenTK: x ms
  1586. // SlimDx: x ms
  1587. float dot = 0;
  1588. PerformanceTester.Profile10MilionTimes("Vector.Dot", delegate
  1589. {
  1590. dot = Dot(testVector1, testVector2);
  1591. });
  1592. Assert.Equal(dot, Dot(testVector1, testVector2));
  1593. }
  1594. #endregion
  1595. #region TestCross Performance
  1596. /// <summary>
  1597. /// Test the cross method of the vector struct.
  1598. /// </summary>
  1599. [Test]
  1600. public static void TestCross()
  1601. {
  1602. Vector testVector1 = new Vector(10, 20, 30);
  1603. Vector testVector2 = new Vector(3, 2, 1);
  1604. // Results (Release)
  1605. // -------
  1606. // 2010-04-08
  1607. // Delta: x ms
  1608. // XNA: 350 ms
  1609. // OpenTK: x ms
  1610. // SlimDx: x ms
  1611. Vector cross = Zero;
  1612. PerformanceTester.Profile10MilionTimes("Vector.Cross", delegate
  1613. {
  1614. cross = Cross(testVector1, testVector2);
  1615. });
  1616. Assert.Equal(cross, Cross(testVector1, testVector2));
  1617. }
  1618. #endregion
  1619. #region TestDistance Performance
  1620. /// <summary>
  1621. /// Test the distance method of the vector struct.
  1622. /// </summary>
  1623. [Test]
  1624. public static void TestDistance()
  1625. {
  1626. Vector testVector1 = new Vector(10, 20, 30);
  1627. Vector testVector2 = new Vector(3, 2, 1);
  1628. // Results (Release)
  1629. // -------
  1630. // 2010-04-08
  1631. // Delta: x ms
  1632. // XNA: 350 ms
  1633. // OpenTK: x ms
  1634. // SlimDx: x ms
  1635. float distance = 0;
  1636. PerformanceTester.Profile10MilionTimes("Vector.Distance", delegate
  1637. {
  1638. distance = Distance(testVector1, testVector2);
  1639. });
  1640. Assert.Equal(distance, Distance(testVector1, testVector2));
  1641. }
  1642. #endregion
  1643. #region TestNormalize Performance
  1644. /// <summary>
  1645. /// Test the normalize method of the vector struct.
  1646. /// </summary>
  1647. [Test]
  1648. public static void TestNormalize()
  1649. {
  1650. Vector testVector1 = new Vector(10, 20, 30);
  1651. // Results (Release)
  1652. // -------
  1653. // 2010-04-08
  1654. // Delta: x ms
  1655. // XNA: 350 ms
  1656. // OpenTK: x ms
  1657. // SlimDx: x ms
  1658. Vector vector = Zero;
  1659. PerformanceTester.Profile10MilionTimes("Vector.Normalize", delegate
  1660. {
  1661. vector = Normalize(testVector1);
  1662. });
  1663. Assert.Equal(vector, Normalize(testVector1));
  1664. }
  1665. #endregion
  1666. #region TestTransformNormal Performance
  1667. /// <summary>
  1668. /// Test the transform normal method of the vector struct.
  1669. /// </summary>
  1670. [Test]
  1671. public static void TestTransformNormal()
  1672. {
  1673. Vector testVector1 = new Vector(10, 20, 30);
  1674. Matrix testMatrix = Matrix.CreateRotationX(90);
  1675. // Results (Release)
  1676. // -------
  1677. // 2010-04-08
  1678. // Delta: x ms
  1679. // XNA: 350 ms
  1680. // OpenTK: x ms
  1681. // SlimDx: x ms
  1682. Vector vector = Zero;
  1683. PerformanceTester.Profile10MilionTimes(
  1684. "Vector.TransformNormal", delegate
  1685. {
  1686. vector = TransformNormal(testVector1, testMatrix);
  1687. });
  1688. Assert.Equal(vector, TransformNormal(testVector1, testMatrix));
  1689. }
  1690. #endregion
  1691. #region ExecuteAllForPerformanceOverview Performance
  1692. /// <summary>
  1693. /// Execute all vector tests for a performance overview.
  1694. /// </summary>
  1695. [Test]
  1696. public static void ExecuteAllForPerformanceOverview()
  1697. {
  1698. // Also doing now some Vector and Point tests now, used heavily
  1699. // in the engine.
  1700. // Warm up the CPU with some stress testing (for several secs) :)
  1701. TestLength();
  1702. PerformanceTester.ShowTotalProfileRuns();
  1703. Log.Test("Starting testing again after warming up!");
  1704. //Log.Test("Delta Vector test:");
  1705. Log.Test("XNA Vector test:");
  1706. //Log.Test("OpenTK Vector test:");
  1707. //Log.Test("SlimDX Vector test:");
  1708. TestLength();
  1709. TestDot();
  1710. TestCross();
  1711. TestDistance();
  1712. TestNormalize();
  1713. TestTransformNormal();
  1714. PerformanceTester.ShowTotalProfileRuns();
  1715. // Result: In all cases our code is faster than calling XNA code!
  1716. // This could be different on certain platforms (e.g. Xbox 360),
  1717. // we still need to check that!
  1718. // Vector performance:
  1719. /*XNA Vector test (all disabled now):
  1720. 140ms for Vector.Length (10 million calls)
  1721. 66ms for Vector.LengthSquared (10 million calls)
  1722. 207ms for Vector.Dot (10 million calls)
  1723. 224ms for Vector.Cross (10 million calls)
  1724. 231ms for Vector.Distance (10 million calls)
  1725. 291ms for Vector.Normalize (10 million calls)
  1726. 438ms for Vector.TransformNormal (10 million calls)
  1727. In total 7 Tests were executed. Total time=1597ms, Average time=228.1ms.
  1728. */
  1729. /*Delta Vector test:
  1730. 109ms for Vector.Length (10 million calls)
  1731. 47ms for Vector.LengthSquared (10 million calls)
  1732. 91ms for Vector.Dot (10 million calls)
  1733. 111ms for Vector.Cross (10 million calls)
  1734. 110ms for Vector.Distance (10 million calls)
  1735. 276ms for Vector.Normalize (10 million calls)
  1736. 232ms for Vector.TransformNormal (10 million calls)
  1737. In total 7 Tests were executed. Total time=976ms, Average time=139.4ms.
  1738. */
  1739. }
  1740. #endregion
  1741. }
  1742. }
  1743. }