public bool TryFindNodeAt(TKey[] point, out KdTreeNode <TKey, TValue> value) { var navParent = root; int dimension = -1; do { dimension = (dimension + 1) % dimensions; if (navParent == null) { value = default(KdTreeNode <TKey, TValue>); return(false); } else if (isNode(dimension)) { var parent = (KdTreeNode <TKey, TValue>)navParent; if (typeMath.AreEqual(point, parent.Points)) { value = parent; return(true); } } // Keep searching int compare = typeMath.Compare(point[dimension], navParent.Point); navParent = navParent[compare]; }while (true); }
private KdTreeNode <TValue> Add(TValue value, KdTreeNode <TValue> node, int depth) { if (node == null) { node = new KdTreeNode <TValue>(value); } else { // Check if node should be added to left or right sub-tree of current node. var dimension = depth % this.dimensionality; var comparison = fieldComparer.Compare(this.locationGetter(value)[dimension], this.locationGetter(node.Value)[dimension]); if (comparison <= 0) { node.LeftChild = Add(value, node.LeftChild, depth + 1); } else { node.RightChild = Add(value, node.RightChild, depth + 1); } } return(node); }
private static KdTreeNode <TValue> Construct(KdTree <TValue, TField> tree, TValue[] elements, int startIndex, int endIndex, int depth, ValueLocationComparer valueLocationComparer) { var length = endIndex - startIndex + 1; if (length == 0) { return(null); } // Sort array of elements by component of chosen dimension, in ascending magnitude. valueLocationComparer.Dimension = depth % tree.dimensionality; Array.Sort(elements, startIndex, length, valueLocationComparer); // Select median element as pivot. var medianIndex = startIndex + length / 2; var medianElement = elements[medianIndex]; // Create node and construct sub-trees around pivot element. var node = new KdTreeNode <TValue>(medianElement); node.LeftChild = Construct(tree, elements, startIndex, medianIndex - 1, depth + 1, valueLocationComparer); node.RightChild = Construct(tree, elements, medianIndex + 1, endIndex, depth + 1, valueLocationComparer); return(node); }
internal KdTreeNode <TKey, TValue> this[int compare] { get { if (compare <= 0) { return(LeftChild); } else { return(RightChild); } } set { if (compare <= 0) { LeftChild = value; } else { RightChild = value; } } }
public bool Add(TKey[] point, TValue value) { var nodeToAdd = new KdTreeNode <TKey, TValue>(point, value); if (root == null) { root = new KdTreeNode <TKey, TValue>(point, value); } else { int dimension = -1; KdTreeNode <TKey, TValue> parent = root; do { // Increment the dimension we're searching in dimension = (dimension + 1) % dimensions; // Does the node we're adding have the same hyperpoint as this node? if (typeMath.AreEqual(point, parent.Point)) { switch (AddDuplicateBehavior) { case AddDuplicateBehavior.Skip: return(false); case AddDuplicateBehavior.Error: throw new DuplicateNodeError(); case AddDuplicateBehavior.Update: parent.Value = value; break; default: // Should never happen throw new Exception("Unexpected AddDuplicateBehavior"); } } // Which side does this node sit under in relation to it's parent at this level? int compare = typeMath.Compare(point[dimension], parent.Point[dimension]); if (parent[compare] == null) { parent[compare] = nodeToAdd; break; } else { parent = parent[compare]; } }while (true); } Count++; return(true); }
private void FindNearestNNeighbors(Vector <TField> location, KdTreeNode <TValue> node, ref TValue maxBestValue, ref TField maxBestDistance, int numNeighbors, C5.IPriorityQueue <TValue> valuesList, int depth) { if (node == null) { return; } var dimension = depth % this.dimensionality; var nodeLocation = this.locationGetter(node.Value); var distance = (nodeLocation - location).Norm(this.dimensionality); // Check if current node is better than maximum best node, and replace maximum node in list with it. // Current node cannot be same as search location. if (!fieldArithmetic.AlmostEqual(distance, fieldArithmetic.Zero) && fieldComparer.Compare(distance, maxBestDistance) < 0) { TValue maxValue; if (valuesList.Count == numNeighbors) { maxValue = valuesList.DeleteMax(); } valuesList.Add(node.Value); if (valuesList.Count == numNeighbors) { maxBestValue = valuesList.FindMax(); maxBestDistance = (this.locationGetter(maxBestValue) - location).Norm(this.dimensionality); } } // Check for best node in sub-tree of near child. var nearChildNode = fieldComparer.Compare(location[dimension], nodeLocation[dimension]) < 0 ? node.LeftChild : node.RightChild; if (nearChildNode != null) { FindNearestNNeighbors(location, nearChildNode, ref maxBestValue, ref maxBestDistance, numNeighbors, valuesList, depth + 1); } // Check whether splitting hyperplane given by current node intersects with hypersphere of current smallest // distance around given location. if (fieldComparer.Compare(maxBestDistance, fieldArithmetic.Abs(fieldArithmetic.Subtract( nodeLocation[dimension], location[dimension]))) > 0) { // Check for best node in sub-tree of far child. var farChildValue = nearChildNode == node.LeftChild ? node.RightChild : node.LeftChild; if (farChildValue != null) { FindNearestNNeighbors(location, farChildValue, ref maxBestValue, ref maxBestDistance, numNeighbors, valuesList, depth + 1); } } }
private TValue FindNearestNeighbor(Vector <TField> location, KdTreeNode <TValue> node, TValue bestValue, TField bestDistance, int depth) { if (node == null) { return(bestValue); } var dimension = depth % this.dimensionality; var nodeLocation = this.locationGetter(node.Value); var distance = (nodeLocation - location).Norm(this.dimensionality); // Check if current node is better than best node. // Current node cannot be same as search location. if (!fieldArithmetic.AlmostEqual(distance, fieldArithmetic.Zero) && fieldComparer.Compare(distance, bestDistance) < 0) { bestValue = node.Value; bestDistance = distance; } // Check for best node in sub-tree of near child. var nearChildNode = fieldComparer.Compare(location[dimension], nodeLocation[dimension]) < 0 ? node.LeftChild : node.RightChild; if (nearChildNode != null) { var nearBestValue = FindNearestNeighbor(location, nearChildNode, bestValue, bestDistance, depth + 1); var nearBestLocation = this.locationGetter(nearBestValue); var nearBestDistance = (nearBestLocation - location).Norm(this.dimensionality); bestValue = nearBestValue; bestDistance = nearBestDistance; } // Check whether splitting hyperplane given by current node intersects with hypersphere of current smallest // distance around given location. if (fieldComparer.Compare(bestDistance, fieldArithmetic.Abs(fieldArithmetic.Subtract( nodeLocation[dimension], location[dimension]))) > 0) { // Check for best node in sub-tree of far child. var farChildValue = nearChildNode == node.LeftChild ? node.RightChild : node.LeftChild; if (farChildValue != null) { var farBestValue = FindNearestNeighbor(location, farChildValue, bestValue, bestDistance, depth + 1); var farBestLocation = this.locationGetter(farBestValue); var farBestDistance = (farBestLocation - location).Norm(this.dimensionality); bestValue = farBestValue; bestDistance = farBestDistance; } } return(bestValue); }
private void RemoveChildNodes(KdTreeNode <TKey, TValue> node) { for (var side = -1; side <= 1; side += 2) { if (node[side] != null) { RemoveChildNodes(node[side]); node[side] = null; } } }
public KdTreeNode <TKey, TValue> Remove(KdTreeNode <TKey, TValue> removedNode)//may be not working correct { // Is tree empty? if (root == null) { return(null); } KdTreeNavNode <TKey> navNode = root; KdTreeNavNode <TKey> parentNode = root; var node = ConvertNavNode(root); if (typeMath.AreEqual(removedNode.Points, node.Points)) { root = null; Count--; ReaddChildNodes(navNode); return(node); } int dimension = -1; int compare = 0; do { dimension = (dimension + 1) % dimensions; if (isNode(dimension)) { node = ConvertNavNode(navNode);//todo: may be optimized without using cast if (typeMath.AreEqual(removedNode.Points, node.Points) && removedNode.Value.Equals(node.Value)) { var nodeToRemove = node; parentNode[compare] = null; Count--; ReaddChildNodes(nodeToRemove); return(nodeToRemove); } } compare = typeMath.Compare(removedNode.Points[dimension], navNode.Point); if (navNode[compare] == null) { // Can't find node return(null); } parentNode = navNode; navNode = navNode[compare]; }while (navNode != null); return(null); }
private KdTreeNode <TValue> Find(TValue value, KdTreeNode <TValue> node) { if (node == null) { return(null); } if (node.Value.Equals(value)) { return(node); } return(Find(value, node.LeftChild) ?? Find(value, node.RightChild)); }
private void ReaddChildNodes(KdTreeNode <TKey, TValue> removedNode) { if (removedNode.IsLeaf) { return; } // The folllowing code might seem a little redundant but we're using // 2 queues so we can add the child nodes back in, in (more or less) // the same order they were added in the first place var nodesToReadd = new Queue <KdTreeNode <TKey, TValue> >(); var nodesToReaddQueue = new Queue <KdTreeNode <TKey, TValue> >(); if (removedNode.LeftChild != null) { nodesToReaddQueue.Enqueue(removedNode.LeftChild); } if (removedNode.RightChild != null) { nodesToReaddQueue.Enqueue(removedNode.RightChild); } while (nodesToReaddQueue.Count > 0) { var nodeToReadd = nodesToReaddQueue.Dequeue(); nodesToReadd.Enqueue(nodeToReadd); for (int side = -1; side <= 1; side += 2) { if (nodeToReadd[side] != null) { nodesToReaddQueue.Enqueue(nodeToReadd[side]); nodeToReadd[side] = null; } } } while (nodesToReadd.Count > 0) { var nodeToReadd = nodesToReadd.Dequeue(); Count--; Add(nodeToReadd.Point, nodeToReadd.Value); } }
public void RemoveAt(TKey[] point) { // Is tree empty? if (root == null) { return; } KdTreeNode <TKey, TValue> node; if (typeMath.AreEqual(point, root.Point)) { node = root; root = null; Count--; ReaddChildNodes(node); return; } node = root; int dimension = -1; do { dimension = (dimension + 1) % dimensions; int compare = typeMath.Compare(point[dimension], node.Point[dimension]); if (node[compare] == null) { // Can't find node return; } if (typeMath.AreEqual(point, node[compare].Point)) { var nodeToRemove = node[compare]; node[compare] = null; Count--; ReaddChildNodes(nodeToRemove); } else { node = node[compare]; } }while (node != null); }
/// <summary> /// Initializes a new instance of the <see cref="KdTree{TValue, TField}"/> class with the specified root node. /// </summary> /// <param name="dimensionality">The dimensionality of the kd-space.</param> /// <param name="root">The root node of the tree.</param> /// <param name="locationGetter">A function that returns the location of a given element in kd-space.</param> public KdTree(int dimensionality, KdTreeNode <TValue> root, Func <TValue, Vector <TField> > locationGetter = null) : this() { if (root != null) { throw new ArgumentNullException("root"); } this.dimensionality = dimensionality; this.root = root; if (locationGetter != null) { this.locationGetter = locationGetter; } }
private void AddNodesToList(KdTreeNode <TKey, TValue> node, List <KdTreeNode <TKey, TValue> > nodes) { if (node == null) { return; } nodes.Add(node); for (var side = -1; side <= 1; side += 2) { if (node[side] != null) { AddNodesToList(node[side], nodes); node[side] = null; } } }
private void FindInRange(Vector <TField> location, KdTreeNode <TValue> node, TField range, IList <TValue> valuesList, int depth) { if (node == null) { return; } var dimension = depth % this.dimensionality; var nodeLocation = this.locationGetter(node.Value); var distance = (nodeLocation - location).Norm(this.dimensionality); // Add current node to list if it lies within given range. // Current node cannot be same as search location. if (!fieldArithmetic.AlmostEqual(distance, fieldArithmetic.Zero) && fieldComparer.Compare(distance, range) < 0) { valuesList.Add(node.Value); } // Check for nodes in sub-tree of near child. var nearChildNode = fieldComparer.Compare(location[dimension], nodeLocation[dimension]) < 0 ? node.LeftChild : node.RightChild; if (nearChildNode != null) { FindInRange(location, nearChildNode, range, valuesList, depth + 1); } // Check whether splitting hyperplane given by current node intersects with hypersphere of current smallest // distance around given location. if (fieldComparer.Compare(range, fieldArithmetic.Abs(fieldArithmetic.Subtract( nodeLocation[dimension], location[dimension]))) > 0) { // Check for nodes in sub-tree of far child. var farChildNode = nearChildNode == node.LeftChild ? node.RightChild : node.LeftChild; if (farChildNode != null) { FindInRange(location, farChildNode, range, valuesList, depth + 1); } } }
public bool TryFindNode(TValue value, out KdTreeNode <TKey, TValue> point) { if (root == null) { point = null; return(false); } // First-in, First-out list of nodes to search var nodesToSearch = new Queue <KdTreeNavNode <TKey> >(); nodesToSearch.Enqueue(root); while (nodesToSearch.Count > 0) { var nodeToSearch = nodesToSearch.Dequeue(); if (isNode(nodeToSearch)) { var node = (KdTreeNode <TKey, TValue>)nodeToSearch; if (node.Value.Equals(value)) { point = node; return(true); } } else { for (int side = -1; side <= 1; side += 2) { var childNode = nodeToSearch[side]; if (childNode != null) { nodesToSearch.Enqueue(childNode); } } } } point = null; return(false); }
private KdTreeNode <TValue> Remove(TValue value, KdTreeNode <TValue> node, int depth) { if (node == null) { return(null); } var dimension = depth % this.dimensionality; var valueLocation = this.locationGetter(value); var nodeLocation = this.locationGetter(node.Value); var comparison = fieldComparer.Compare(valueLocation[dimension], nodeLocation[dimension]); // Check if node to remove is in left sub-tree, right sub-tree, or has been found. if (comparison < 0) { node.LeftChild = Remove(value, node.LeftChild, depth + 1); } else if (comparison > 0) { node.RightChild = Remove(value, node.RightChild, depth + 1); } else { if (node.RightChild != null) { node.Value = FindMinimum(node.RightChild, dimension, depth + 1); node.RightChild = Remove(node.Value, node.RightChild, depth + 1); } else if (node.LeftChild != null) { node.Value = FindMinimum(node.LeftChild, dimension, depth + 1); node.RightChild = Remove(node.Value, node.LeftChild, depth + 1); node.LeftChild = null; } else { node = null; } } return(node); }
public bool Add(TKey[] point, TValue value) { var nodeToAdd = new KdTreeNode <TKey, TValue>(point, value); if (root == null) { root = new KdTreeNode <TKey, TValue>(point, value); } else { int dimension = -1; KdTreeNode <TKey, TValue> parent = root; do { // Increment the dimension we're searching in dimension = (dimension + 1) % dimensions; // Does the node we're adding have the same hyperpoint as this node? if (typeMath.AreEqual(point, parent.Point)) { return(false); } // Which side does this node sit under in relation to it's parent at this level? int compare = typeMath.Compare(point[dimension], parent.Point[dimension]); if (parent[compare] == null) { parent[compare] = nodeToAdd; break; } else { parent = parent[compare]; } }while (true); } Count++; return(true); }
private KdTreeNode <TKey, TValue>[] RadialSearch(TKey[] center, TKey radius, NearestNeighbourList <KdTreeNode <TKey, TValue>, TKey> nearestNeighbours) { AddNearestNeighbours( root, center, HyperRect <TKey> .Infinite(dimensions, typeMath), 0, nearestNeighbours, typeMath.Multiply(radius, radius)); var count = nearestNeighbours.Count; var neighbourArray = new KdTreeNode <TKey, TValue> [count]; for (var index = 0; index < count; index++) { neighbourArray[count - index - 1] = nearestNeighbours.RemoveFurtherest(); } return(neighbourArray); }
private void AddNodeToStringBuilder(KdTreeNode <TKey, TValue> node, StringBuilder sb, int depth) { sb.AppendLine(node.ToString()); for (var side = -1; side <= 1; side += 2) { for (var index = 0; index <= depth; index++) { sb.Append("\t"); } sb.Append(side == -1 ? "L " : "R "); if (node[side] == null) { sb.AppendLine(""); } else { AddNodeToStringBuilder(node[side], sb, depth + 1); } } }
public KdTreeNode <TKey, TValue>[] GetNearestNeighbours(TKey[] point, int count) { if (count > Count) { count = Count; } if (count < 0) { throw new ArgumentException("Number of neighbors cannot be negative"); } if (count == 0) { return(new KdTreeNode <TKey, TValue> [0]); } var neighbours = new KdTreeNode <TKey, TValue> [count]; var nearestNeighbours = new NearestNeighbourList <KdTreeNode <TKey, TValue>, TKey>(count, typeMath); var rect = HyperRect <TKey> .Infinite(dimensions, typeMath); AddNearestNeighbours(root, point, rect, 0, nearestNeighbours, typeMath.MaxValue); count = nearestNeighbours.Count; var neighbourArray = new KdTreeNode <TKey, TValue> [count]; for (var index = 0; index < count; index++) { neighbourArray[count - index - 1] = nearestNeighbours.RemoveFurtherest(); } return(neighbourArray); }
/* * 1. Search for the target * * 1.1 Start by splitting the specified hyper rect * on the specified node's point along the current * dimension so that we end up with 2 sub hyper rects * (current dimension = depth % dimensions) * * 1.2 Check what sub rectangle the the target point resides in * under the current dimension * * 1.3 Set that rect to the nearer rect and also the corresponding * child node to the nearest rect and node and the other rect * and child node to the further rect and child node (for use later) * * 1.4 Travel into the nearer rect and node by calling function * recursively with nearer rect and node and incrementing * the depth * * 2. Add leaf to list of nearest neighbours * * 3. Walk back up tree and at each level: * * 3.1 Add node to nearest neighbours if * we haven't filled our nearest neighbour * list yet or if it has a distance to target less * than any of the distances in our current nearest * neighbours. * * 3.2 If there is any point in the further rectangle that is closer to * the target than our furtherest nearest neighbour then travel into * that rect and node * * That's it, when it finally finishes traversing the branches * it needs to we'll have our list! */ private void AddNearestNeighbours( KdTreeNode <TKey, TValue> node, TKey[] target, HyperRect <TKey> rect, int depth, NearestNeighbourList <KdTreeNode <TKey, TValue>, TKey> nearestNeighbours, TKey maxSearchRadiusSquared) { if (node == null) { return; } // Work out the current dimension int dimension = depth % dimensions; // Split our hyper-rect into 2 sub rects along the current // node's point on the current dimension var leftRect = rect.Clone(); leftRect.MaxPoint[dimension] = node.Point[dimension]; var rightRect = rect.Clone(); rightRect.MinPoint[dimension] = node.Point[dimension]; // Which side does the target reside in? int compare = typeMath.Compare(target[dimension], node.Point[dimension]); var nearerRect = compare <= 0 ? leftRect : rightRect; var furtherRect = compare <= 0 ? rightRect : leftRect; var nearerNode = compare <= 0 ? node.LeftChild : node.RightChild; var furtherNode = compare <= 0 ? node.RightChild : node.LeftChild; // Let's walk down into the nearer branch if (nearerNode != null) { AddNearestNeighbours( nearerNode, target, nearerRect, depth + 1, nearestNeighbours, maxSearchRadiusSquared); } TKey distanceSquaredToTarget; // Walk down into the further branch but only if our capacity hasn't been reached // OR if there's a region in the further rect that's closer to the target than our // current furtherest nearest neighbour TKey[] closestPointInFurtherRect = furtherRect.GetClosestPoint(target, typeMath); distanceSquaredToTarget = typeMath.DistanceSquaredBetweenPoints(closestPointInFurtherRect, target); if (typeMath.Compare(distanceSquaredToTarget, maxSearchRadiusSquared) <= 0) { if (nearestNeighbours.IsCapacityReached) { if (typeMath.Compare(distanceSquaredToTarget, nearestNeighbours.GetFurtherestDistance()) < 0) { AddNearestNeighbours( furtherNode, target, furtherRect, depth + 1, nearestNeighbours, maxSearchRadiusSquared); } } else { AddNearestNeighbours( furtherNode, target, furtherRect, depth + 1, nearestNeighbours, maxSearchRadiusSquared); } } // Try to add the current node to our nearest neighbours list distanceSquaredToTarget = typeMath.DistanceSquaredBetweenPoints(node.Point, target); if (typeMath.Compare(distanceSquaredToTarget, maxSearchRadiusSquared) <= 0) { nearestNeighbours.Add(node, distanceSquaredToTarget); } }
private TValue FindMinimum(KdTreeNode <TValue> node, int splittingDimension, int depth) { if (node == null) { return(default(TValue)); } var dimension = depth % this.dimensionality; if (dimension == splittingDimension) { // Find minimum value in left sub-tree. if (node.LeftChild == null) { return(node.Value); } else { return(FindMinimum(node.LeftChild, splittingDimension, depth + 1)); } } else { /* * <ADDED> was added after I received a friendly email with possible bug in the code. * * Quote * * "I came across a strange bug you may not have discovered. * In your Findmin method, if the TValue is a value type not a reference type all hell breaks loose when removing nodes. * Also, I noted that you cannot actually remove all the nodes from your data structure * (the root always remains) but I wouldn't call that a bug." * */ // Find node with minimum value in sub-tree of current node. var nodeLocation = this.locationGetter(node.Value); var leftMinValue = FindMinimum(node.LeftChild, splittingDimension, depth + 1); var rightMinValue = FindMinimum(node.RightChild, splittingDimension, depth + 1); var leftMinValueBetter = leftMinValue != null && fieldComparer.Compare(this.locationGetter(leftMinValue)[splittingDimension], nodeLocation[splittingDimension]) < 0; // <ADDED> if (leftMinValue.Equals(default(TValue))) { leftMinValueBetter = false; } // </ADDED> var rightMinValueBetter = rightMinValue != null && fieldComparer.Compare(this.locationGetter(rightMinValue)[splittingDimension], nodeLocation[splittingDimension]) < 0; // <ADDED> if (rightMinValue.Equals(default(TValue))) { rightMinValueBetter = false; } // </ADDED> if (leftMinValueBetter && !rightMinValueBetter) { return(leftMinValue); } else if (rightMinValueBetter) { return(rightMinValue); } else { return(node.Value); } } }
public int Compare(KdTreeNode <TKey, TValue> x, KdTreeNode <TKey, TValue> y) { return(typeComparer.Compare(x.Points[byDimension], y.Points[byDimension])); }
/// <summary> /// Add new node to tree /// </summary> /// <param name="point">Key</param> /// <param name="value">Value</param> /// <returns>Return <code>true</code> if node was added</returns> public bool Add(TKey[] point, TValue value) { var nodeToAdd = new KdTreeNode <TKey, TValue>(point, value); if (root == null) { root = nodeToAdd; } else { int dimension = -1; var navParent = root; do { dimension = (dimension + 1) % dimensions; if (isNode(dimension)) { var parent = (KdTreeNode <TKey, TValue>)navParent; if (typeMath.AreEqual(point, parent.Points)) { switch (AddDuplicateBehavior) { case AddDuplicateBehavior.Skip: return(false); case AddDuplicateBehavior.Continue: break; case AddDuplicateBehavior.Error: throw new DuplicateNodeError(); case AddDuplicateBehavior.Update: if (OnNodeUpdate != null) { if (OnNodeUpdate.Invoke(this, parent, nodeToAdd)) { return(true); } } else { parent.Value = value; return(true); } break; default: // Should never happen throw new Exception("Unexpected AddDuplicateBehavior"); } } } // Which side does this node sit under in relation to it's parent at this level? int compare = typeMath.Compare(point[dimension], navParent.Point); if (navParent[compare] == null) { if (isNavNode(dimension + 1)) { navParent[compare] = new KdTreeNavNode <TKey>(point[(dimension + 1) % dimensions]); navParent = navParent[compare]; } else { navParent[compare] = nodeToAdd; break; } } else { navParent = navParent[compare]; } } while (true); } Count++; return(true); }