/// <inheritdoc/> public IEnumerable<Pair<int>> GetOverlaps(ISpatialPartition<int> otherPartition) { if (otherPartition == null) throw new ArgumentNullException("otherPartition"); var otherBasePartition = otherPartition as BasePartition<int>; if (otherBasePartition != null) otherBasePartition.UpdateInternal(); else otherPartition.Update(false); Update(false); #if !POOL_ENUMERABLES // Test all leaf nodes that touch the other partition's AABB. foreach (var leaf in GetLeafNodes(otherPartition.Aabb)) { var otherCandidates = otherPartition.GetOverlaps(GetAabb(leaf)); // We return one pair for each candidate vs. otherItem overlap. foreach (var otherCandidate in otherCandidates) { var overlap = new Pair<int>(leaf.Item, otherCandidate); if (Filter == null || Filter.Filter(overlap)) yield return overlap; } } #else return GetOverlapsWithPartitionWork.Create(this, otherPartition); #endif }
/// <inheritdoc/> public IEnumerable<Pair<int>> GetOverlaps(ISpatialPartition<int> otherPartition) { if (otherPartition == null) throw new ArgumentNullException("otherPartition"); var otherBasePartition = otherPartition as BasePartition<int>; if (otherBasePartition != null) otherBasePartition.UpdateInternal(); else otherPartition.Update(false); Update(false); // Test all leaf nodes that touch the other partition's AABB. foreach (var leaf in GetLeafNodes(otherPartition.Aabb)) { var otherCandidates = otherPartition.GetOverlaps(GetAabb(leaf)); // We return one pair for each candidate vs. otherItem overlap. foreach (var otherCandidate in otherCandidates) { var overlap = new Pair<int>(leaf.Item, otherCandidate); if (Filter == null || Filter.Filter(overlap)) yield return overlap; } } #else return GetOverlapsWithPartitionWork.Create(this, otherPartition); }
/// <inheritdoc/> public override IEnumerable<Pair<T>> GetOverlaps(Vector3 scale, Pose pose, ISpatialPartition<T> otherPartition, Vector3 otherScale, Pose otherPose) { if (otherPartition == null) throw new ArgumentNullException("otherPartition"); var otherBasePartition = otherPartition as BasePartition<T>; if (otherBasePartition != null) otherBasePartition.UpdateInternal(); else otherPartition.Update(false); UpdateInternal(); if (_root == null) return LinqHelper.Empty<Pair<T>>(); var otherTree = otherPartition as AdaptiveAabbTree<T>; if (otherTree == null) { // AdaptiveAabbTree<T> vs. ISpatialPartition<T>. return GetOverlapsImpl(scale, otherPartition, otherScale, pose.Inverse * otherPose); } else { // AdaptiveAabbTree<T> vs. AdaptiveAabbTree<T> if (otherTree._root == null) return LinqHelper.Empty<Pair<T>>(); return GetOverlapsImpl(scale, otherTree, otherScale, pose.Inverse * otherPose); } }
public void GetClosestPointCandidates(Vector3F scale, Pose pose, ISpatialPartition<int> otherPartition, Vector3F otherScale, Pose otherPose, Func<int, int, float> callback) { if (otherPartition == null) throw new ArgumentNullException("otherPartition"); if (callback == null) throw new ArgumentNullException("callback"); // Make sure we are up-to-date. var otherBasePartition = otherPartition as BasePartition<int>; if (otherBasePartition != null) otherBasePartition.UpdateInternal(); else otherPartition.Update(false); Update(false); if (_numberOfItems == 0) return; if (otherPartition is ISupportClosestPointQueries<int>) { // ----- CompressedAabbTree vs. ISupportClosestPointQueries<int> GetClosestPointCandidatesImpl(scale, pose, (ISupportClosestPointQueries<int>)otherPartition, otherScale, otherPose, callback); } else { // ----- CompressedAabbTree vs. * GetClosestPointCandidatesImpl(otherPartition, callback); } }
public void GetClosestPointCandidates(Vector3 scale, Pose pose, ISpatialPartition<int> otherPartition, Vector3 otherScale, Pose otherPose, Func<int, int, float> callback) { if (otherPartition == null) throw new ArgumentNullException("otherPartition"); if (callback == null) throw new ArgumentNullException("callback"); // Make sure we are up-to-date. var otherBasePartition = otherPartition as BasePartition<int>; if (otherBasePartition != null) otherBasePartition.UpdateInternal(); else otherPartition.Update(false); Update(false); if (_numberOfItems == 0) return; if (otherPartition is ISupportClosestPointQueries<int>) { // ----- CompressedAabbTree vs. ISupportClosestPointQueries<int> GetClosestPointCandidatesImpl(scale, pose, (ISupportClosestPointQueries<int>)otherPartition, otherScale, otherPose, callback); } else { // ----- CompressedAabbTree vs. * GetClosestPointCandidatesImpl(otherPartition, callback); } }
// TODO: Add DynamicAabbTree<T> vs. AabbTree<T>. /// <inheritdoc/> public override IEnumerable<Pair<T>> GetOverlaps(ISpatialPartition<T> otherPartition) { if (otherPartition == null) throw new ArgumentNullException("otherPartition"); var otherBasePartition = otherPartition as BasePartition<T>; if (otherBasePartition != null) otherBasePartition.UpdateInternal(); else otherPartition.Update(false); UpdateInternal(); if (_root == null) return LinqHelper.Empty<Pair<T>>(); var otherTree = otherPartition as DynamicAabbTree<T>; if (otherTree == null) { // DynamicAabbTree<T> vs. ISpatialPartition<T>. return GetOverlapsImpl(otherPartition); } else { // DynamicAabbTree<T> vs. DynamicAabbTree<T> if (otherTree._root == null) return LinqHelper.Empty<Pair<T>>(); return GetOverlapsImpl(otherTree); } }
/// <summary> /// Sets the spatial partition. (For use by the content pipeline only.) /// </summary> /// <param name="partition">The spatial partition.</param> /// <remarks> /// This method is used internally to directly set the spatial partition. The spatial partition /// might already be initialized and should not be invalidated. /// </remarks> internal void SetPartition(ISpatialPartition <int> partition) { if (partition != null) { _partition = partition; _partition.GetAabbForItem = i => Children[i].Aabb; // ----- Validate spatial partition. // Some spatial partitions, such as the CompressedAabbTree, are pre-initialized when // loaded via content pipeline. Other spatial partitions need to be initialized manually. int numberOfChildren = Children.Count; if (_partition.Count != numberOfChildren) { // The partition is not initialized. _partition.Clear(); for (int i = 0; i < numberOfChildren; i++) { _partition.Add(i); } _partition.Update(false); } else { // The partition is already initialized. Debug.Assert(Enumerable.Range(0, numberOfChildren).All(_partition.Contains), "Invalid partition. The pre-initialized partition does not contain the same children as the CompositeShape."); } } }
/// <inheritdoc/> public virtual IEnumerable <Pair <T> > GetOverlaps(Vector3F scale, Pose pose, ISpatialPartition <T> otherPartition, Vector3F otherScale, Pose otherPose) { if (otherPartition == null) { throw new ArgumentNullException("otherPartition"); } var otherBasePartition = otherPartition as BasePartition <T>; if (otherBasePartition != null) { otherBasePartition.UpdateInternal(); } else { otherPartition.Update(false); } UpdateInternal(); // Compute transformations. Vector3F scaleInverse = Vector3F.One / scale; Vector3F otherScaleInverse = Vector3F.One / otherScale; Pose toLocal = pose.Inverse * otherPose; Pose toOther = toLocal.Inverse; // Transform the AABB of the other partition into space of the this partition. var otherAabb = otherPartition.Aabb; otherAabb = otherAabb.GetAabb(otherScale, toLocal); // Apply local scale and transform to scaled local space of this partition. otherAabb.Scale(scaleInverse); // Transform to unscaled local space of this partition. var candidates = GetOverlaps(otherAabb); #if !POOL_ENUMERABLES foreach (var candidate in candidates) { // Transform AABB of this partition into space of the other partition. var aabb = GetAabbForItem(candidate); aabb = aabb.GetAabb(scale, toOther); // Apply local scale and transform to scaled local space of other partition. aabb.Scale(otherScaleInverse); // Transform to unscaled local space of other partition. foreach (var otherCandidate in otherPartition.GetOverlaps(aabb)) { var overlap = new Pair <T>(candidate, otherCandidate); if (Filter == null || Filter.Filter(overlap)) { yield return(overlap); } } } #else // Avoiding garbage: return(GetOverlapsWithTransformedPartitionWork.Create(this, otherPartition, candidates, ref scale, ref otherScaleInverse, ref toOther)); #endif }
public SpatialPartitionSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.Gray; GraphicsScreen.DrawReticle = true; SetCamera(new Vector3(0, 1, 10), 0, 0); // Create a spatial partition. DigitalRune Geometry supports several types, see also // http://digitalrune.github.io/DigitalRune-Documentation/html/e32cab3b-cc7c-42ee-8ec9-23dd4467edd0.htm#WhichPartition // An AabbTree is useful for static objects. A DynamicAabbTree is good for moving objects. // The spatial partition can manage different types of items. In this case it manages // GeometricObjects. A delegate has to inform the spatial partition how to get the AABB // of an object. //_spatialPartition = new DynamicAabbTree<GeometricObject> _spatialPartition = new AabbTree <GeometricObject> { GetAabbForItem = geometricObject => geometricObject.Aabb, // Optional: The tree is automatically built using a mixed top-down/bottom-up approach. // Bottom-up building is slower but produces better trees. If the tree building takes too // long, we can lower the BottomUpBuildThreshold (default is 128). //BottomUpBuildThreshold = 0, // Optional: A filter can be set to disable certain kind of overlaps. //Filter = ... }; // Create a triangle mesh. var triangleMesh = new SphereShape(1).GetMesh(0.01f, 4); var triangleMeshShape = new TriangleMeshShape(triangleMesh) { // TriangleMeshShapes can also use a spatial partition to manage triangle. // The items in the spatial partition are the triangle indices. The GetAabbForItem // delegate is set automatically. Partition = new AabbTree <int>(), }; // Spatial partitions are built automatically when needed. However, it is still recommended // to call Update to initialize the spatial partition explicitly. triangleMeshShape.Partition.Update(false); // Add a lot of triangle mesh objects to _spatialPartition. var random = new Random(); for (int i = 0; i < 50; i++) { var randomPosition = new Vector3(random.NextFloat(-6, 6), random.NextFloat(-3, 3), random.NextFloat(-10, 0)); var geometricObject = new GeometricObject(triangleMeshShape, new Pose(randomPosition)); _spatialPartition.Add(geometricObject); } _spatialPartition.Update(false); }
public SpatialPartitionSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.Gray; GraphicsScreen.DrawReticle = true; SetCamera(new Vector3F(0, 1, 10), 0, 0); // Create a spatial partition. DigitalRune Geometry supports several types, see also // http://digitalrune.github.io/DigitalRune-Documentation/html/e32cab3b-cc7c-42ee-8ec9-23dd4467edd0.htm#WhichPartition // An AabbTree is useful for static objects. A DynamicAabbTree is good for moving objects. // The spatial partition can manage different types of items. In this case it manages // GeometricObjects. A delegate has to inform the spatial partition how to get the AABB // of an object. //_spatialPartition = new DynamicAabbTree<GeometricObject> _spatialPartition = new AabbTree<GeometricObject> { GetAabbForItem = geometricObject => geometricObject.Aabb, // Optional: The tree is automatically built using a mixed top-down/bottom-up approach. // Bottom-up building is slower but produces better trees. If the tree building takes too // long, we can lower the BottomUpBuildThreshold (default is 128). //BottomUpBuildThreshold = 0, // Optional: A filter can be set to disable certain kind of overlaps. //Filter = ... }; // Create a triangle mesh. var triangleMesh = new SphereShape(1).GetMesh(0.01f, 4); var triangleMeshShape = new TriangleMeshShape(triangleMesh) { // TriangleMeshShapes can also use a spatial partition to manage triangle. // The items in the spatial partition are the triangle indices. The GetAabbForItem // delegate is set automatically. Partition = new AabbTree<int>(), }; // Spatial partitions are built automatically when needed. However, it is still recommended // to call Update to initialize the spatial partition explicitly. triangleMeshShape.Partition.Update(false); // Add a lot of triangle mesh objects to _spatialPartition. var random = new Random(); for (int i = 0; i < 50; i++) { var randomPosition = new Vector3F(random.NextFloat(-6, 6), random.NextFloat(-3, 3), random.NextFloat(-10, 0)); var geometricObject = new GeometricObject(triangleMeshShape, new Pose(randomPosition)); _spatialPartition.Add(geometricObject); } _spatialPartition.Update(false); }
public void GetClosestPointCandidates(Vector3F scale, Pose pose, ISpatialPartition <T> otherPartition, Vector3F otherScale, Pose otherPose, Func <T, T, float> callback) { if (otherPartition == null) { throw new ArgumentNullException("otherPartition"); } if (callback == null) { throw new ArgumentNullException("callback"); } // Make sure we are up-to-date. var otherBasePartition = otherPartition as BasePartition <T>; if (otherBasePartition != null) { otherBasePartition.UpdateInternal(); } else { otherPartition.Update(false); } UpdateInternal(); if (_root == null) { return; } if (otherPartition is AdaptiveAabbTree <T> ) { // ----- AdaptiveAabbTree<T> vs. AdaptiveAabbTree<T> // (Transform second partition into local space.) var otherTree = (AdaptiveAabbTree <T>)otherPartition; float closestPointDistanceSquared = float.PositiveInfinity; GetClosestPointCandidatesImpl(_root, scale, otherTree._root, otherScale, pose.Inverse * otherPose, callback, ref closestPointDistanceSquared); } else if (otherPartition is ISupportClosestPointQueries <T> ) { // ----- AdaptiveAabbTree<T> vs. ISupportClosestPointQueries<T> GetClosestPointCandidatesImpl(scale, pose, (ISupportClosestPointQueries <T>)otherPartition, otherScale, otherPose, callback); } else { // ----- AdaptiveAabbTree<T> vs. * GetClosestPointCandidatesImpl(otherPartition, callback); } }
/// <inheritdoc/> public virtual IEnumerable <Pair <T> > GetOverlaps(ISpatialPartition <T> otherPartition) { if (otherPartition == null) { throw new ArgumentNullException("otherPartition"); } var otherBasePartition = otherPartition as BasePartition <T>; if (otherBasePartition != null) { otherBasePartition.UpdateInternal(); } else { otherPartition.Update(false); } UpdateInternal(); #if !POOL_ENUMERABLES // Get all items that touch the other partition's AABB. var candidates = GetOverlaps(otherPartition.Aabb); // Now, we test each candidate against the other partition. foreach (var candidate in candidates) { Aabb candidateAabb = GetAabbForItem(candidate); var otherCandidates = otherPartition.GetOverlaps(candidateAabb); // We return one pair for each candidate vs. otherItem overlap. foreach (var otherCandidate in otherCandidates) { var overlap = new Pair <T>(candidate, otherCandidate); if (Filter == null || Filter.Filter(overlap)) { yield return(overlap); } } } #else // Avoiding garbage: return(GetOverlapsWithPartitionWork.Create(this, otherPartition)); #endif }
/// <inheritdoc/> public IEnumerable<Pair<int>> GetOverlaps(Vector3 scale, Pose pose, ISpatialPartition<int> otherPartition, Vector3 otherScale, Pose otherPose) { if (otherPartition == null) throw new ArgumentNullException("otherPartition"); var otherBasePartition = otherPartition as BasePartition<int>; if (otherBasePartition != null) otherBasePartition.UpdateInternal(); else otherPartition.Update(false); Update(false); // Compute transformations. Vector3 scaleInverse = Vector3.One / scale; Vector3 otherScaleInverse = Vector3.One / otherScale; Pose toLocal = pose.Inverse * otherPose; Pose toOther = toLocal.Inverse; // Transform the AABB of the other partition into space of the this partition. var otherAabb = otherPartition.Aabb; otherAabb = otherAabb.GetAabb(otherScale, toLocal); // Apply local scale and transform to scaled local space of this partition. otherAabb.Scale(scaleInverse); // Transform to unscaled local space of this partition. var leafNodes = GetLeafNodes(otherAabb); foreach (var leaf in leafNodes) { // Transform AABB of this partition into space of the other partition. var aabb = GetAabb(leaf); aabb = aabb.GetAabb(scale, toOther); // Apply local scale and transform to scaled local space of other partition. aabb.Scale(otherScaleInverse); // Transform to unscaled local space of other partition. foreach (var otherCandidate in otherPartition.GetOverlaps(aabb)) { var overlap = new Pair<int>(leaf.Item, otherCandidate); if (Filter == null || Filter.Filter(overlap)) yield return overlap; } } #else return GetOverlapsWithTransformedPartitionWork.Create(this, otherPartition, leafNodes, ref scale, ref otherScaleInverse, ref toOther); }
/// <inheritdoc/> public override IEnumerable <Pair <T> > GetOverlaps(Vector3F scale, Pose pose, ISpatialPartition <T> otherPartition, Vector3F otherScale, Pose otherPose) { if (otherPartition == null) { throw new ArgumentNullException("otherPartition"); } var otherBasePartition = otherPartition as BasePartition <T>; if (otherBasePartition != null) { otherBasePartition.UpdateInternal(); } else { otherPartition.Update(false); } UpdateInternal(); if (_root == null) { return(LinqHelper.Empty <Pair <T> >()); } var otherTree = otherPartition as DynamicAabbTree <T>; if (otherTree == null) { // DynamicAabbTree<T> vs. ISpatialPartition<T>. return(GetOverlapsImpl(scale, otherPartition, otherScale, pose.Inverse * otherPose)); } else { // DynamicAabbTree<T> vs. DynamicAabbTree<T> if (otherTree._root == null) { return(LinqHelper.Empty <Pair <T> >()); } return(GetOverlapsImpl(scale, otherTree, otherScale, pose.Inverse * otherPose)); } }
/// <inheritdoc/> public override IEnumerable <Pair <T> > GetOverlaps(ISpatialPartition <T> otherPartition) { if (otherPartition == null) { throw new ArgumentNullException("otherPartition"); } var otherBasePartition = otherPartition as BasePartition <T>; if (otherBasePartition != null) { otherBasePartition.UpdateInternal(); } else { otherPartition.Update(false); } UpdateInternal(); if (_root == null) { return(LinqHelper.Empty <Pair <T> >()); } var otherTree = otherPartition as AdaptiveAabbTree <T>; if (otherTree == null) { // AdaptiveAabbTree<T> vs. ISpatialPartition<T>. return(GetOverlapsImpl(otherPartition)); } else { // AdaptiveAabbTree<T> vs. AdaptiveAabbTree<T> if (otherTree._root == null) { return(LinqHelper.Empty <Pair <T> >()); } return(GetOverlapsImpl(otherTree)); } }
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); }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- #if XNA || MONOGAME /// <summary> /// Sets the spatial partition. (For use by the content pipeline only.) /// </summary> /// <param name="partition">The spatial partition.</param> /// <remarks> /// This method is used internally to directly set the spatial partition. The spatial partition /// might already be initialized and should not be invalidated. /// </remarks> internal void SetPartition(ISpatialPartition<int> partition) { if (partition != null) { _partition = partition; _partition.GetAabbForItem = i => Children[i].Aabb; // ----- Validate spatial partition. // Some spatial partitions, such as the CompressedAabbTree, are pre-initialized when // loaded via content pipeline. Other spatial partitions need to be initialized manually. int numberOfChildren = Children.Count; if (_partition.Count != numberOfChildren) { // The partition is not initialized. _partition.Clear(); for (int i = 0; i < numberOfChildren; i++) _partition.Add(i); _partition.Update(false); } else { // The partition is already initialized. Debug.Assert(Enumerable.Range(0, numberOfChildren).All(_partition.Contains), "Invalid partition. The pre-initialized partition does not contain the same children as the CompositeShape."); } } }
/// <inheritdoc/> public IEnumerable<Pair<int>> GetOverlaps(Vector3F scale, Pose pose, ISpatialPartition<int> otherPartition, Vector3F otherScale, Pose otherPose) { if (otherPartition == null) throw new ArgumentNullException("otherPartition"); var otherBasePartition = otherPartition as BasePartition<int>; if (otherBasePartition != null) otherBasePartition.UpdateInternal(); else otherPartition.Update(false); Update(false); // Compute transformations. Vector3F scaleInverse = Vector3F.One / scale; Vector3F otherScaleInverse = Vector3F.One / otherScale; Pose toLocal = pose.Inverse * otherPose; Pose toOther = toLocal.Inverse; // Transform the AABB of the other partition into space of the this partition. var otherAabb = otherPartition.Aabb; otherAabb = otherAabb.GetAabb(otherScale, toLocal); // Apply local scale and transform to scaled local space of this partition. otherAabb.Scale(scaleInverse); // Transform to unscaled local space of this partition. var leafNodes = GetLeafNodes(otherAabb); #if !POOL_ENUMERABLES foreach (var leaf in leafNodes) { // Transform AABB of this partition into space of the other partition. var aabb = GetAabb(leaf); aabb = aabb.GetAabb(scale, toOther); // Apply local scale and transform to scaled local space of other partition. aabb.Scale(otherScaleInverse); // Transform to unscaled local space of other partition. foreach (var otherCandidate in otherPartition.GetOverlaps(aabb)) { var overlap = new Pair<int>(leaf.Item, otherCandidate); if (Filter == null || Filter.Filter(overlap)) yield return overlap; } } #else return GetOverlapsWithTransformedPartitionWork.Create(this, otherPartition, leafNodes, ref scale, ref otherScaleInverse, ref toOther); #endif }