/// <summary> /// Checks if two regions are overlapping /// </summary> /// <param name="in_region">Other region to check</param> /// <returns>True if regions are overlapping</returns> public bool IsOverlapping(QuadTreeFloatPointRegion in_region) { return((Math.Abs(CenterX - in_region.CenterX) <= (in_region.HalfSize + HalfSize)) && (Math.Abs(CenterY - in_region.CenterY) <= (in_region.HalfSize + HalfSize))); }
public List <T> QueryNeighbours(float in_x, float in_y, int in_neighbours_count) { QuadTreeFloatPointRegion neighbour_region = new QuadTreeFloatPointRegion(in_x, in_y, 0); Stack <QuadTreeNode> stack = new Stack <QuadTreeNode>(); QuadTreeNode current; List <T> neighbours = new List <T>(); double[] neighbour_distances = new double[in_neighbours_count]; double neighbours_worst_distance = 0; int neighbours_worst_index = 0; // set root node as current current = m_root; while (current != null) { // move downwards if this node has child nodes if (current.Children != null) { // store regions in the stack and continue with the closest region double closest_region_distance; int closest_region_index; // find closest region closest_region_index = 0; closest_region_distance = current.Children[0].Bounds.GetSquaredDistanceOfCenter(in_x, in_y); for (int i = 0; i < 4; i++) { double distance = current.Children[i].Bounds.GetSquaredDistanceOfCenter(in_x, in_y); if (distance < closest_region_distance) { closest_region_distance = distance; closest_region_index = i; } } // store regions for (int i = 0; i < 4; i++) { if (i == closest_region_index) { continue; } // if the neighbor region is defined then store only the overlapping regions, otherwise store all regions if (neighbour_region.HalfSize == 0 || current.Children[i].Bounds.IsOverlapping(neighbour_region)) { stack.Push(current.Children[i]); } } // continue processing with the closest region current = current.Children[closest_region_index]; } else { // process data points QuadTreeLeaf current_leaf_entry = current.Data; while (current_leaf_entry != null) { // calculate distance (squared) double squared_distance = current_leaf_entry.GetSquaredDistance(in_x, in_y); if (current.Data != null) { // simply store data point if the list is not full if (neighbours.Count < in_neighbours_count) { if (neighbours.Count == 0) { neighbours_worst_distance = squared_distance; neighbours_worst_index = 0; } else { if (squared_distance > neighbours_worst_distance) { neighbours_worst_distance = squared_distance; neighbours_worst_index = neighbours.Count; } } // add this item to the neighbours list neighbour_distances[neighbours.Count] = squared_distance; neighbours.Add(current_leaf_entry.Data); // if the required number of neighbour is found store the worst distance in the region if (neighbours.Count == in_neighbours_count) { neighbour_region.HalfSize = (float)Math.Sqrt(neighbours_worst_distance); } } else { // list is full, store only when this item is closer than the worst item (largest distance) in the list if (squared_distance < neighbours_worst_distance) { // replace worst element neighbour_distances[neighbours_worst_index] = squared_distance; neighbours[neighbours_worst_index] = current_leaf_entry.Data; // find the current worst element neighbours_worst_index = 0; neighbours_worst_distance = neighbour_distances[0]; for (int i = 1; i < in_neighbours_count; i++) { if (neighbour_distances[i] > neighbours_worst_distance) { neighbours_worst_distance = neighbour_distances[i]; neighbours_worst_index = i; } } neighbour_region.HalfSize = (float)Math.Sqrt(neighbours_worst_distance); } } } current_leaf_entry = current_leaf_entry.Next; } // get new element from the stack or exit if no more element to investigate do { if (stack.Count > 0) { current = stack.Pop(); } else { current = null; break; } // if the neighbour region is know skip all elements with a non-overlapping region } while (neighbour_region.HalfSize > 0 && !current.Bounds.IsOverlapping(neighbour_region)); } } return(neighbours); }
/// <summary> /// Internal recursive insert function /// </summary> /// <param name="in_current_node"></param> /// <param name="in_node_to_insert"></param> private void Insert(QuadTreeNode in_current_node, QuadTreeLeaf in_node_to_insert) { // check if point ot insert is inside -> if it is not then it can not be child of this node if (!in_current_node.Bounds.IsPointInside(in_node_to_insert.Data)) { return; } QuadTreeLeaf nodes_to_insert = null; QuadTreeLeaf node; int quadrant; // if this node is leaf if (in_current_node.Children == null) { // this is the first data item in the leaf if (in_current_node.Data == null) { in_current_node.Data = in_node_to_insert; return; } else { int item_count = 0; // add to the end of list of data node = in_current_node.Data; while (true) { if (Math.Abs(in_node_to_insert.Data.X - node.Data.X) <= float.Epsilon && Math.Abs(in_node_to_insert.Data.Y - node.Data.Y) <= float.Epsilon) { throw new ArgumentException("Key already exists"); } item_count++; if (node.Next != null) { node = node.Next; } else { break; } } // there is room for this item if (item_count < m_bucket_capacity) { // add node to the list of data node.Next = in_node_to_insert; return; } // current node needs to be splitted nodes_to_insert = (QuadTreeLeaf)in_current_node.Data; // remove data in_current_node.Data = null; } } else { // move downward on the tree following the apropriate quadrant quadrant = in_current_node.Bounds.GetQuadrantIndex(in_node_to_insert.Data); Insert(in_current_node.Children[quadrant], in_node_to_insert); return; } // subdivide current node QuadTreeFloatPointRegion bounds = in_current_node.Bounds; float half = bounds.HalfSize / 2; QuadTreeNode[] subdivision = new QuadTreeNode[4] { new QuadTreeNode(new QuadTreeFloatPointRegion(bounds.CenterX - half, bounds.CenterY - half, half)), new QuadTreeNode(new QuadTreeFloatPointRegion(bounds.CenterX + half, bounds.CenterY - half, half)), new QuadTreeNode(new QuadTreeFloatPointRegion(bounds.CenterX - half, bounds.CenterY + half, half)), new QuadTreeNode(new QuadTreeFloatPointRegion(bounds.CenterX + half, bounds.CenterY + half, half)) }; // insert node quadrant = bounds.GetQuadrantIndex(in_node_to_insert.Data); Insert(subdivision[quadrant], in_node_to_insert); // insert nodes from the splitted node node = nodes_to_insert; QuadTreeLeaf next_node; while (node != null) { next_node = node.Next; node.Next = null; quadrant = bounds.GetQuadrantIndex(node.Data); Insert(subdivision[quadrant], node); node = next_node; } in_current_node.Children = subdivision; }
/// <summary> /// Creates QuadTree for the specified region with bucket capacity set to one. /// </summary> /// <param name="in_region">Region used for the stored point</param> public QuadTreeFloatPoint(QuadTreeFloatPointRegion in_region) : this(in_region, 1) { }
/// <summary> /// Construct QuadTree for the given region with the sepcified bucket capacity /// </summary> /// <param name="in_region">Coordinate region used for stored points</param> /// <param name="in_bucket_capacity">Number of points stored in one bucket</param> public QuadTreeFloatPoint(QuadTreeFloatPointRegion in_region, int in_bucket_capacity) { m_root = new QuadTreeNode(in_region); m_bucket_capacity = in_bucket_capacity; m_node_count = 0; }
public QuadTreeNode(QuadTreeFloatPointRegion in_region) { Bounds = in_region; Children = null; Data = null; }