439 lines
15 KiB
Text
439 lines
15 KiB
Text
//----------------------------------------------------------------------
|
|
// File: kd_util.cpp
|
|
// Programmer: Sunil Arya and David Mount
|
|
// Description: Common utilities for kd-trees
|
|
// Last modified: 01/04/05 (Version 1.0)
|
|
//----------------------------------------------------------------------
|
|
// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
|
|
// David Mount. All Rights Reserved.
|
|
//
|
|
// This software and related documentation is part of the Approximate
|
|
// Nearest Neighbor Library (ANN). This software is provided under
|
|
// the provisions of the Lesser GNU Public License (LGPL). See the
|
|
// file ../ReadMe.txt for further information.
|
|
//
|
|
// The University of Maryland (U.M.) and the authors make no
|
|
// representations about the suitability or fitness of this software for
|
|
// any purpose. It is provided "as is" without express or implied
|
|
// warranty.
|
|
//----------------------------------------------------------------------
|
|
// History:
|
|
// Revision 0.1 03/04/98
|
|
// Initial release
|
|
//----------------------------------------------------------------------
|
|
|
|
#include "kd_util.h" // kd-utility declarations
|
|
|
|
#include <ANN/ANNperf.h> // performance evaluation
|
|
|
|
//----------------------------------------------------------------------
|
|
// The following routines are utility functions for manipulating
|
|
// points sets, used in determining splitting planes for kd-tree
|
|
// construction.
|
|
//----------------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------------
|
|
// NOTE: Virtually all point indexing is done through an index (i.e.
|
|
// permutation) array pidx. Consequently, a reference to the d-th
|
|
// coordinate of the i-th point is pa[pidx[i]][d]. The macro PA(i,d)
|
|
// is a shorthand for this.
|
|
//----------------------------------------------------------------------
|
|
// standard 2-d indirect indexing
|
|
#define PA(i,d) (pa[pidx[(i)]][(d)])
|
|
// accessing a single point
|
|
#define PP(i) (pa[pidx[(i)]])
|
|
|
|
//----------------------------------------------------------------------
|
|
// annAspectRatio
|
|
// Compute the aspect ratio (ratio of longest to shortest side)
|
|
// of a rectangle.
|
|
//----------------------------------------------------------------------
|
|
|
|
double annAspectRatio(
|
|
int dim, // dimension
|
|
const ANNorthRect &bnd_box) // bounding cube
|
|
{
|
|
ANNcoord length = bnd_box.hi[0] - bnd_box.lo[0];
|
|
ANNcoord min_length = length; // min side length
|
|
ANNcoord max_length = length; // max side length
|
|
for (int d = 0; d < dim; d++) {
|
|
length = bnd_box.hi[d] - bnd_box.lo[d];
|
|
if (length < min_length) min_length = length;
|
|
if (length > max_length) max_length = length;
|
|
}
|
|
return max_length/min_length;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// annEnclRect, annEnclCube
|
|
// These utilities compute the smallest rectangle and cube enclosing
|
|
// a set of points, respectively.
|
|
//----------------------------------------------------------------------
|
|
|
|
void annEnclRect(
|
|
ANNpointArray pa, // point array
|
|
ANNidxArray pidx, // point indices
|
|
int n, // number of points
|
|
int dim, // dimension
|
|
ANNorthRect &bnds) // bounding cube (returned)
|
|
{
|
|
for (int d = 0; d < dim; d++) { // find smallest enclosing rectangle
|
|
ANNcoord lo_bnd = PA(0,d); // lower bound on dimension d
|
|
ANNcoord hi_bnd = PA(0,d); // upper bound on dimension d
|
|
for (int i = 0; i < n; i++) {
|
|
if (PA(i,d) < lo_bnd) lo_bnd = PA(i,d);
|
|
else if (PA(i,d) > hi_bnd) hi_bnd = PA(i,d);
|
|
}
|
|
bnds.lo[d] = lo_bnd;
|
|
bnds.hi[d] = hi_bnd;
|
|
}
|
|
}
|
|
|
|
void annEnclCube( // compute smallest enclosing cube
|
|
ANNpointArray pa, // point array
|
|
ANNidxArray pidx, // point indices
|
|
int n, // number of points
|
|
int dim, // dimension
|
|
ANNorthRect &bnds) // bounding cube (returned)
|
|
{
|
|
int d;
|
|
// compute smallest enclosing rect
|
|
annEnclRect(pa, pidx, n, dim, bnds);
|
|
|
|
ANNcoord max_len = 0; // max length of any side
|
|
for (d = 0; d < dim; d++) { // determine max side length
|
|
ANNcoord len = bnds.hi[d] - bnds.lo[d];
|
|
if (len > max_len) { // update max_len if longest
|
|
max_len = len;
|
|
}
|
|
}
|
|
for (d = 0; d < dim; d++) { // grow sides to match max
|
|
ANNcoord len = bnds.hi[d] - bnds.lo[d];
|
|
ANNcoord half_diff = (max_len - len) / 2;
|
|
bnds.lo[d] -= half_diff;
|
|
bnds.hi[d] += half_diff;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// annBoxDistance - utility routine which computes distance from point to
|
|
// box (Note: most distances to boxes are computed using incremental
|
|
// distance updates, not this function.)
|
|
//----------------------------------------------------------------------
|
|
|
|
ANNdist annBoxDistance( // compute distance from point to box
|
|
const ANNpoint q, // the point
|
|
const ANNpoint lo, // low point of box
|
|
const ANNpoint hi, // high point of box
|
|
int dim) // dimension of space
|
|
{
|
|
register ANNdist dist = 0.0; // sum of squared distances
|
|
register ANNdist t;
|
|
|
|
for (register int d = 0; d < dim; d++) {
|
|
if (q[d] < lo[d]) { // q is left of box
|
|
t = ANNdist(lo[d]) - ANNdist(q[d]);
|
|
dist = ANN_SUM(dist, ANN_POW(t));
|
|
}
|
|
else if (q[d] > hi[d]) { // q is right of box
|
|
t = ANNdist(q[d]) - ANNdist(hi[d]);
|
|
dist = ANN_SUM(dist, ANN_POW(t));
|
|
}
|
|
}
|
|
ANN_FLOP(4*dim) // increment floating op count
|
|
|
|
return dist;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// annSpread - find spread along given dimension
|
|
// annMinMax - find min and max coordinates along given dimension
|
|
// annMaxSpread - find dimension of max spread
|
|
//----------------------------------------------------------------------
|
|
|
|
ANNcoord annSpread( // compute point spread along dimension
|
|
ANNpointArray pa, // point array
|
|
ANNidxArray pidx, // point indices
|
|
int n, // number of points
|
|
int d) // dimension to check
|
|
{
|
|
ANNcoord min = PA(0,d); // compute max and min coords
|
|
ANNcoord max = PA(0,d);
|
|
for (int i = 1; i < n; i++) {
|
|
ANNcoord c = PA(i,d);
|
|
if (c < min) min = c;
|
|
else if (c > max) max = c;
|
|
}
|
|
return (max - min); // total spread is difference
|
|
}
|
|
|
|
void annMinMax( // compute min and max coordinates along dim
|
|
ANNpointArray pa, // point array
|
|
ANNidxArray pidx, // point indices
|
|
int n, // number of points
|
|
int d, // dimension to check
|
|
ANNcoord &min, // minimum value (returned)
|
|
ANNcoord &max) // maximum value (returned)
|
|
{
|
|
min = PA(0,d); // compute max and min coords
|
|
max = PA(0,d);
|
|
for (int i = 1; i < n; i++) {
|
|
ANNcoord c = PA(i,d);
|
|
if (c < min) min = c;
|
|
else if (c > max) max = c;
|
|
}
|
|
}
|
|
|
|
int annMaxSpread( // compute dimension of max spread
|
|
ANNpointArray pa, // point array
|
|
ANNidxArray pidx, // point indices
|
|
int n, // number of points
|
|
int dim) // dimension of space
|
|
{
|
|
int max_dim = 0; // dimension of max spread
|
|
ANNcoord max_spr = 0; // amount of max spread
|
|
|
|
if (n == 0) return max_dim; // no points, who cares?
|
|
|
|
for (int d = 0; d < dim; d++) { // compute spread along each dim
|
|
ANNcoord spr = annSpread(pa, pidx, n, d);
|
|
if (spr > max_spr) { // bigger than current max
|
|
max_spr = spr;
|
|
max_dim = d;
|
|
}
|
|
}
|
|
return max_dim;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// annMedianSplit - split point array about its median
|
|
// Splits a subarray of points pa[0..n] about an element of given
|
|
// rank (median: n_lo = n/2) with respect to dimension d. It places
|
|
// the element of rank n_lo-1 correctly (because our splitting rule
|
|
// takes the mean of these two). On exit, the array is permuted so
|
|
// that:
|
|
//
|
|
// pa[0..n_lo-2][d] <= pa[n_lo-1][d] <= pa[n_lo][d] <= pa[n_lo+1..n-1][d].
|
|
//
|
|
// The mean of pa[n_lo-1][d] and pa[n_lo][d] is returned as the
|
|
// splitting value.
|
|
//
|
|
// All indexing is done indirectly through the index array pidx.
|
|
//
|
|
// This function uses the well known selection algorithm due to
|
|
// C.A.R. Hoare.
|
|
//----------------------------------------------------------------------
|
|
|
|
// swap two points in pa array
|
|
#define PASWAP(a,b) { int tmp = pidx[a]; pidx[a] = pidx[b]; pidx[b] = tmp; }
|
|
|
|
void annMedianSplit(
|
|
ANNpointArray pa, // points to split
|
|
ANNidxArray pidx, // point indices
|
|
int n, // number of points
|
|
int d, // dimension along which to split
|
|
ANNcoord &cv, // cutting value
|
|
int n_lo) // split into n_lo and n-n_lo
|
|
{
|
|
int l = 0; // left end of current subarray
|
|
int r = n-1; // right end of current subarray
|
|
while (l < r) {
|
|
register int i = (r+l)/2; // select middle as pivot
|
|
register int k;
|
|
|
|
if (PA(i,d) > PA(r,d)) // make sure last > pivot
|
|
PASWAP(i,r)
|
|
PASWAP(l,i); // move pivot to first position
|
|
|
|
ANNcoord c = PA(l,d); // pivot value
|
|
i = l;
|
|
k = r;
|
|
for(;;) { // pivot about c
|
|
while (PA(++i,d) < c) ;
|
|
while (PA(--k,d) > c) ;
|
|
if (i < k) PASWAP(i,k) else break;
|
|
}
|
|
PASWAP(l,k); // pivot winds up in location k
|
|
|
|
if (k > n_lo) r = k-1; // recurse on proper subarray
|
|
else if (k < n_lo) l = k+1;
|
|
else break; // got the median exactly
|
|
}
|
|
if (n_lo > 0) { // search for next smaller item
|
|
ANNcoord c = PA(0,d); // candidate for max
|
|
int k = 0; // candidate's index
|
|
for (int i = 1; i < n_lo; i++) {
|
|
if (PA(i,d) > c) {
|
|
c = PA(i,d);
|
|
k = i;
|
|
}
|
|
}
|
|
PASWAP(n_lo-1, k); // max among pa[0..n_lo-1] to pa[n_lo-1]
|
|
}
|
|
// cut value is midpoint value
|
|
cv = (PA(n_lo-1,d) + PA(n_lo,d))/2.0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// annPlaneSplit - split point array about a cutting plane
|
|
// Split the points in an array about a given plane along a
|
|
// given cutting dimension. On exit, br1 and br2 are set so
|
|
// that:
|
|
//
|
|
// pa[ 0 ..br1-1] < cv
|
|
// pa[br1..br2-1] == cv
|
|
// pa[br2.. n -1] > cv
|
|
//
|
|
// All indexing is done indirectly through the index array pidx.
|
|
//
|
|
//----------------------------------------------------------------------
|
|
|
|
void annPlaneSplit( // split points by a plane
|
|
ANNpointArray pa, // points to split
|
|
ANNidxArray pidx, // point indices
|
|
int n, // number of points
|
|
int d, // dimension along which to split
|
|
ANNcoord cv, // cutting value
|
|
int &br1, // first break (values < cv)
|
|
int &br2) // second break (values == cv)
|
|
{
|
|
int l = 0;
|
|
int r = n-1;
|
|
for(;;) { // partition pa[0..n-1] about cv
|
|
while (l < n && PA(l,d) < cv) l++;
|
|
while (r >= 0 && PA(r,d) >= cv) r--;
|
|
if (l > r) break;
|
|
PASWAP(l,r);
|
|
l++; r--;
|
|
}
|
|
br1 = l; // now: pa[0..br1-1] < cv <= pa[br1..n-1]
|
|
r = n-1;
|
|
for(;;) { // partition pa[br1..n-1] about cv
|
|
while (l < n && PA(l,d) <= cv) l++;
|
|
while (r >= br1 && PA(r,d) > cv) r--;
|
|
if (l > r) break;
|
|
PASWAP(l,r);
|
|
l++; r--;
|
|
}
|
|
br2 = l; // now: pa[br1..br2-1] == cv < pa[br2..n-1]
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// annBoxSplit - split point array about a orthogonal rectangle
|
|
// Split the points in an array about a given orthogonal
|
|
// rectangle. On exit, n_in is set to the number of points
|
|
// that are inside (or on the boundary of) the rectangle.
|
|
//
|
|
// All indexing is done indirectly through the index array pidx.
|
|
//
|
|
//----------------------------------------------------------------------
|
|
|
|
void annBoxSplit( // split points by a box
|
|
ANNpointArray pa, // points to split
|
|
ANNidxArray pidx, // point indices
|
|
int n, // number of points
|
|
int dim, // dimension of space
|
|
ANNorthRect &box, // the box
|
|
int &n_in) // number of points inside (returned)
|
|
{
|
|
int l = 0;
|
|
int r = n-1;
|
|
for(;;) { // partition pa[0..n-1] about box
|
|
while (l < n && box.inside(dim, PP(l))) l++;
|
|
while (r >= 0 && !box.inside(dim, PP(r))) r--;
|
|
if (l > r) break;
|
|
PASWAP(l,r);
|
|
l++; r--;
|
|
}
|
|
n_in = l; // now: pa[0..n_in-1] inside and rest outside
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// annSplitBalance - compute balance factor for a given plane split
|
|
// Balance factor is defined as the number of points lying
|
|
// below the splitting value minus n/2 (median). Thus, a
|
|
// median split has balance 0, left of this is negative and
|
|
// right of this is positive. (The points are unchanged.)
|
|
//----------------------------------------------------------------------
|
|
|
|
int annSplitBalance( // determine balance factor of a split
|
|
ANNpointArray pa, // points to split
|
|
ANNidxArray pidx, // point indices
|
|
int n, // number of points
|
|
int d, // dimension along which to split
|
|
ANNcoord cv) // cutting value
|
|
{
|
|
int n_lo = 0;
|
|
for(int i = 0; i < n; i++) { // count number less than cv
|
|
if (PA(i,d) < cv) n_lo++;
|
|
}
|
|
return n_lo - n/2;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// annBox2Bnds - convert bounding box to list of bounds
|
|
// Given two boxes, an inner box enclosed within a bounding
|
|
// box, this routine determines all the sides for which the
|
|
// inner box is strictly contained with the bounding box,
|
|
// and adds an appropriate entry to a list of bounds. Then
|
|
// we allocate storage for the final list of bounds, and return
|
|
// the resulting list and its size.
|
|
//----------------------------------------------------------------------
|
|
|
|
void annBox2Bnds( // convert inner box to bounds
|
|
const ANNorthRect &inner_box, // inner box
|
|
const ANNorthRect &bnd_box, // enclosing box
|
|
int dim, // dimension of space
|
|
int &n_bnds, // number of bounds (returned)
|
|
ANNorthHSArray &bnds) // bounds array (returned)
|
|
{
|
|
int i;
|
|
n_bnds = 0; // count number of bounds
|
|
for (i = 0; i < dim; i++) {
|
|
if (inner_box.lo[i] > bnd_box.lo[i]) // low bound is inside
|
|
n_bnds++;
|
|
if (inner_box.hi[i] < bnd_box.hi[i]) // high bound is inside
|
|
n_bnds++;
|
|
}
|
|
|
|
bnds = new ANNorthHalfSpace[n_bnds]; // allocate appropriate size
|
|
|
|
int j = 0;
|
|
for (i = 0; i < dim; i++) { // fill the array
|
|
if (inner_box.lo[i] > bnd_box.lo[i]) {
|
|
bnds[j].cd = i;
|
|
bnds[j].cv = inner_box.lo[i];
|
|
bnds[j].sd = +1;
|
|
j++;
|
|
}
|
|
if (inner_box.hi[i] < bnd_box.hi[i]) {
|
|
bnds[j].cd = i;
|
|
bnds[j].cv = inner_box.hi[i];
|
|
bnds[j].sd = -1;
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// annBnds2Box - convert list of bounds to bounding box
|
|
// Given an enclosing box and a list of bounds, this routine
|
|
// computes the corresponding inner box. It is assumed that
|
|
// the box points have been allocated already.
|
|
//----------------------------------------------------------------------
|
|
|
|
void annBnds2Box(
|
|
const ANNorthRect &bnd_box, // enclosing box
|
|
int dim, // dimension of space
|
|
int n_bnds, // number of bounds
|
|
ANNorthHSArray bnds, // bounds array
|
|
ANNorthRect &inner_box) // inner box (returned)
|
|
{
|
|
annAssignRect(dim, inner_box, bnd_box); // copy bounding box to inner
|
|
|
|
for (int i = 0; i < n_bnds; i++) {
|
|
bnds[i].project(inner_box.lo); // project each endpoint
|
|
bnds[i].project(inner_box.hi);
|
|
}
|
|
}
|