123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- using System;
- using System.Collections.Generic;
- using FarseerPhysics.Collision;
- using Microsoft.Xna.Framework;
- public class Element<T>
- {
- public QuadTree<T> Parent;
- public AABB Span;
- public T Value;
- public Element(T value, AABB span)
- {
- Span = span;
- Value = value;
- Parent = null;
- }
- }
- public class QuadTree<T>
- {
- public int MaxBucket;
- public int MaxDepth;
- public List<Element<T>> Nodes;
- public AABB Span;
- public QuadTree<T>[] SubTrees;
- public QuadTree(AABB span, int maxbucket, int maxdepth)
- {
- Span = span;
- Nodes = new List<Element<T>>();
- MaxBucket = maxbucket;
- MaxDepth = maxdepth;
- }
- public bool IsPartitioned
- {
- get { return SubTrees != null; }
- }
- /// <summary>
- /// returns the quadrant of span that entirely contains test. if none, return 0.
- /// </summary>
- /// <param name="span"></param>
- /// <param name="test"></param>
- /// <returns></returns>
- private int Partition(AABB span, AABB test)
- {
- if (span.Q1.Contains(ref test)) return 1;
- if (span.Q2.Contains(ref test)) return 2;
- if (span.Q3.Contains(ref test)) return 3;
- if (span.Q4.Contains(ref test)) return 4;
- return 0;
- }
- public void AddNode(Element<T> node)
- {
- if (!IsPartitioned)
- {
- if (Nodes.Count >= MaxBucket && MaxDepth > 0) //bin is full and can still subdivide
- {
- //
- //partition into quadrants and sort existing nodes amonst quads.
- //
- Nodes.Add(node); //treat new node just like other nodes for partitioning
- SubTrees = new QuadTree<T>[4];
- SubTrees[0] = new QuadTree<T>(Span.Q1, MaxBucket, MaxDepth - 1);
- SubTrees[1] = new QuadTree<T>(Span.Q2, MaxBucket, MaxDepth - 1);
- SubTrees[2] = new QuadTree<T>(Span.Q3, MaxBucket, MaxDepth - 1);
- SubTrees[3] = new QuadTree<T>(Span.Q4, MaxBucket, MaxDepth - 1);
- List<Element<T>> remNodes = new List<Element<T>>();
- //nodes that are not fully contained by any quadrant
- foreach (Element<T> n in Nodes)
- {
- switch (Partition(Span, n.Span))
- {
- case 1: //quadrant 1
- SubTrees[0].AddNode(n);
- break;
- case 2:
- SubTrees[1].AddNode(n);
- break;
- case 3:
- SubTrees[2].AddNode(n);
- break;
- case 4:
- SubTrees[3].AddNode(n);
- break;
- default:
- n.Parent = this;
- remNodes.Add(n);
- break;
- }
- }
- Nodes = remNodes;
- }
- else
- {
- node.Parent = this;
- Nodes.Add(node);
- //if bin is not yet full or max depth has been reached, just add the node without subdividing
- }
- }
- else //we already have children nodes
- {
- //
- //add node to specific sub-tree
- //
- switch (Partition(Span, node.Span))
- {
- case 1: //quadrant 1
- SubTrees[0].AddNode(node);
- break;
- case 2:
- SubTrees[1].AddNode(node);
- break;
- case 3:
- SubTrees[2].AddNode(node);
- break;
- case 4:
- SubTrees[3].AddNode(node);
- break;
- default:
- node.Parent = this;
- Nodes.Add(node);
- break;
- }
- }
- }
- /// <summary>
- /// tests if ray intersects AABB
- /// </summary>
- /// <param name="aabb"></param>
- /// <returns></returns>
- public static bool RayCastAABB(AABB aabb, Vector2 p1, Vector2 p2)
- {
- AABB segmentAABB = new AABB();
- {
- Vector2.Min(ref p1, ref p2, out segmentAABB.LowerBound);
- Vector2.Max(ref p1, ref p2, out segmentAABB.UpperBound);
- }
- if (!AABB.TestOverlap(aabb, segmentAABB)) return false;
- Vector2 rayDir = p2 - p1;
- Vector2 rayPos = p1;
- Vector2 norm = new Vector2(-rayDir.Y, rayDir.X); //normal to ray
- if (norm.Length() == 0.0)
- return true; //if ray is just a point, return true (iff point is within aabb, as tested earlier)
- norm.Normalize();
- float dPos = Vector2.Dot(rayPos, norm);
- Vector2[] verts = aabb.GetVertices();
- float d0 = Vector2.Dot(verts[0], norm) - dPos;
- for (int i = 1; i < 4; i++)
- {
- float d = Vector2.Dot(verts[i], norm) - dPos;
- if (Math.Sign(d) != Math.Sign(d0))
- //return true if the ray splits the vertices (ie: sign of dot products with normal are not all same)
- return true;
- }
- return false;
- }
- public void QueryAABB(Func<Element<T>, bool> callback, ref AABB searchR)
- {
- Stack<QuadTree<T>> stack = new Stack<QuadTree<T>>();
- stack.Push(this);
- while (stack.Count > 0)
- {
- QuadTree<T> qt = stack.Pop();
- if (!AABB.TestOverlap(ref searchR, ref qt.Span))
- continue;
- foreach (Element<T> n in qt.Nodes)
- if (AABB.TestOverlap(ref searchR, ref n.Span))
- {
- if (!callback(n)) return;
- }
- if (qt.IsPartitioned)
- foreach (QuadTree<T> st in qt.SubTrees)
- stack.Push(st);
- }
- }
- public void RayCast(Func<RayCastInput, Element<T>, float> callback, ref RayCastInput input)
- {
- Stack<QuadTree<T>> stack = new Stack<QuadTree<T>>();
- stack.Push(this);
- float maxFraction = input.MaxFraction;
- Vector2 p1 = input.Point1;
- Vector2 p2 = p1 + (input.Point2 - input.Point1) * maxFraction;
- while (stack.Count > 0)
- {
- QuadTree<T> qt = stack.Pop();
- if (!RayCastAABB(qt.Span, p1, p2))
- continue;
- foreach (Element<T> n in qt.Nodes)
- {
- if (!RayCastAABB(n.Span, p1, p2))
- continue;
- RayCastInput subInput;
- subInput.Point1 = input.Point1;
- subInput.Point2 = input.Point2;
- subInput.MaxFraction = maxFraction;
- float value = callback(subInput, n);
- if (value == 0.0f)
- return; // the client has terminated the raycast.
- if (value <= 0.0f)
- continue;
- maxFraction = value;
- p2 = p1 + (input.Point2 - input.Point1) * maxFraction; //update segment endpoint
- }
- if (IsPartitioned)
- foreach (QuadTree<T> st in qt.SubTrees)
- stack.Push(st);
- }
- }
- public void GetAllNodesR(ref List<Element<T>> nodes)
- {
- nodes.AddRange(Nodes);
- if (IsPartitioned)
- foreach (QuadTree<T> st in SubTrees) st.GetAllNodesR(ref nodes);
- }
- public void RemoveNode(Element<T> node)
- {
- node.Parent.Nodes.Remove(node);
- }
- public void Reconstruct()
- {
- List<Element<T>> allNodes = new List<Element<T>>();
- GetAllNodesR(ref allNodes);
- Clear();
- allNodes.ForEach(AddNode);
- }
- public void Clear()
- {
- Nodes.Clear();
- SubTrees = null;
- }
- }
|