PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Datatypes/Point.cs

#
C# | 1261 lines | 684 code | 103 blank | 474 comment | 16 complexity | ba0c09e8e31902208f30d2c918054ebc 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 NUnit.Framework;
  8. namespace Delta.Utilities.Datatypes
  9. {
  10. /// <summary>
  11. /// Point class, can also be used as a Vector2 (because we got floats here).
  12. /// </summary>
  13. [Serializable]
  14. [StructLayout(LayoutKind.Explicit)]
  15. [DebuggerDisplay("Point=({X}, {Y})")]
  16. [Description("Expand to edit this Point")]
  17. [TypeConverter(typeof(ExpandableObjectConverter))]
  18. public struct Point : ISaveLoadBinary, IEquatable<Point>
  19. {
  20. #region Constants
  21. /// <summary>
  22. /// Represents the size in bytes of each Point (2 * 4 = 8 bytes).
  23. /// </summary>
  24. public const int DataSize = 2 * 4;
  25. /// <summary>
  26. /// Invalid point, usually used to indicate invalid cached points.
  27. /// </summary>
  28. public static readonly Point Invalid = new Point(-1, -1);
  29. /// <summary>
  30. /// Returns the zero point (0, 0).
  31. /// </summary>
  32. public static readonly Point Zero = new Point(0);
  33. /// <summary>
  34. /// Returns the point (0.5, 0.5).
  35. /// </summary>
  36. public static readonly Point Half = new Point(0.5f);
  37. /// <summary>
  38. /// Returns the point (1, 1).
  39. /// </summary>
  40. public static readonly Point One = new Point(1);
  41. /// <summary>
  42. /// Unit x vector, returns the point (1, 0)
  43. /// </summary>
  44. public static readonly Point UnitX = new Point(1, 0);
  45. /// <summary>
  46. /// Unit y vector, returns the point (0, 1)
  47. /// </summary>
  48. public static readonly Point UnitY = new Point(0, 1);
  49. #endregion
  50. #region Normalize (Static)
  51. /// <summary>
  52. /// Normalize
  53. /// </summary>
  54. /// <param name="anyPoint">anyPoint</param>
  55. /// <returns>Normalized point</returns>
  56. public static Point Normalize(Point anyPoint)
  57. {
  58. float lengthSquared = anyPoint.LengthSquared;
  59. if (lengthSquared == 0)
  60. {
  61. // and return the original unchanged value
  62. return anyPoint;
  63. } // if
  64. float invLength = 1f / MathHelper.Sqrt(lengthSquared);
  65. return new Point(anyPoint.X * invLength, anyPoint.Y * invLength);
  66. }
  67. #endregion
  68. #region Dot (Static)
  69. /// <summary>
  70. /// Return a scale as result of the dot product from two points
  71. /// </summary>
  72. /// <param name="value1">Point 1</param>
  73. /// <param name="value2">Point 2</param>
  74. /// <returns>Result of the dot product</returns>
  75. public static float Dot(Point value1, Point value2)
  76. {
  77. return value1.X * value2.X + value1.Y * value2.Y;
  78. }
  79. /// <summary>
  80. /// Return a scale as result of the do product from two points
  81. /// </summary>
  82. /// <param name="value1">Point 1</param>
  83. /// <param name="value2">Point 2</param>
  84. /// <param name="result">Result of the dot product</param>
  85. public static void Dot(ref Point value1, ref Point value2,
  86. out float result)
  87. {
  88. result = value1.X * value2.X + value1.Y * value2.Y;
  89. }
  90. #endregion
  91. #region Distance (Static)
  92. /// <summary>
  93. /// Calculate the distance from two points resulting in scale value
  94. /// </summary>
  95. /// <param name="value1">Point 1</param>
  96. /// <param name="value2">Point 2</param>
  97. /// <returns>Distance between points</returns>
  98. public static float Distance(Point value1, Point value2)
  99. {
  100. float result;
  101. DistanceSquared(ref value1, ref value2, out result);
  102. return MathHelper.Sqrt(result);
  103. }
  104. /// <summary>
  105. /// Calculate the distance from two points resulting in scale value
  106. /// </summary>
  107. /// <param name="value1">Point 1</param>
  108. /// <param name="value2">Point 2</param>
  109. /// <param name="result">Distance between points</param>
  110. public static void Distance(ref Point value1, ref Point value2,
  111. out float result)
  112. {
  113. DistanceSquared(ref value1, ref value2, out result);
  114. result = MathHelper.Sqrt(result);
  115. }
  116. #endregion
  117. #region DistanceSquared (Static)
  118. /// <summary>
  119. /// Calculate the squared distance from two points. This is faster than
  120. /// using Distance because we don't need to take the square root.
  121. /// </summary>
  122. /// <param name="value1">Point 1</param>
  123. /// <param name="value2">Point 2</param>
  124. /// <returns>Squared distance between points</returns>
  125. public static float DistanceSquared(Point value1, Point value2)
  126. {
  127. float result;
  128. DistanceSquared(ref value1, ref value2, out result);
  129. return result;
  130. }
  131. /// <summary>
  132. /// Calculate the squared distance from two points resulting in scale value
  133. /// </summary>
  134. /// <param name="value1">Point 1</param>
  135. /// <param name="value2">Point 2</param>
  136. /// <param name="result">Squared distance between points</param>
  137. public static void DistanceSquared(ref Point value1, ref Point value2,
  138. out float result)
  139. {
  140. result =
  141. (value1.X - value2.X) * (value1.X - value2.X) +
  142. (value1.Y - value2.Y) * (value1.Y - value2.Y);
  143. }
  144. #endregion
  145. #region Transform (Static)
  146. /// <summary>
  147. /// Transform point by multiplying it with a matrix.
  148. /// Note: This is slower than using the ref version of Transform.
  149. /// </summary>
  150. /// <param name="matrix">Matrix for the transformation</param>
  151. /// <param name="position">Position to transform</param>
  152. /// <returns>Transformed point resulting from matrix*position</returns>
  153. public static Point Transform(Point position, Matrix matrix)
  154. {
  155. Transform(ref position, ref matrix, out position);
  156. return position;
  157. }
  158. /// <summary>
  159. /// Transform point by multiplying it with a matrix
  160. /// </summary>
  161. /// <param name="matrix">Matrix for the transformation</param>
  162. /// <param name="position">Position to transform</param>
  163. /// <param name="result">Transformed point resulting from matrix*position
  164. /// </param>
  165. public static void Transform(ref Point position, ref Matrix matrix,
  166. out Point result)
  167. {
  168. result = new Point(
  169. position.X * matrix.M11 + position.Y * matrix.M21 + matrix.M41,
  170. position.X * matrix.M12 + position.Y * matrix.M22 + matrix.M42);
  171. }
  172. #endregion
  173. #region TransformNormal (Static)
  174. /// <summary>
  175. /// Transform normal by multiplying it with a matrix.
  176. /// Note: This is slower than using the ref version of Transform.
  177. /// </summary>
  178. /// <param name="normal">Position to transform</param>
  179. /// <param name="matrix">Matrix for the transformation</param>
  180. /// <returns>Transformed normal</returns>
  181. public static Point TransformNormal(Point normal, Matrix matrix)
  182. {
  183. TransformNormal(ref normal, ref matrix, out normal);
  184. return normal;
  185. }
  186. /// <summary>
  187. /// Transform normal by multiplying it with a matrix
  188. /// </summary>
  189. /// <param name="normal">Position to transform</param>
  190. /// <param name="matrix">Matrix for the transformation</param>
  191. /// <param name="result">Transformed normal</param>
  192. public static void TransformNormal(ref Point normal, ref Matrix matrix,
  193. out Point result)
  194. {
  195. result = new Point(
  196. (normal.X * matrix.M11) + (normal.Y * matrix.M21),
  197. (normal.X * matrix.M12) + (normal.Y * matrix.M22));
  198. }
  199. #endregion
  200. #region ComputeNormal (Static)
  201. /// <summary>
  202. /// Computes the normal of the given 2D vector. For more information see:
  203. /// http://www.sciface.com/education/data/web/Beschreibung-von-Geraden.html
  204. /// http://www.matheboard.de/archive/23062/thread.html
  205. /// </summary>
  206. /// <param name="vector2D">2D Vector</param>
  207. /// <returns>Normal, which is just (-Y, X)</returns>
  208. public static Point ComputeNormal(Point vector2D)
  209. {
  210. return new Point(-vector2D.Y, vector2D.X);
  211. }
  212. #endregion
  213. #region Lerp (Static)
  214. /// <summary>
  215. /// Performs a linear interpolation between two points.
  216. /// </summary>
  217. /// <param name="amount">Amount to lerp</param>
  218. /// <param name="value1">Point 1</param>
  219. /// <param name="value2">Point 2</param>
  220. /// <returns>Interpolated point between both points</returns>
  221. public static Point Lerp(Point value1, Point value2, float amount)
  222. {
  223. return new Point(
  224. MathHelper.Lerp(value1.X, value2.X, amount),
  225. MathHelper.Lerp(value1.Y, value2.Y, amount));
  226. }
  227. /// <summary>
  228. /// Performs a linear interpolation between two points.
  229. /// </summary>
  230. /// <param name="amount">Amount to lerp</param>
  231. /// <param name="value1">Point 1</param>
  232. /// <param name="value2">Point 2</param>
  233. /// <param name="result">Interpolated point between both points</param>
  234. public static void Lerp(ref Point value1, ref Point value2, float amount,
  235. out Point result)
  236. {
  237. result = new Point(
  238. MathHelper.Lerp(value1.X, value2.X, amount),
  239. MathHelper.Lerp(value1.Y, value2.Y, amount));
  240. }
  241. #endregion
  242. #region Min (Static)
  243. /// <summary>
  244. /// Returns the minimum of both points (X and Y are handled seperately).
  245. /// -> A(2,4), B(4,1) => Min(2,1)
  246. /// </summary>
  247. /// <param name="value1">Point 1</param>
  248. /// <param name="value2">Point 2</param>
  249. /// <returns>Minimum value of X and Y</returns>
  250. public static Point Min(Point value1, Point value2)
  251. {
  252. return new Point(MathHelper.Min(value1.X, value2.X),
  253. MathHelper.Min(value1.Y, value2.Y));
  254. }
  255. #endregion
  256. #region Max (Static)
  257. /// <summary>
  258. /// Returns the maximum of both points (X and Y are handled seperately).
  259. /// -> A(2,4), B(4,1) => Max(4,4)
  260. /// </summary>
  261. /// <param name="value1">Point 1</param>
  262. /// <param name="value2">Point 2</param>
  263. /// <returns>Maximum value of X and Y</returns>
  264. public static Point Max(Point value1, Point value2)
  265. {
  266. return new Point(MathHelper.Max(value1.X, value2.X),
  267. MathHelper.Max(value1.Y, value2.Y));
  268. }
  269. #endregion
  270. #region Clamp (Static)
  271. /// <summary>
  272. /// Clamp
  273. /// </summary>
  274. /// <param name="value">Value</param>
  275. /// <param name="min">Minimum</param>
  276. /// <param name="max">Maximum</param>
  277. public static Point Clamp(Point value, Point min, Point max)
  278. {
  279. return new Point(
  280. MathHelper.Clamp(value.X, min.X, max.X),
  281. MathHelper.Clamp(value.Y, min.Y, max.Y));
  282. }
  283. #endregion
  284. #region FromString (Static)
  285. /// <summary>
  286. /// Convert a string to a Point. The expected format is (x.x, y.y), but
  287. /// it works fine with ToColladaString or ToCommaString strings too :)
  288. /// </summary>
  289. /// <param name="pointString">The string in the correct format
  290. /// (with or without brackets, comma or space seperated).</param>
  291. /// <returns>
  292. /// Point from the given string or Zero if parsing failed.
  293. /// </returns>
  294. public static Point FromString(string pointString)
  295. {
  296. // Remove the brackets and split the string up into seperate values
  297. pointString = pointString.Replace("(", "");
  298. pointString = pointString.Replace(")", "");
  299. pointString = pointString.Replace("{", "");
  300. pointString = pointString.Replace("}", "");
  301. string[] pointStrings = pointString.Split(new[]
  302. {
  303. ',', ' '
  304. },
  305. StringSplitOptions.RemoveEmptyEntries);
  306. // Then check if the length is 2 for 2 values and return the new point.
  307. // If the length is not 2 than return Point.Zero.
  308. if (pointStrings.Length == 2)
  309. {
  310. return new Point(
  311. pointStrings[0].FromInvariantString(0.0f),
  312. pointStrings[1].FromInvariantString(0.0f));
  313. }
  314. return Zero;
  315. }
  316. #endregion
  317. #region IsPointInside (Static)
  318. /// <summary>
  319. /// Is the given point inside the rectangle?
  320. /// </summary>
  321. /// <param name="point">Point to check</param>
  322. /// <param name="rect">Rectangle to check against</param>
  323. /// <returns>True if the point is inside the rectangle.</returns>
  324. public static bool IsPointInside(ref Point point, ref Rectangle rect)
  325. {
  326. return point.X >= rect.Left &&
  327. point.X <= rect.Right &&
  328. point.Y >= rect.Top &&
  329. point.Y <= rect.Bottom;
  330. }
  331. #endregion
  332. #region IsCircleInside (Static)
  333. /// <summary>
  334. /// Check if a circle is inside a rectangle or not.
  335. /// </summary>
  336. /// <param name="centerPoint">The center point of the circle.</param>
  337. /// <param name="radius">The radius of the cirlce.</param>
  338. /// <param name="rect">The rectangle to check if the circle lays in.</param>
  339. /// <returns>
  340. /// True if the circle is in the rectangle, otherwise False.
  341. /// </returns>
  342. public static bool IsCircleInside(ref Point centerPoint, float radius,
  343. ref Rectangle rect)
  344. {
  345. return centerPoint.X + radius >= rect.Left &&
  346. centerPoint.X - radius <= rect.Right &&
  347. centerPoint.Y + radius >= rect.Top &&
  348. centerPoint.Y - radius <= rect.Bottom;
  349. }
  350. #endregion
  351. #region GetRandomPoint (Static)
  352. /// <summary>
  353. /// Get random point with x and y between 0 and 1.
  354. /// </summary>
  355. /// <returns>Random point with x and y between 0 and 1</returns>
  356. public static Point GetRandomPoint()
  357. {
  358. return GetRandomPoint(0, 1);
  359. }
  360. /// <summary>
  361. /// Get random point with x and y between minValue and maxValue.
  362. /// </summary>
  363. /// <param name="maxValue">Maximum value</param>
  364. /// <param name="minValue">Mininum value</param>
  365. /// <returns>
  366. /// Random point with x and y between minValue and maxValue.
  367. /// </returns>
  368. public static Point GetRandomPoint(float minValue, float maxValue)
  369. {
  370. return new Point(
  371. RandomHelper.RandomFloat(minValue, maxValue),
  372. RandomHelper.RandomFloat(minValue, maxValue));
  373. }
  374. #endregion
  375. #region GetUnitCirclePosition (Static)
  376. /// <summary>
  377. /// Returns the represented position by the given degree on the unit circle
  378. /// (-> degree of: 0/360 = top, 90 = right, 180 = bottom, 270 = left)
  379. /// </summary>
  380. /// <param name="degreeValue">Degree value</param>
  381. /// <returns>Unit vector pointing in the given direction.</returns>
  382. public static Point GetUnitCirclePosition(float degreeValue)
  383. {
  384. return new Point(MathHelper.Sin(degreeValue),
  385. -MathHelper.Cos(degreeValue));
  386. }
  387. #endregion
  388. #region GetRotation (Static)
  389. /// <summary>
  390. /// This will convert the given unit-circle position in an unit-circle
  391. /// rotation (-> top = 0/360, right = 90, bottom = 180, left = 270 degree).
  392. /// If the given position isn't normalized yet, then just set the second
  393. /// parameter to 'true'.
  394. /// <para />
  395. /// Note: When unitCirclePosition is Point.Zero, 0 is returned.
  396. /// </summary>
  397. /// <param name="unitCirclePosition">Unit circle position</param>
  398. /// <param name="isNormalizationNeeded">Is normalization needed</param>
  399. /// <returns>The unit circle rotation (top=0, right=90, etc.)</returns>
  400. public static float GetRotation(Point unitCirclePosition,
  401. bool isNormalizationNeeded)
  402. {
  403. #region Validation
  404. if (unitCirclePosition == Zero)
  405. {
  406. return 0;
  407. }
  408. #endregion
  409. if (isNormalizationNeeded)
  410. {
  411. unitCirclePosition = Normalize(unitCirclePosition);
  412. }
  413. // Will return a value between 0 until 180 and -179 until -1
  414. float rawAngle = MathHelper.Atan(unitCirclePosition.X,
  415. -unitCirclePosition.Y);
  416. // so we have to convert from 0 until 180 and 181 until 359
  417. return (rawAngle < 0.0f)
  418. ? 360.0f + rawAngle
  419. : rawAngle;
  420. }
  421. /// <summary>
  422. /// This will convert the given unit-circle position in an unit-circle
  423. /// rotation (-> top = 0/360, right = 90, bottom = 180, left = 270 degree).
  424. /// The given position will be normalized automatically, if that isn't
  425. /// wished or needed, then just use the other overload with 'false' at the
  426. /// second parameter.
  427. /// </summary>
  428. /// <param name="unitCirclePosition">Unit circle position</param>
  429. /// <returns>Point</returns>
  430. public static float GetRotation(Point unitCirclePosition)
  431. {
  432. return GetRotation(unitCirclePosition, true);
  433. }
  434. #endregion
  435. #region AngleBetweenPoints (Static)
  436. /// <summary>
  437. /// Angle between points in degrees, pretty much the same as
  438. /// AngleBetweenVectors, just in 2D:
  439. /// http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
  440. /// RadiansToDegrees(atan2(a.y,a.x) - atan2(b.y,b.x)) would only give
  441. /// you 0-180 degrees, but we want full 0-360 degrees with Acos :)
  442. /// <para />
  443. /// Note: If one of the points is zero the method we will return 0.0f.
  444. /// </summary>
  445. /// <param name="a">First vector.</param>
  446. /// <param name="b">Second vector.</param>
  447. /// <returns>Angle between the two vectors in the range [0, 360]</returns>
  448. public static float AngleBetweenPoints(Point a, Point b)
  449. {
  450. #region Validation
  451. // Having a single zero vector in this method will cause the calculation
  452. // to return 90 degrees which is not right. So we simply return 0f.
  453. if (a == Zero ||
  454. b == Zero)
  455. {
  456. return 0f;
  457. }
  458. #endregion
  459. // We need to normalize the vectors so we get the cos from 0 to 1
  460. // the cos is the dot product of the vectors a and b
  461. float cos = Dot(Normalize(a), Normalize(b));
  462. cos = MathHelper.Clamp(cos, -1.0f, 1.0f);
  463. // Note: Special way for 2D vector handling (instead of Vector.Cross)
  464. float cross = MathHelper.Atan(a.Y, a.X) - MathHelper.Atan(b.Y, b.X);
  465. return cross < 0.0f
  466. ? // cross products directory is upwards
  467. 360 - MathHelper.Acos(cos)
  468. : // else
  469. MathHelper.Acos(cos);
  470. }
  471. #endregion
  472. #region Framework Union Defines (Public)
  473. #endregion
  474. #region X (Public)
  475. /// <summary>
  476. /// X coordinate.
  477. /// </summary>
  478. [FieldOffset(0)]
  479. public float X;
  480. #endregion
  481. #region Y (Public)
  482. /// <summary>
  483. /// Y coordinate.
  484. /// </summary>
  485. [FieldOffset(4)]
  486. public float Y;
  487. #endregion
  488. #region XProperty (Public)
  489. /// <summary>
  490. /// Property-wrapper for using the X field in the editor.
  491. /// </summary>
  492. [Browsable(true)]
  493. [DisplayName("X")]
  494. public float XProperty
  495. {
  496. get
  497. {
  498. return X;
  499. }
  500. set
  501. {
  502. X = value;
  503. }
  504. }
  505. #endregion
  506. #region YProperty (Public)
  507. /// <summary>
  508. /// Property-wrapper for using the Y field in the editor
  509. /// </summary>
  510. [Browsable(true)]
  511. [DisplayName("Y")]
  512. public float YProperty
  513. {
  514. get
  515. {
  516. return Y;
  517. }
  518. set
  519. {
  520. Y = value;
  521. }
  522. }
  523. #endregion
  524. #region Length (Public)
  525. /// <summary>
  526. /// Length
  527. /// </summary>
  528. [Browsable(false)]
  529. public float Length
  530. {
  531. get
  532. {
  533. return MathHelper.Sqrt(X * X + Y * Y);
  534. }
  535. }
  536. #endregion
  537. #region LengthSquared (Public)
  538. /// <summary>
  539. /// Length squared
  540. /// </summary>
  541. [Browsable(false)]
  542. public float LengthSquared
  543. {
  544. get
  545. {
  546. return X * X + Y * Y;
  547. }
  548. }
  549. #endregion
  550. #region Constructors
  551. /// <summary>
  552. /// Creates a 2D point
  553. /// </summary>
  554. /// <param name="setValue">setValue</param>
  555. public Point(float setValue)
  556. : this(setValue, setValue)
  557. {
  558. }
  559. /// <summary>
  560. /// Creates a 2D point
  561. /// </summary>
  562. /// <param name="setX">setX</param>
  563. /// <param name="setY">setY</param>
  564. public Point(float setX, float setY)
  565. : this()
  566. {
  567. X = setX;
  568. Y = setY;
  569. }
  570. /// <summary>
  571. /// Creates a 2D point from a Vector (ignores Z)
  572. /// </summary>
  573. /// <param name="fromVector">fromVector</param>
  574. public Point(Vector fromVector)
  575. : this()
  576. {
  577. X = fromVector.X;
  578. Y = fromVector.Y;
  579. }
  580. /// <summary>
  581. /// Create point
  582. /// </summary>
  583. /// <param name="reader">reader</param>
  584. public Point(BinaryReader reader)
  585. : this()
  586. {
  587. Load(reader);
  588. }
  589. #endregion
  590. #region IEquatable<Point> Members
  591. /// <summary>
  592. /// Equals check will check if the other point is nearly equals (using
  593. /// the MathHelper.Epsilon value, which is close to zero).
  594. /// </summary>
  595. /// <param name="other">Other point to check against</param>
  596. /// <returns>
  597. /// True if both points are almost equal, false if they are apart.
  598. /// </returns>
  599. public bool Equals(Point other)
  600. {
  601. return
  602. // Allow a difference of the Epsilon (in both directions)
  603. // for the X value range
  604. X - MathHelper.Epsilon <= other.X &&
  605. X + MathHelper.Epsilon >= other.X &&
  606. // and Y value range
  607. Y - MathHelper.Epsilon <= other.Y &&
  608. Y + MathHelper.Epsilon >= other.Y;
  609. }
  610. #endregion
  611. #region ISaveLoadBinary Members
  612. /// <summary>
  613. /// Load the point values from a stream.
  614. /// </summary>
  615. /// <param name="reader">The stream that will be used.</param>
  616. public void Load(BinaryReader reader)
  617. {
  618. X = reader.ReadSingle();
  619. Y = reader.ReadSingle();
  620. }
  621. /// <summary>
  622. /// Saves the point to a stream.
  623. /// </summary>
  624. /// <param name="writer">The stream that will be used.</param>
  625. public void Save(BinaryWriter writer)
  626. {
  627. writer.Write(X);
  628. writer.Write(Y);
  629. }
  630. #endregion
  631. #region op_UnaryNegation (Operator)
  632. /// <summary>
  633. /// Operator for negation
  634. /// </summary>
  635. /// <param name="value">Value to subtract</param>
  636. /// <returns>
  637. /// The returned point is the negative version of the input point.
  638. /// </returns>
  639. public static Point operator -(Point value)
  640. {
  641. return new Point(-value.X, -value.Y);
  642. }
  643. #endregion
  644. #region op_Equality (Operator)
  645. /// <summary>
  646. /// Operator for equality
  647. /// </summary>
  648. /// <param name="value1">value1</param>
  649. /// <param name="value2">value2</param>
  650. /// <returns>Returns True if the points are equal, otherwise False.
  651. /// </returns>
  652. public static bool operator ==(Point value1, Point value2)
  653. {
  654. return value1.X == value2.X && value1.Y == value2.Y;
  655. }
  656. #endregion
  657. #region op_Inequality (Operator)
  658. /// <summary>
  659. /// Operator for inequality
  660. /// </summary>
  661. /// <param name="value1">value1</param>
  662. /// <param name="value2">value2</param>
  663. /// <returns>Returns True if the points are unequal, otherwise False.
  664. /// </returns>
  665. public static bool operator !=(Point value1, Point value2)
  666. {
  667. return value1.X != value2.X || value1.Y != value2.Y;
  668. }
  669. #endregion
  670. #region op_Addition (Operator)
  671. /// <summary>
  672. /// Operator for addition
  673. /// </summary>
  674. /// <param name="value1">value1</param>
  675. /// <param name="value2">value2</param>
  676. /// <returns>Returns a point as the result of the addition.</returns>
  677. public static Point operator +(Point value1, Point value2)
  678. {
  679. return new Point(value1.X + value2.X, value1.Y + value2.Y);
  680. }
  681. #endregion
  682. #region op_Subtraction (Operator)
  683. /// <summary>
  684. /// Operator for subtraction
  685. /// </summary>
  686. /// <param name="value1">Point 1</param>
  687. /// <param name="value2">Point 2</param>
  688. /// <returns>Result of the subtraction</returns>
  689. public static Point operator -(Point value1, Point value2)
  690. {
  691. return new Point(value1.X - value2.X, value1.Y - value2.Y);
  692. }
  693. #endregion
  694. #region op_Multiply (Operator)
  695. /// <summary>
  696. /// Operator for multiplication points
  697. /// </summary>
  698. /// <param name="value1">Point 1</param>
  699. /// <param name="value2">Point 2</param>
  700. /// <returns>Result of the multiplication</returns>
  701. public static Point operator *(Point value1, Point value2)
  702. {
  703. return new Point(value1.X * value2.X, value1.Y * value2.Y);
  704. }
  705. /// <summary>
  706. /// Operator for multiplication a point with a float as scaling
  707. /// </summary>
  708. /// <param name="scaleFactor">scaleFactor</param>
  709. /// <param name="value">value</param>
  710. /// <returns>Result of the multiplication</returns>
  711. public static Point operator *(Point value, float scaleFactor)
  712. {
  713. return new Point(value.X * scaleFactor, value.Y * scaleFactor);
  714. }
  715. /// <summary>
  716. /// Operator for multiplication a float as scaling with a point
  717. /// </summary>
  718. /// <param name="scaleFactor">scaleFactor</param>
  719. /// <param name="value">value</param>
  720. /// <returns>Result of the multiplication</returns>
  721. public static Point operator *(float scaleFactor, Point value)
  722. {
  723. return new Point(value.X * scaleFactor, value.Y * scaleFactor);
  724. }
  725. #endregion
  726. #region op_Division (Operator)
  727. /// <summary>
  728. /// Operator to divide a point with a float.
  729. /// </summary>
  730. /// <param name="value">Value</param>
  731. /// <param name="divisor">Divisor</param>
  732. /// <returns>Result of the division</returns>
  733. public static Point operator /(Point value, float divisor)
  734. {
  735. return new Point(value.X / divisor, value.Y / divisor);
  736. }
  737. /// <summary>
  738. /// Operator to divide a point with a float.
  739. /// </summary>
  740. /// <param name="divisor">Divisor</param>
  741. /// <param name="value">Value</param>
  742. /// <returns>Result of the division</returns>
  743. public static Point operator /(float divisor, Point value)
  744. {
  745. return new Point(divisor / value.X, divisor / value.Y);
  746. }
  747. #endregion
  748. #region op_Implicit (Operator)
  749. /// <summary>
  750. /// Operator to implicit convert Size to Point.
  751. /// </summary>
  752. /// <param name="anySize">Any size</param>
  753. /// <returns>
  754. /// Point created from the size (x from width, y from height).
  755. /// </returns>
  756. public static implicit operator Point(Size anySize)
  757. {
  758. return new Point(anySize.Width, anySize.Height);
  759. }
  760. #endregion
  761. #region CloneX (Public)
  762. /// <summary>
  763. /// Returns a new Point with the same X value as before and a new Y
  764. /// changed by the given offset (parameter) value.
  765. /// </summary>
  766. /// <param name="setOffsetOfY">The offset to compute the new Y value
  767. /// for the cloned point.</param>
  768. /// <returns>New cloned Point instance.</returns>
  769. public Point CloneX(float setOffsetOfY)
  770. {
  771. return new Point(X, Y + setOffsetOfY);
  772. }
  773. #endregion
  774. #region CloneY (Public)
  775. /// <summary>
  776. /// Returns a new Point with the same Y value as before and a new X
  777. /// changed by the given offset (parameter) value.
  778. /// </summary>
  779. /// <param name="setOffsetOfX">The offset to compute the new X value for
  780. /// the cloned point.</param>
  781. /// <returns>New cloned Point instance.</returns>
  782. public Point CloneY(float setOffsetOfX)
  783. {
  784. return new Point(X + setOffsetOfX, Y);
  785. }
  786. #endregion
  787. #region Equals (Public)
  788. /// <summary>
  789. /// Check if another object is a point and equals to this point.
  790. /// </summary>
  791. /// <param name="obj">Object to compare with</param>
  792. /// <returns>
  793. /// True if the object is a Point and equal to this point.
  794. /// </returns>
  795. public override bool Equals(object obj)
  796. {
  797. return (obj is Point)
  798. ? Equals((Point)obj)
  799. : base.Equals(obj);
  800. }
  801. #endregion
  802. #region NearlyEquals (Public)
  803. /// <summary>
  804. /// NearlyEquals
  805. /// </summary>
  806. /// <param name="other">Other point we wan't check equality with.</param>
  807. /// <param name="epsilon">A very small value defining the range in which
  808. /// the two points can differ to still be nearly equal enough.</param>
  809. /// <returns>Value indicating the equality of two vectors</returns>
  810. public bool NearlyEquals(Point other, float epsilon)
  811. {
  812. return
  813. // Allow a difference of the Epsilon (in both directions)
  814. // for the X value range
  815. X - epsilon <= other.X &&
  816. X + epsilon >= other.X &&
  817. // and Y value range
  818. Y - epsilon <= other.Y &&
  819. Y + epsilon >= other.Y;
  820. }
  821. #endregion
  822. #region GetHashCode (Public)
  823. /// <summary>
  824. /// Get hash code
  825. /// </summary>
  826. /// <returns>Hash code from X and Y values</returns>
  827. public override int GetHashCode()
  828. {
  829. return X.GetHashCode() ^ Y.GetHashCode();
  830. }
  831. #endregion
  832. #region Resize (Public)
  833. /// <summary>
  834. /// Resize this point to given length and returns the new Point
  835. /// </summary>
  836. /// <param name="length">length</param>
  837. /// <returns>Resized point</returns>
  838. public Point Resize(float length)
  839. {
  840. return Normalize(this) * length;
  841. }
  842. #endregion
  843. #region Rotate (Public)
  844. /// <summary>
  845. /// Rotates this instance around the origin, returns the
  846. /// </summary>
  847. /// <param name="angle">Angle to rotate in degree</param>
  848. /// <returns>Rotated point with given direction from angle</returns>
  849. public Point Rotate(float angle)
  850. {
  851. float sine = MathHelper.Sin(-angle);
  852. float cosine = MathHelper.Cos(-angle);
  853. float newX = X * cosine + Y * sine;
  854. Y = -X * sine + Y * cosine;
  855. X = newX;
  856. return this;
  857. }
  858. #endregion
  859. #region ToString (Public)
  860. /// <summary>
  861. /// To string
  862. /// </summary>
  863. /// <returns>String with braces, e.g. "(1.0, 2.3)"</returns>
  864. public override string ToString()
  865. {
  866. return ToString("(", ")");
  867. }
  868. /// <summary>
  869. /// To string
  870. /// </summary>
  871. /// <returns>String with custom braces, e.g. "(1.0, 2.3)"</returns>
  872. public string ToString(string openBrace, string closeBrace)
  873. {
  874. return openBrace + X.ToInvariantString("0.000") +
  875. ", " + Y.ToInvariantString("0.000") + closeBrace;
  876. }
  877. #endregion
  878. /// <summary>
  879. /// Tests
  880. /// </summary>
  881. internal class PointTests
  882. {
  883. #region SizeOf
  884. /// <summary>
  885. /// Checks if the size of Point is exactly 8 bytes (2 floats: X and Y)
  886. /// </summary>
  887. [Test]
  888. public void SizeOf()
  889. {
  890. // Points are 2 floats: X and Y
  891. Assert.Equal(2 * 4, Marshal.SizeOf(typeof(Point)));
  892. }
  893. #endregion
  894. #region DistanceSquared
  895. /// <summary>
  896. /// DistanceSquared
  897. /// </summary>
  898. [Test]
  899. public void DistanceSquared()
  900. {
  901. Assert.Equal(4, Point.DistanceSquared(new Point(2, 0), Zero));
  902. }
  903. #endregion
  904. #region Distance
  905. /// <summary>
  906. /// Distance
  907. /// </summary>
  908. [Test]
  909. public void Distance()
  910. {
  911. Assert.Equal(2, Point.Distance(new Point(2, 0), Zero));
  912. }
  913. #endregion
  914. #region Lerp
  915. /// <summary>
  916. /// Lerp
  917. /// </summary>
  918. [Test]
  919. public void Lerp()
  920. {
  921. Assert.Equal(Zero, Point.Lerp(new Point(2, 2), Zero, 1));
  922. }
  923. #endregion
  924. #region TransformNormal
  925. /// <summary>
  926. /// TransformNormal
  927. /// </summary>
  928. [Test]
  929. public void TransformNormal()
  930. {
  931. Assert.Equal(One, Point.TransformNormal(One,
  932. Matrix.Identity));
  933. }
  934. #endregion
  935. #region Transform
  936. /// <summary>
  937. /// Transform
  938. /// </summary>
  939. [Test]
  940. public void Transform()
  941. {
  942. Assert.Equal(One, Point.Transform(One, Matrix.Identity));
  943. }
  944. #endregion
  945. #region Dot
  946. /// <summary>
  947. /// Dot
  948. /// </summary>
  949. [Test]
  950. public void Dot()
  951. {
  952. Assert.Equal(2, Point.Dot(One, One));
  953. }
  954. #endregion
  955. #region Length
  956. /// <summary>
  957. /// Length
  958. /// </summary>
  959. [Test]
  960. public void Length()
  961. {
  962. Assert.Equal(5, new Point(0, 5).Length);
  963. Assert.Equal(1, new Point(-1, 0).Length);
  964. Assert.Equal(5, new Point(3, 4).Length);
  965. }
  966. #endregion
  967. #region LengthSquared
  968. /// <summary>
  969. /// Length squared
  970. /// </summary>
  971. [Test]
  972. public void LengthSquared()
  973. {
  974. Assert.Equal(25, new Point(0, 5).LengthSquared);
  975. Assert.Equal(1, new Point(-1, 0).LengthSquared);
  976. Assert.Equal(25, new Point(3, 4).LengthSquared);
  977. }
  978. #endregion
  979. #region Negation
  980. /// <summary>
  981. /// Negation
  982. /// </summary>
  983. [Test]
  984. public void Negation()
  985. {
  986. Assert.Equal(new Point(-3, 1), -(new Point(3, -1)));
  987. }
  988. #endregion
  989. #region Equality
  990. /// <summary>
  991. /// Equality
  992. /// </summary>
  993. [Test]
  994. public void Equality()
  995. {
  996. Assert.Equal(new Point(10, 10), new Point(10));
  997. Assert.NotEqual(new Point(10, 5), new Point(10f, 10f));
  998. }
  999. #endregion
  1000. #region Addition
  1001. /// <summary>
  1002. /// Addition
  1003. /// </summary>
  1004. [Test]
  1005. public void Addition()
  1006. {
  1007. Assert.Equal(new Point(9, 8.8f), new Point(5, 3) + new Point(4, 5.8f));
  1008. Assert.Equal(new Point(-1, -2.8f),
  1009. new Point(-5, 3) + new Point(4, -5.8f));
  1010. }
  1011. #endregion
  1012. #region Substraction
  1013. /// <summary>
  1014. /// Substraction
  1015. /// </summary>
  1016. [Test]
  1017. public void Substraction()
  1018. {
  1019. Assert.Equal(new Point(1, -2.8f), new Point(5, 3) - new Point(4, 5.8f));
  1020. Assert.Equal(new Point(-9, 8.8f),
  1021. new Point(-5, 3) - new Point(4, -5.8f));
  1022. }
  1023. #endregion
  1024. #region Multiplication
  1025. /// <summary>
  1026. /// Multiplication
  1027. /// </summary>
  1028. [Test]
  1029. public void Multiplication()
  1030. {
  1031. // with Points
  1032. Assert.Equal(new Point(2, 2), new Point(2, 4) * new Point(1, 0.5f));
  1033. Assert.Equal(new Point(-2, -4), new Point(2, 4) * new Point(-1));
  1034. // with a scale factor
  1035. Assert.Equal(new Point(10, 20), new Point(2, 4) * 5);
  1036. Assert.Equal(new Point(-1, -2), new Point(2, 4) * -0.5f);
  1037. Assert.Equal(new Point(0.5f, 1), 0.25f * new Point(2, 4));
  1038. }
  1039. #endregion
  1040. #region Division
  1041. /// <summary>
  1042. /// Division
  1043. /// </summary>
  1044. [Test]
  1045. public void Division()
  1046. {
  1047. Assert.Equal(new Point(2, 5), new Point(10, 25) / 5.0f);
  1048. Assert.Equal(new Point(5, 6), new Point(10, 12) / 2);
  1049. Assert.Equal(new Point(0.1f, 0.04f), 1.0f / new Point(10, 25));
  1050. }
  1051. #endregion
  1052. #region NearlyEquals
  1053. /// <summary>
  1054. /// Nearly equals
  1055. /// </summary>
  1056. [Test]
  1057. public void NearlyEquals()
  1058. {
  1059. Point testPoint = new Point(2, 3);
  1060. // Check the point directly
  1061. Assert.True(testPoint.Equals(testPoint), "TODO");
  1062. // by the "object" overload from .NET
  1063. Assert.True(testPoint.Equals((object)testPoint), "TODO");
  1064. // and the nearly equal check
  1065. Assert.True(testPoint.Equals(
  1066. new Point(2 + MathHelper.Epsilon, 3 - MathHelper.Epsilon)), "TODO");
  1067. // Finally check the "bad" false cases with unequal values
  1068. Assert.False(testPoint.Equals(new Point(4, 3)), "TODO");
  1069. // and a too big epsilon
  1070. Assert.False(testPoint.Equals(
  1071. new Point(2 + (2 * MathHelper.Epsilon), 3)), "TODO");
  1072. }
  1073. #endregion
  1074. #region Normalize
  1075. /// <summary>
  1076. /// Normalize
  1077. /// </summary>
  1078. [Test]
  1079. public void Normalize()
  1080. {
  1081. Assert.Equal(UnitX, Point.Normalize(new Point(13, 0)));
  1082. Assert.Equal(-UnitY, Point.Normalize(new Point(0, -7)));
  1083. }
  1084. #endregion
  1085. #region Min
  1086. /// <summary>
  1087. /// Minimum
  1088. /// </summary>
  1089. [Test]
  1090. public void Min()
  1091. {
  1092. Assert.Equal(new Point(2, 1),
  1093. Point.Min(new Point(2, 4), new Point(4, 1)));
  1094. }
  1095. #endregion
  1096. #region Max
  1097. /// <summary>
  1098. /// Maximum
  1099. /// </summary>
  1100. [Test]
  1101. public void Max()
  1102. {
  1103. Assert.Equal(new Point(4, 4),
  1104. Point.Max(new Point(2, 4), new Point(4, 1)));
  1105. }
  1106. #endregion
  1107. #region Rotate
  1108. /// <summary>
  1109. /// Rotate
  1110. /// </summary>
  1111. [Test]
  1112. public void Rotate()
  1113. {
  1114. Point p = new Point(10, 20);
  1115. p.Rotate(90);
  1116. Assert.Equal(new Point(-20, 10), p);
  1117. }
  1118. #endregion
  1119. #region GetRotation
  1120. /// <summary>
  1121. /// Get rotation
  1122. /// </summary>
  1123. [Test]
  1124. public void GetRotation()
  1125. {
  1126. Assert.NearlyEqual(Point.GetRotation(new Point(0, -1), false), 0);
  1127. Assert.NearlyEqual(Point.GetRotation(new Point(1, 0), false), 90);
  1128. Assert.NearlyEqual(Point.GetRotation(new Point(0, 1), false), 180);
  1129. Assert.NearlyEqual(Point.GetRotation(new Point(-1, 0), false), 270);
  1130. Assert.NearlyEqual(Point.GetRotation(new Point(0, -4)), 0);
  1131. Assert.NearlyEqual(Point.GetRotation(new Point(2, 0)), 90);
  1132. Assert.NearlyEqual(Point.GetRotation(new Point(0, 8)), 180);
  1133. Assert.NearlyEqual(Point.GetRotation(new Point(-5, 0)), 270);
  1134. }
  1135. #endregion
  1136. #region ToString
  1137. /// <summary>
  1138. /// To string
  1139. /// </summary>
  1140. [Test]
  1141. public new void ToString()
  1142. {
  1143. Assert.Equal("(5.000, 3.500)", new Point(5, 3.5f).ToString());
  1144. Assert.Equal("(0.000, 0.000)", new Point().ToString());
  1145. Assert.Equal("5.000, 3.500", new Point(5, 3.5f).ToString("", ""));
  1146. Assert.Equal("{5.000, 3.700}", new Point(5, 3.7f).ToString("{", "}"));
  1147. // And the other way around
  1148. Assert.Equal(new Point(5, 3.5f), FromString("(5, 3.5)"));
  1149. Assert.Equal(new Point(), FromString("(0, 0)"));
  1150. Assert.Equal(new Point(5, 3.5f), FromString("5 3.5"));
  1151. Assert.Equal(new Point(), FromString("0 0"));
  1152. Assert.Equal(new Point(5, 3.5f), FromString("5, 3.5"));
  1153. Assert.Equal(new Point(5, 3.7f), FromString("{5, 3.7}"));
  1154. }
  1155. #endregion
  1156. }
  1157. }
  1158. }