/// <summary> /// Insert a new point into this leaf node. /// </summary> /// <param name="tPoint">The position which represents the data.</param> /// <param name="kValue">The value of the data.</param> public void AddPoint(double[] tPoint, T kValue) { // Find the correct leaf node. KDNode_Rednaxela <T> pCursor = this; while (!pCursor.IsLeaf) { // Extend the size of the leaf. pCursor.ExtendBounds(tPoint); pCursor.Size++; // If it is larger select the right, or lower, select the left. if (tPoint[pCursor.splitDimension] > pCursor.fSplitValue) { pCursor = pCursor.pRight; } else { pCursor = pCursor.pLeft; } } // Insert it into the leaf. pCursor.AddLeafPoint(tPoint, kValue); }
/// <summary> /// Split this leaf node by creating left and right children, then moving all the children of /// this node into the respective buckets. /// </summary> private void SplitLeafNode() { // Create the new children. pRight = new KDNode_Rednaxela <T>(dimensions, bucketCapacity); pLeft = new KDNode_Rednaxela <T>(dimensions, bucketCapacity); // Move each item in this leaf into the children. for (int i = 0; i < Size; ++i) { // Store. double[] tOldPoint = points[i]; T kOldData = data[i]; // If larger, put it in the right. if (tOldPoint[splitDimension] > fSplitValue) { pRight.AddLeafPoint(tOldPoint, kOldData); } // If smaller, put it in the left. else { pLeft.AddLeafPoint(tOldPoint, kOldData); } } // Wipe the data from this KDNode. points = null; data = null; }
private void CheckSinglePoint(KDNode_Rednaxela <T> pCursor) { // Work out the distance between this point and the search point. double fDistance = kDistanceFunction.Distance(pCursor.points[0], tSearchPoint); //// Skip if the point exceeds the threshold. //// Technically this should never happen, but be prescise. //if (fThreshold >= 0 && fDistance >= fThreshold) // continue; // Add the point if either need more points or it's closer than furthest on list so far. if (pEvaluated.Size < iPointsRemaining || fDistance <= pEvaluated.MaxKey) { for (int i = 0; i < pCursor.Size; ++i) { // If we don't need any more, replace max if (pEvaluated.Size == iPointsRemaining) { pEvaluated.ReplaceMax(fDistance, pCursor.data[i], i); } // Otherwise insert. else { pEvaluated.Insert(fDistance, pCursor.data[i], i); } } } }
/// <summary> /// Construct a new nearest neighbour iterator. /// </summary> /// <param name="pRoot">The root of the tree to begin searching from.</param> /// <param name="tSearchPoint">The point in n-dimensional space to search.</param> /// <param name="kDistance">The distance function used to evaluate the points.</param> /// <param name="iMaxPoints">The max number of points which can be returned by this iterator. Capped to max in tree.</param> /// <param name="fThreshold">Threshold to apply to the search space. Negative numbers indicate that no threshold is applied.</param> public NearestNeighbour(KDNode_Rednaxela <T> pRoot, double[] tSearchPoint, IDistanceFunction kDistance, int iMaxPoints, double fThreshold) { // Check the dimensionality of the search point. if (tSearchPoint.Length != pRoot.dimensions) { throw new Exception("Dimensionality of search point and kd-tree are not the same."); } // Store the search point. this.tSearchPoint = new double[tSearchPoint.Length]; Array.Copy(tSearchPoint, this.tSearchPoint, tSearchPoint.Length); // Store the point count, distance function and tree root. this.iPointsRemaining = Math.Min(iMaxPoints, pRoot.Size); this.fThreshold = fThreshold; this.kDistanceFunction = kDistance; this.pRoot = pRoot; this.iMaxPointsReturned = iMaxPoints; _CurrentDistance = -1; // Create an interval heap for the points we check. this.pEvaluated = new IntervalHeap <T>(); // Create a min heap for the things we need to check. this.pPending = new MinHeap <KDNode_Rednaxela <T> >(); this.pPending.Insert(0, pRoot); }
private void DescendPath(KDNode_Rednaxela <T> pCursor) { KDNode_Rednaxela <T> pNotTaken; // If the seach point is larger, select the right path. if (tSearchPoint[pCursor.splitDimension] > pCursor.fSplitValue) { pNotTaken = pCursor.pLeft; pCursor = pCursor.pRight; } else { pNotTaken = pCursor.pRight; pCursor = pCursor.pLeft; } // Calculate the shortest distance between the search point and the min and max bounds of the kd-node. double fDistance = kDistanceFunction.DistanceToRectangle(tSearchPoint, pNotTaken.minBound, pNotTaken.maxBound); //// If it is greater than the threshold, skip. //if (fThreshold >= 0 && fDistance > fThreshold) //{ // //pPending.Insert(fDistance, pNotTaken); // continue; //} // Only add the path we need more points or the node is closer than furthest point on list so far. if (pEvaluated.Size < iPointsRemaining || fDistance <= pEvaluated.MaxKey) { pPending.Insert(fDistance, pNotTaken); } }
private void CheckSpreadOutPoints(KDNode_Rednaxela <T> pCursor) { // Treat the distance of each point seperately. for (int i = 0; i < pCursor.Size; ++i) { // Compute the distance between the points. double fDistance = kDistanceFunction.Distance(pCursor.points[i], tSearchPoint); //// Skip if it exceeds the threshold. //if (fThreshold >= 0 && fDistance >= fThreshold) // continue; // Insert the point if we have more to take. if (pEvaluated.Size < iPointsRemaining) { pEvaluated.Insert(fDistance, pCursor.data[i], i); } // Otherwise replace the max. else if (fDistance < pEvaluated.MaxKey) { pEvaluated.ReplaceMax(fDistance, pCursor.data[i], i); } } }
/// <summary> /// Check for the next iterator item. /// </summary> /// <returns>True if we have one, false if not.</returns> public bool MoveNext() { // Bail if we are finished. if (iPointsRemaining == 0) { _Current = default(T); return(false); } // While we still have paths to evaluate. while (pPending.Size > 0 && (pEvaluated.Size == 0 || (pPending.MinKey < pEvaluated.DistanceMin))) { // If there are pending paths possibly closer than the nearest evaluated point, check it out KDNode_Rednaxela <T> pCursor = pPending.Min; pPending.RemoveMin(); // Descend the tree, record paths not taken while (!pCursor.IsLeaf) { // DescendPath(pCursor); KDNode_Rednaxela <T> pNotTaken; // If the seach point is larger, select the right path. if (tSearchPoint[pCursor.splitDimension] > pCursor.fSplitValue) { pNotTaken = pCursor.pLeft; pCursor = pCursor.pRight; } else { pNotTaken = pCursor.pRight; pCursor = pCursor.pLeft; } // Calculate the shortest distance between the search point and the min and max bounds of the kd-node. double fDistance = kDistanceFunction.DistanceToRectangle(tSearchPoint, pNotTaken.minBound, pNotTaken.maxBound); //// If it is greater than the threshold, skip. //if (fThreshold >= 0 && fDistance > fThreshold) //{ // //pPending.Insert(fDistance, pNotTaken); // continue; //} // Only add the path we need more points or the node is closer than furthest point on list so far. if (pEvaluated.Size < iPointsRemaining || fDistance <= pEvaluated.MaxKey) { pPending.Insert(fDistance, pNotTaken); } } // If all the points in this KD node are in one place. if (pCursor.IsSinglePoint) { CheckSinglePoint(pCursor); } // If the points in the KD node are spread out. else { CheckSpreadOutPoints(pCursor); } } // Select the point with the smallest distance. if (pEvaluated.Size == 0) { return(false); } iPointsRemaining--; _CurrentDistance = pEvaluated.DistanceMin; _Current = pEvaluated.ClosestPoint; _CurrentIndex = pEvaluated.ClosestPointIndex; pEvaluated.RemoveMin(); return(true); }