/// <summary> /// Invalidates the whole triangle mesh or a single triangle. /// </summary> /// <param name="triangleIndex"> /// Index of the triangle. Can be -1 to invalidate the whole mesh. /// </param> /// <param name="invalidateTopology"> /// If set to <see langword="true"/> the mesh topology is invalidated. /// </param> /// <remarks> /// <para> /// This method must be called if the position of a triangle stored in <see cref="Mesh"/> is /// changed. This method updates the <see cref="Partition"/> and raises the /// <see cref="Shape.Changed"/> event by calling <see cref="Shape.OnChanged"/>. /// </para> /// <para> /// If the mesh topology has changed, <paramref name="invalidateTopology"/> must be set to /// <see langword="true"/>. The topology has changed if triangle neighbor relationships have /// changed. If each triangle has the same neighbor triangles as before and only the vertices /// were moved, <paramref name="invalidateTopology"/> can be <see langword="false"/>. /// </para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="triangleIndex"/> is out of range. /// </exception> public void Invalidate(int triangleIndex, bool invalidateTopology) { int numberOfTriangles = Mesh.NumberOfTriangles; if (triangleIndex >= numberOfTriangles) { throw new ArgumentOutOfRangeException("triangleIndex"); } // Set cached AABB to "invalid". _aabbLocal = new Aabb(new Vector3(float.NaN), new Vector3(float.NaN)); // Fill new spatial partition. if (_partition != null) { if (numberOfTriangles != _partition.Count) { // Triangle count has changed. Re-initialize partition content. _partition.Clear(); for (int i = 0; i < numberOfTriangles; i++) { _partition.Add(i); } } else { // Same number of triangles - invalidate the triangle. if (triangleIndex >= 0) { _partition.Invalidate(triangleIndex); } else { _partition.Invalidate(); } } } if (invalidateTopology) { ComputeTriangleNeighbors(); } if (triangleIndex < 0) { OnChanged(ShapeChangedEventArgs.Empty); } else { var eventArgs = ShapeChangedEventArgs.Create(triangleIndex); OnChanged(eventArgs); eventArgs.Recycle(); } }
private void TestPartition(ISpatialPartition <int> partition) { partition.Clear(); Assert.AreEqual(0, partition.Count); partition.EnableSelfOverlaps = true; Assert.AreEqual(0, partition.GetOverlaps().Count()); Assert.AreEqual(0, partition.GetOverlaps(0).Count()); Assert.AreEqual(0, partition.GetOverlaps(new Aabb()).Count()); Assert.AreEqual(0, partition.GetOverlaps(_partition2).Count()); Assert.AreEqual(0, partition.GetOverlaps(Vector3.One, Pose.Identity, _partition2, Vector3.One, Pose.Identity).Count()); var testObject = new TestObject(new Aabb(new Vector3(10), new Vector3(10))); _testObjects.Add(testObject); partition.Add(testObject.Id); for (int i = 0; i < 1000; i++) { // ----- Tests Assert.AreEqual(_testObjects.Count, partition.Count, "Wrong number of items."); if (i > 10 && i % 6 == 0) { TestGetOverlaps0(partition); } if (i > 10 && i % 6 == 1) { TestGetOverlaps1(partition); } if (i > 10 && i % 6 == 2) { TestGetOverlaps2(partition); } if (i > 10 && i % 6 == 3) { TestGetOverlaps3(partition); } if (i > 10 && i % 6 == 4) { TestGetOverlaps4(partition); } if (i > 10 && i % 6 == 5) { TestGetOverlaps5(partition); } // Update partition. From time to time rebuild all. // For the above tests update should have been called automatically! partition.Update(i % 10 == 9); TestAabb(partition); var dice100 = RandomHelper.Random.Next(0, 100); if (dice100 < 2) { // Test remove/re-add without Update inbetween. if (partition.Count > 0) { partition.Remove(_testObjects[0].Id); partition.Add(_testObjects[0].Id); } } dice100 = RandomHelper.Random.Next(0, 100); if (dice100 < 10) { // Remove objects. int removeCount = RandomHelper.Random.NextInteger(1, 4); for (int k = 0; k < removeCount && partition.Count > 0; k++) { var index = RandomHelper.Random.NextInteger(0, partition.Count - 1); var obj = _testObjects[index]; _testObjects.Remove(obj); partition.Remove(obj.Id); } } dice100 = RandomHelper.Random.Next(0, 100); if (dice100 < 10) { // Add new objects. int addCount = RandomHelper.Random.NextInteger(1, 4); for (int k = 0; k < addCount; k++) { var newObj = new TestObject(GetRandomAabb()); _testObjects.Add(newObj); partition.Add(newObj.Id); } } else { // Move an object. int moveCount = RandomHelper.Random.NextInteger(1, 10); for (int k = 0; k < moveCount && partition.Count > 0; k++) { var index = RandomHelper.Random.NextInteger(0, partition.Count - 1); var obj = _testObjects[index]; obj.Aabb = GetRandomAabb(); partition.Invalidate(obj.Id); } } // From time to time invalidate all. if (dice100 < 3) { partition.Invalidate(); } // From time to time change EnableSelfOverlaps. if (dice100 > 3 && dice100 < 6) { partition.EnableSelfOverlaps = false; } else if (dice100 < 10) { partition.EnableSelfOverlaps = true; } // From time to time change filter. if (dice100 > 10 && dice100 < 13) { partition.Filter = null; } else if (dice100 < 10) { if (partition.Filter == null) { partition.Filter = new DelegatePairFilter <int>(AreInSameGroup); } } } partition.Clear(); Assert.AreEqual(0, partition.Count); }
private void TestPartition(ISpatialPartition<int> partition) { partition.Clear(); Assert.AreEqual(0, partition.Count); partition.EnableSelfOverlaps = true; Assert.AreEqual(0, partition.GetOverlaps().Count()); Assert.AreEqual(0, partition.GetOverlaps(0).Count()); Assert.AreEqual(0, partition.GetOverlaps(new Aabb()).Count()); Assert.AreEqual(0, partition.GetOverlaps(_partition2).Count()); Assert.AreEqual(0, partition.GetOverlaps(Vector3F.One, Pose.Identity, _partition2, Vector3F.One, Pose.Identity).Count()); var testObject = new TestObject(new Aabb(new Vector3F(10), new Vector3F(10))); _testObjects.Add(testObject); partition.Add(testObject.Id); for (int i = 0; i < 1000; i++) { // ----- Tests Assert.AreEqual(_testObjects.Count, partition.Count, "Wrong number of items."); if (i > 10 && i % 6 == 0) TestGetOverlaps0(partition); if (i > 10 && i % 6 == 1) TestGetOverlaps1(partition); if (i > 10 && i % 6 == 2) TestGetOverlaps2(partition); if (i > 10 && i % 6 == 3) TestGetOverlaps3(partition); if (i > 10 && i % 6 == 4) TestGetOverlaps4(partition); if (i > 10 && i % 6 == 5) TestGetOverlaps5(partition); // Update partition. From time to time rebuild all. // For the above tests update should have been called automatically! partition.Update(i % 10 == 9); TestAabb(partition); var dice100 = RandomHelper.Random.Next(0, 100); if (dice100 < 2) { // Test remove/re-add without Update inbetween. if (partition.Count > 0) { partition.Remove(_testObjects[0].Id); partition.Add(_testObjects[0].Id); } } dice100 = RandomHelper.Random.Next(0, 100); if (dice100 < 10) { // Remove objects. int removeCount = RandomHelper.Random.NextInteger(1, 4); for (int k = 0; k < removeCount && partition.Count > 0; k++) { var index = RandomHelper.Random.NextInteger(0, partition.Count - 1); var obj = _testObjects[index]; _testObjects.Remove(obj); partition.Remove(obj.Id); } } dice100 = RandomHelper.Random.Next(0, 100); if (dice100 < 10) { // Add new objects. int addCount = RandomHelper.Random.NextInteger(1, 4); for (int k = 0; k < addCount; k++) { var newObj = new TestObject(GetRandomAabb()); _testObjects.Add(newObj); partition.Add(newObj.Id); } } else { // Move an object. int moveCount = RandomHelper.Random.NextInteger(1, 10); for (int k = 0; k < moveCount && partition.Count > 0; k++) { var index = RandomHelper.Random.NextInteger(0, partition.Count - 1); var obj = _testObjects[index]; obj.Aabb = GetRandomAabb(); partition.Invalidate(obj.Id); } } // From time to time invalidate all. if (dice100 < 3) partition.Invalidate(); // From time to time change EnableSelfOverlaps. if (dice100 > 3 && dice100 < 6) partition.EnableSelfOverlaps = false; else if (dice100 < 10) partition.EnableSelfOverlaps = true; // From time to time change filter. if (dice100 > 10 && dice100 < 13) { partition.Filter = null; } else if (dice100 < 10) { if (partition.Filter == null) partition.Filter = new DelegatePairFilter<int>(AreInSameGroup); } } partition.Clear(); Assert.AreEqual(0, partition.Count); }