UnitTests.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. //---------------------------------------------------------------------------------------------------------------------
  2. // ShapeOps.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //---------------------------------------------------------------------------------------------------------------------
  7. using System;
  8. using System.Diagnostics;
  9. using CollisionSample;
  10. using Microsoft.Xna.Framework;
  11. namespace UnitTests
  12. {
  13. // Test the collision primitives.
  14. public class UnitTests
  15. {
  16. public int TestsFailed = 0;
  17. public int TestsPassed = 0;
  18. void Fail<T1, T2>(string msg, IShapeOps<T1> obj1, IShapeOps<T2> obj2)
  19. {
  20. TestsFailed++;
  21. Console.WriteLine("Failed {0}(new {1}, new {2})", msg, obj1, obj2);
  22. Debug.WriteLine("Failed {0}(new {1}, new {2})", msg, obj1, obj2);
  23. }
  24. void Pass()
  25. {
  26. TestsPassed++;
  27. }
  28. void Check<T1, T2>(string msg, IShapeOps<T1> obj1, IShapeOps<T2> obj2, Func<T1, T2, bool> checker)
  29. {
  30. Check(msg, obj1, obj2, checker, x => x);
  31. }
  32. void Check<T1, T2, R>(string msg, IShapeOps<T1> obj1, IShapeOps<T2> obj2, Func<T1, T2, R> checker, Func<R, bool> wanted)
  33. {
  34. if (wanted(checker(obj1.Shape, obj2.Shape)))
  35. {
  36. Pass();
  37. }
  38. else
  39. {
  40. Fail(msg, obj1, obj2);
  41. checker(obj1.Shape, obj2.Shape); // run test again for debugging
  42. }
  43. }
  44. static Vector3 RandomPointOutsideShape<T>(IShapeOps<T> h, T obj, Random random)
  45. {
  46. Vector3 p1 = h.RandomInteriorPoint(random);
  47. Vector3 p2 = h.RandomInteriorPoint(random);
  48. for (; ; )
  49. {
  50. // move p2 on a line away from p1 until it's outside
  51. p2 = p1 + (p2 - p1) * 1.1f;
  52. if (!h.ContainsPoint(p2))
  53. return p2;
  54. }
  55. }
  56. /// <summary>
  57. /// Test containment and intersection between randomly created shapes of type T1 and T2.
  58. /// </summary>
  59. void TestRandomObjects<T1, T2>(Random random, IShapeOps<T1> h1, IShapeOps<T2> h2, Func<T1, T2, ContainmentType> contains, Func<T1, T2, bool> intersects)
  60. {
  61. // Create a random shape of each type
  62. h1.CreateRandomShape(random, (float)Math.Exp(random.NextDouble() * 4 - 2));
  63. h2.CreateRandomShape(random, (float)Math.Exp(random.NextDouble() * 4 - 2));
  64. // First ensure that the shapes are intersecting (or one is inside the other), by picking a point within each
  65. // and translating so they coincide.
  66. Vector3 p1 = h1.RandomInteriorPoint(random);
  67. Vector3 p2 = h2.RandomInteriorPoint(random);
  68. h2.Translate(p1 - p2);
  69. Check("Contains", h1, h2, contains, c => c != ContainmentType.Disjoint);
  70. Check("Intersects", h1, h2, intersects);
  71. // Next ensure that shape2 is intersecting (not contained or disjoint), by picking another point
  72. // in shape2, and scaling around p1 until the new point is outside of shape1.
  73. p2 = h2.RandomInteriorPoint(random);
  74. while (h1.ContainsPoint(p2))
  75. {
  76. p2 = p1 + (p2 - p1) * 1.1f;
  77. h2.Translate(-p1);
  78. h2.Scale(1.1f);
  79. h2.Translate(p1);
  80. }
  81. Check("surface Intersects", h1, h2, contains, c => c == ContainmentType.Intersects);
  82. Check("Intersects", h1, h2, intersects);
  83. // Ensure that shape2 is fully contained within shape1 by scaling it down
  84. // around a point in shape1, until its hull is inside shape1
  85. for (; ; )
  86. {
  87. Vector3[] hull = h2.GetHull();
  88. bool allInside = true;
  89. foreach (Vector3 point in hull)
  90. {
  91. if (!h1.ContainsPoint(point))
  92. {
  93. allInside = false;
  94. break;
  95. }
  96. }
  97. if (allInside)
  98. break;
  99. p1 = h1.RandomInteriorPoint(random);
  100. h2.Translate(-p1);
  101. h2.Scale(0.9f);
  102. h2.Translate(p1);
  103. }
  104. // Ensure that shape2 is fully disjoint from shape1 by picking a random plane and
  105. // translating the shapes to opposite sides of it
  106. Plane plane;
  107. plane.Normal = Vector3.Normalize(random.PointInSphere());
  108. plane.D = (float)(random.NextDouble() * 2 - 1) * 100;
  109. h1.Translate( (-h1.MinimumDistanceFromPlane(plane) + .001f) * plane.Normal);
  110. plane.D = -plane.D;
  111. plane.Normal = -plane.Normal;
  112. h2.Translate( (-h2.MinimumDistanceFromPlane(plane) + .001f) * plane.Normal);
  113. Check("Disjoint", h1, h2, contains, c => c == ContainmentType.Disjoint);
  114. Check("!Intersects", h1, h2, intersects, r => !r);
  115. }
  116. public void RunTests()
  117. {
  118. Random random = new Random(1);
  119. for (int i = 0; i < 10000; i++)
  120. {
  121. TestRandomObjects(
  122. random, new BoundingOrientedBoxOps(), new BoundingOrientedBoxOps(),
  123. (b1, b2) => b1.Contains(ref b2), (b1, b2) => b1.Intersects(ref b2));
  124. TestRandomObjects(
  125. random, new BoundingOrientedBoxOps(), new BoundingBoxOps(),
  126. (b1, b2) => b1.Contains(ref b2), (b1, b2) => b1.Intersects(ref b2));
  127. TestRandomObjects(
  128. random, new BoundingBoxOps(), new BoundingOrientedBoxOps(),
  129. (b1, b2) => BoundingOrientedBox.Contains(ref b1, ref b2), (b1, b2) => b2.Intersects(ref b1));
  130. TestRandomObjects(
  131. random, new BoundingBoxOps(), new BoundingBoxOps(),
  132. (b1, b2) => b1.Contains(b2), (b1, b2) => b1.Intersects(b2));
  133. TestRandomObjects(
  134. random, new BoundingSphereOps(), new TriangleOps(),
  135. (b, t) => TriangleTest.Contains(ref b, ref t),
  136. (b, t) => TriangleTest.Intersects(ref b, ref t));
  137. TestRandomObjects(
  138. random, new BoundingBoxOps(), new TriangleOps(),
  139. (b, t) => TriangleTest.Contains(ref b, ref t),
  140. (b, t) => TriangleTest.Intersects(ref b, ref t.V0, ref t.V1, ref t.V2));
  141. TestRandomObjects(
  142. random, new BoundingOrientedBoxOps(), new TriangleOps(),
  143. (b, t) => TriangleTest.Contains(ref b, ref t),
  144. (b, t) => TriangleTest.Intersects(ref b, ref t.V0, ref t.V1, ref t.V2));
  145. }
  146. Console.WriteLine("Passed: {0} Failed: {1}", TestsPassed, TestsFailed);
  147. Debug.WriteLine("Passed: {0} Failed: {1}", TestsPassed, TestsFailed);
  148. }
  149. }
  150. }