private bool approximate(KDTreeNode <T> current, double[] position, KDTreeNodeCollection <T> list, int maxLeaves, ref int visited) { // Compute distance from this node to the point double d = this.Distance(position, current.Position); list.Add(current, d); if (++visited > maxLeaves) { return(true); } // Check for leafs on the opposite sides of // the subtrees to nearest possible neighbors. // Prepare for recursion. The following null checks // will be used to avoid function calls if possible double value = position[current.Axis]; double median = current.Position[current.Axis]; double u = value - median; if (u <= 0) { if (current.Left != null) { if (approximate(current.Left, position, list, maxLeaves, ref visited)) { return(true); } } if (current.Right != null && Math.Abs(u) <= list.Maximum) { if (approximate(current.Right, position, list, maxLeaves, ref visited)) { return(true); } } } else { if (current.Right != null) { approximate(current.Right, position, list, maxLeaves, ref visited); } if (current.Left != null && Math.Abs(u) <= list.Maximum) { if (approximate(current.Left, position, list, maxLeaves, ref visited)) { return(true); } } } return(false); }
/// <summary> /// Returns an enumerator that iterates through the tree. /// </summary> /// /// <returns> /// An <see cref="T:System.Collections.IEnumerator"/> object /// that can be used to iterate through the collection. /// </returns> /// public virtual IEnumerator <KDTreeNode <T> > GetEnumerator() { if (Root == null) { yield break; } var stack = new Stack <KDTreeNode <T> >(new[] { Root }); while (stack.Count != 0) { KDTreeNode <T> current = stack.Pop(); yield return(current); if (current.Left != null) { stack.Push(current.Left); } if (current.Right != null) { stack.Push(current.Right); } } }
/// <summary> /// Creates the Root node for a new <see cref="KDTree{T}"/> given /// a set of data points and their respective stored values. /// </summary> /// /// <param name="points">The data points to be inserted in the tree.</param> /// <param name="leaves">Return the number of leaves in the Root subtree.</param> /// <param name="inPlace">Whether the given <paramref name="points"/> vector /// can be ordered in place. Passing true will change the original order of /// the vector. If set to false, all operations will be performed on an extra /// copy of the vector.</param> /// /// <returns>The Root node for a new <see cref="KDTree{T}"/> /// contained the given <paramref name="points"/>.</returns> /// protected static KDTreeNode <T> CreateRoot(double[][] points, bool inPlace, out int leaves) { // Initial argument checks for creating the tree if (points == null) { throw new ArgumentNullException("points"); } if (!inPlace) { points = (double[][])points.Clone(); } leaves = 0; int dimensions = points[0].Length; // Create a comparer to compare individual array // elements at specified positions when sorting ElementComparer <double> comparer = new ElementComparer <double>(); // Call the recursive algorithm to operate on the whole array (from 0 to points.Length) KDTreeNode <T> Root = create(points, 0, dimensions, 0, points.Length, comparer, ref leaves); // Create and return the newly formed tree return(Root); }
/// <summary> /// Attempts to add a value to the collection. If the list is full /// and the value is more distant than the farthest node in the /// collection, the value will not be added. /// </summary> /// /// <param name="value">The node to be added.</param> /// <param name="distance">The node distance.</param> /// /// <returns>Returns true if the node has been added; false otherwise.</returns> /// public bool AddFarthest(KDTreeNode <T> value, double distance) { // The list does have a limit. We have to check if the list // is already full or not, to see if we can discard or keep // the point if (count < Capacity) { // The list still has room for new elements. // Just add the value at the right position. add(distance, value); return(true); // a value has been added } // The list is at its maximum capacity. Check if the value // to be added is farther than the current nearest point. if (distance > Minimum) { // Yes, it is farther. Remove the previous nearest point // and insert this new one at an appropriate position to // keep the list ordered. RemoveNearest(); add(distance, value); return(true); // a value has been added } // The value is even closer return(false); // discard it }
/// <summary> /// Creates a new <see cref="KDTree<T>"/>. /// </summary> /// /// <param name="dimension">The number of dimensions in the tree.</param> /// <param name="Root">The Root node, if already existent.</param> /// <param name="count">The number of elements in the Root node.</param> /// <param name="leaves">The number of leaves linked through the Root node.</param> /// public KDTree(int dimension, KDTreeNode <T> Root, int count, int leaves) : this(dimension) { this.Root = Root; this.count = count; this.leaves = leaves; }
private KDTreeNode <T> insert(ref KDTreeNode <T> node, double[] position, int depth) { if (node == null) { // Base case: node is null return(node = new KDTreeNode <T>() { Axis = depth % dimensions, Position = position, }); } else { KDTreeNode <T> newNode; // Recursive case: keep looking for a position to insert if (position[node.Axis] < node.Position[node.Axis]) { KDTreeNode <T> child = node.Left; newNode = insert(ref child, position, depth + 1); node.Left = child; } else { KDTreeNode <T> child = node.Right; newNode = insert(ref child, position, depth + 1); node.Right = child; } return(newNode); } }
/// <summary> /// Inserts a value into the tree at the desired position. /// </summary> /// /// <param name="position">A double-vector with the same number of elements as dimensions in the tree.</param> /// protected KDTreeNode <T> AddNode(double[] position) { count++; var root = Root; KDTreeNode <T> node = insert(ref root, position, 0); Root = root; return(node); }
/// <summary> /// Adds the specified item to the collection. /// </summary> /// /// <param name="distance">The distance from the node to the query point.</param> /// <param name="item">The item to be added.</param> /// private void add(double distance, KDTreeNode <T> item) { positions[count] = item; distances[count] = distance; count++; // Ensure it is in the right place. siftUpLast(); }
/// <summary> /// Retrieves the nearest point to a given point. /// </summary> /// /// <param name="position">The queried point.</param> /// <param name="distance">The distance from the <paramref name="position"/> /// to its nearest neighbor found in the tree.</param> /// /// <returns>A list of neighbor points, ordered by distance.</returns> /// public KDTreeNode <T> Nearest(double[] position, out double distance) { KDTreeNode <T> result = Root; distance = this.Distance(Root.Position, position); nearest(Root, position, ref result, ref distance); return(result); }
// helper: get the right rectangle of node inside parent's rect private static Hyperrectangle rightRect(Hyperrectangle hyperrect, KDTreeNode <T> node) { //var rect = hyperrect.ToRectangle(); //return (node.Axis != 0 ? // Rectangle.FromLTRB(rect.Left, (int)node.Position[1], rect.Right, rect.Bottom) : // Rectangle.FromLTRB((int)node.Position[0], rect.Top, rect.Right, rect.Bottom)).ToHyperrectangle(); Hyperrectangle copy = new Hyperrectangle((double[])hyperrect.Min.Clone(), (double[])hyperrect.Max.Clone()); copy.Min[node.Axis] = node.Position[node.Axis]; return(copy); }
/// <summary> /// Creates a new <see cref="KDTree<T>"/>. /// </summary> /// /// <param name="dimension">The number of dimensions in the tree.</param> /// <param name="Root">The Root node, if already existent.</param> /// public KDTree(int dimension, KDTreeNode <T> Root) : this(dimension) { this.Root = Root; foreach (var node in this) { count++; if (node.IsLeaf) { leaves++; } } }
private void nearest(KDTreeNode <T> current, double[] position, ref KDTreeNode <T> match, ref double minDistance) { // Compute distance from this node to the point double d = this.Distance(position, current.Position); if (d < minDistance) { minDistance = d; match = current; } // Check for leafs on the opposite sides of // the subtrees to nearest possible neighbors. // Prepare for recursion. The following null checks // will be used to avoid function calls if possible double value = position[current.Axis]; double median = current.Position[current.Axis]; double u = value - median; if (u <= 0) { if (current.Left != null) { nearest(current.Left, position, ref match, ref minDistance); } if (current.Right != null && u <= minDistance) { nearest(current.Right, position, ref match, ref minDistance); } } else { if (current.Right != null) { nearest(current.Right, position, ref match, ref minDistance); } if (current.Left != null && u <= minDistance) { nearest(current.Left, position, ref match, ref minDistance); } } }
private IList <KDTreeNode <T> > getNodesInsideRegion(KDTreeNode <T> node, Hyperrectangle region, Hyperrectangle subRegion) { var result = new List <KDTreeNode <T> >(); if (node != null && region.IntersectsWith(subRegion)) { if (region.Contains(node.Position)) { result.Add(node); } result.AddRange(getNodesInsideRegion(node.Left, region, leftRect(subRegion, node))); result.AddRange(getNodesInsideRegion(node.Right, region, rightRect(subRegion, node))); } return(result); }
/// <summary> /// Radius search. /// </summary> /// private void nearest(KDTreeNode <T> current, double[] position, double radius, ICollection <NodeDistance <KDTreeNode <T> > > list) { // Check if the distance of the point from this // node is within the desired radius, and if it // is, add to the list of nearest nodes. double d = this.Distance(position, current.Position); if (d <= radius) { list.Add(new NodeDistance <KDTreeNode <T> >(current, d)); } // Prepare for recursion. The following null checks // will be used to avoid function calls if possible double value = position[current.Axis]; double median = current.Position[current.Axis]; double u = value - median; if (u <= 0) { if (current.Left != null) { nearest(current.Left, position, radius, list); } if (current.Right != null && Math.Abs(u) <= radius) { nearest(current.Right, position, radius, list); } } else { if (current.Right != null) { nearest(current.Right, position, radius, list); } if (current.Left != null && Math.Abs(u) <= radius) { nearest(current.Left, position, radius, list); } } }
/// <summary> /// k-nearest neighbors search. /// </summary> /// private void nearest(KDTreeNode <T> current, double[] position, KDTreeNodeCollection <T> list) { // Compute distance from this node to the point double d = this.Distance(position, current.Position); list.Add(current, d); // Check for leafs on the opposite sides of // the subtrees to nearest possible neighbors. // Prepare for recursion. The following null checks // will be used to avoid function calls if possible double value = position[current.Axis]; double median = current.Position[current.Axis]; double u = value - median; if (u <= 0) { if (current.Left != null) { nearest(current.Left, position, list); } if (current.Right != null && Math.Abs(u) <= list.Maximum) { nearest(current.Right, position, list); } } else { if (current.Right != null) { nearest(current.Right, position, list); } if (current.Left != null && Math.Abs(u) <= list.Maximum) { nearest(current.Left, position, list); } } }
/// <summary> /// Indicates whether the current object is equal to another object of the same type. /// </summary> /// <param name="other">An object to compare with this object.</param> /// <returns> /// true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false. /// </returns> public bool Equals(KDTreeNode <T> other) // TODO: Try to remove IEquatable { return(this == other); }
/// <summary> /// Compares the current object with another object of the same type. /// </summary> /// <param name="other">An object to compare with this object.</param> /// <returns> /// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other" /> parameter.Zero This object is equal to <paramref name="other" />. Greater than zero This object is greater than <paramref name="other" />. /// </returns> public int CompareTo(KDTreeNode <T> other) { return(this.Position[this.Axis].CompareTo(other.Position[other.Axis])); }