coriolis/flute/src/3.1/neighbors.cpp

531 lines
11 KiB
C++
Executable File

#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "global.h"
#include "err.h"
#include "dist.h"
namespace Flute {
long octant
(
Point from,
Point to
);
static Point* _pt;
/***************************************************************************/
/*
For efficiency purposes auxiliary arrays are allocated as globals
*/
long max_arrays_size = 0;
nn_array* nn = (nn_array*)NULL;
Point* sheared = (Point*)NULL;
long* sorted = (long*)NULL;
long* aux = (long*)NULL;
/***************************************************************************/
/*
resize the auxiliary arrays to fit the specified number of points
*/
void allocate_nn_arrays( long n )
{
if( max_arrays_size < n )
{
nn = (nn_array*)realloc( (void*)nn, (size_t)n*sizeof(nn_array) );
sheared = (Point*)realloc( (void*)sheared, (size_t)n*sizeof(Point) );
sorted = (long*)realloc( (void*)sorted, (size_t)n*sizeof(long) );
aux = (long*)realloc( (void*)aux, (size_t)n*sizeof(long) );
if( !nn || !sheared || !sorted || !aux )
{
err_exit( "Cannot allocate memory in allocate_nn_arrays!" );
}
max_arrays_size = n;
}
}
/***************************************************************************/
/*
free memory used by auxiliary arrays
*/
void deallocate_nn_arrays()
{
max_arrays_size = 0;
if( nn )
{
free( (void*)nn );
nn = (nn_array*)NULL;
}
if( sheared )
{
free( (void*)sheared );
sheared = (Point*)NULL;
}
if( sorted )
{
free( (void*)sorted );
sorted = (long*)NULL;
}
if( aux )
{
free( (void*)aux );
aux = (long*)NULL;
}
}
/***************************************************************************/
/*
comparison function for use in quicksort
*/
static int compare_x
(
const void* i,
const void* j
)
{
/*
points with the same x must appear in increasing order of y
*/
if( sheared[*((long*)i)].x == sheared[*((long*)j)].x)
{
return sheared[*((long*)i)].y - sheared[*((long*)j)].y;
}
else
{
return sheared[*((long*)i)].x - sheared[*((long*)j)].x;
}
}
/***************************************************************************/
/*
Combine step of the Guibas-Stolfi divide-and-conquer NE nearest neighbor
algorithm. For efficiency purposes SW nearest neighbors are computed
at the same time.
*/
void ne_sw_combine
(
long left,
long mid,
long right,
Point* pt,
long* sorted,
long* aux,
long oct,
nn_array* nn
)
{
long i, j, k, y2;
long i1;
long i2;
long best_i2; /* index of current best nearest-neighbor */
long best_dist; /* distance to best nearest-neighbor */
long d;
#ifdef DEBUG
assert( right > mid );
assert( mid > left );
#endif
/*
update north-east nearest neighbors accross the mid-line
*/
i1 = left;
i2 = mid; y2 = pt[ sorted[i2] ].y;
while( (i1 < mid) && (pt[ sorted[i1] ].y >= y2) )
{
i1++;
}
if( i1 < mid )
{
best_i2 = i2;
best_dist = dist2( pt + sorted[i1], pt + sorted[best_i2] );
i2++;
while( (i1 < mid) && (i2 < right) )
{
if( pt[ sorted[i1] ].y < pt[ sorted[i2] ].y )
{
d = dist2( pt + sorted[i1], pt + sorted[i2] );
if( d < best_dist )
{
best_i2 = i2;
best_dist = d;
}
i2++;
}
else
{
if( (nn[ sorted[i1] ][oct] == -1) ||
( best_dist < dist2( pt + sorted[i1], pt + nn[ sorted[i1] ][oct]) )
)
{
nn[ sorted[i1] ][oct] = sorted[best_i2];
}
i1++;
if( i1 < mid )
{
best_dist = dist2( pt + sorted[i1], pt + sorted[best_i2] );
}
}
}
while( i1 < mid )
{
if( (nn[ sorted[i1] ][oct] == -1) ||
( dist2( pt + sorted[i1], pt + sorted[best_i2] ) <
dist2( pt + sorted[i1], pt + nn[ sorted[i1] ][oct]) )
)
{
nn[ sorted[i1] ][oct] = sorted[best_i2];
}
i1++;
}
}
/*
repeat for south-west nearest neighbors
*/
oct = (oct + 4) % 8;
i1 = right - 1;
i2 = mid - 1; y2 = pt[ sorted[i2] ].y;
while( (i1 >= mid) && (pt[ sorted[i1] ].y <= y2) )
{
i1--;
}
if( i1 >= mid )
{
best_i2 = i2;
best_dist = dist2( pt + sorted[i1], pt + sorted[best_i2] );
i2--;
while( (i1 >= mid) && (i2 >= left) )
{
if( pt[ sorted[i1] ].y > pt[ sorted[i2] ].y )
{
d = dist2( pt + sorted[i1], pt + sorted[i2] );
if( d < best_dist )
{
best_i2 = i2;
best_dist = d;
}
i2--;
}
else
{
if( (nn[ sorted[i1] ][oct] == -1) ||
( best_dist < dist2( pt + sorted[i1], pt + nn[ sorted[i1] ][oct]) )
)
{
nn[ sorted[i1] ][oct] = sorted[best_i2];
}
i1--;
if( i1 >= mid )
{
best_dist = dist2( pt + sorted[i1], pt + sorted[best_i2] );
}
}
}
while( i1 >= mid )
{
if( (nn[ sorted[i1] ][oct] == -1) ||
( dist2( pt + sorted[i1], pt + sorted[best_i2] ) <
dist2( pt + sorted[i1], pt + nn[ sorted[i1] ][oct]) )
)
{
nn[ sorted[i1] ][oct] = sorted[best_i2];
}
i1--;
}
}
/*
merge sorted[left..mid-1] with sorted[mid..right-1] by y-coordinate
*/
i = left; /* first unprocessed element in left list */
j = mid; /* first unprocessed element in right list */
k = left; /* first free available slot in output list */
while( (i < mid) && (j < right) )
{
if( pt[ sorted[i] ].y >= pt[ sorted[j] ].y )
{
aux[k++] = sorted[i++];
}
else
{
aux[k++] = sorted[j++];
}
}
/*
copy leftovers
*/
while( i < mid ) { aux[k++] = sorted[i++]; }
while( j < right ) { aux[k++] = sorted[j++]; }
/*
now copy sorted points from 'aux' to 'sorted'
*/
for( i = left; i < right; i++ ) { sorted[i] = aux[i]; }
#if 0
memcpy( (void*)(sorted+left), /* destination */
(void*)(aux+left), /* source */
(size_t)(right-left)*sizeof(long) /* number of bytes */
);
#endif
}
/***************************************************************************/
/*
compute north-east and south-west nearest neighbors for points indexed
by {sorted[left],...,sorted[right-1]}
*/
void ne_sw_nearest_neighbors
(
long left,
long right,
Point* pt,
long* sorted,
long* aux,
long oct,
nn_array* nn
)
{
long mid;
#ifdef DEBUG
assert( right > left );
#endif
if( right == left + 1 )
{
nn[ sorted[left] ][oct] = nn[ sorted[left]][(oct+4) % 8] = -1;
}
else
{
mid = (left + right) / 2;
ne_sw_nearest_neighbors( left, mid, pt, sorted, aux, oct, nn );
ne_sw_nearest_neighbors( mid, right, pt, sorted, aux, oct, nn );
ne_sw_combine( left, mid, right, pt, sorted, aux, oct, nn );
}
}
/***************************************************************************/
/*
Guibas-Stolfi algorithm for computing nearest NE neighbors
*/
void dq_nearest_neighbors
(
long n,
Point* pt,
nn_array* nn
)
{
long i, oct;
void check_nn( long, Point*, nn_array* );
long shear[4][4] = {
{1, -1, 0, 2},
{2, 0, -1, 1},
{1, 1, -2, 0},
{0, 2, -1, -1}
};
_pt = pt;
for( oct = 0; oct < 4; oct++ )
{
for( i = 0; i < n; i++ )
{
sheared[i].x = shear[oct][0]*pt[i].x + shear[oct][1]*pt[i].y;
sheared[i].y = shear[oct][2]*pt[i].x + shear[oct][3]*pt[i].y;
sorted[i] = i;
}
qsort( sorted, n, sizeof(long), compare_x );
ne_sw_nearest_neighbors( 0, n, sheared, sorted, aux, oct, nn );
}
#ifdef DEBUG
check_nn( n, pt, nn );
#endif
}
/***************************************************************************/
/***************************************************************************/
/*
Brute-force nearest-neighbor computation for debugging purposes
*/
/***************************************************************************/
/*
Half-open octants are numbered from 0 to 7 in anti-clockwise order
starting from ( dx >= dy > 0 ).
*/
#define sgn(x) ( x>0 ? 1 : (x < 0 ? -1 : 0) )
long octant
(
Point from,
Point to
)
{
long dx = to.x - from.x;
long dy = to.y - from.y;
long sgn1 = sgn(dx)*sgn(dy);
long sgn2 = sgn(dx+dy)*sgn(dx-dy);
long oct = 0x0;
if( (dy < 0) || ((dy==0) && (dx>0)) ) oct += 4;
if( (sgn1 < 0) || (dy==0) ) oct += 2;
if( (sgn1*sgn2 < 0) || (dy==0) || (dx==0) ) oct += 1;
return oct;
}
/***************************************************************************/
/*
O(n^2) algorithm for computing all nearest neighbors
*/
void brute_force_nearest_neighbors
(
long n,
Point* pt,
nn_array* nn
)
{
long i, j, oct;
long d;
/*
compute nearest neighbors by inspecting all pairs of points
*/
for( i = 0; i < n; i++ )
{
for( oct = 0; oct < 8; oct++ )
{
nn[i][oct] = -1;
}
}
for( i = 0; i < n; i++ )
{
for( j = i+1; j < n; j++ )
{
d = dist(pt[i], pt[j]);
oct = octant( pt[i], pt[j] );
if( ( nn[i][oct] == -1 ) ||
( d < dist(pt[i], pt[ nn[i][oct] ]) )
)
{
nn[i][oct] = j;
}
oct = (oct + 4) % 8;
if( ( nn[j][oct] == -1 ) ||
( d < dist(pt[j], pt[ nn[j][oct] ]) )
)
{
nn[j][oct] = i;
}
}
}
}
/***************************************************************************/
/*
compare nearest neighbors against those computed by brute force
*/
void check_nn
(
long n,
Point* pt,
nn_array* nn
)
{
long i, j, oct;
nn_array* nn1;
nn1 = (nn_array*)calloc( (size_t)n, (size_t)sizeof(nn_array) );
brute_force_nearest_neighbors( n, pt, nn1 );
for( i = 0; i < n; i++ )
{
for( oct = 0; oct < 8; oct++ )
{
if( nn[i][oct] == -1 )
{
assert( nn1[i][oct] == -1 );
}
else
{
assert( nn1[i][oct] != -1 );
if( octant(pt[i], pt[ nn[i][oct] ]) != oct )
{
printf( "WRONG OCTANT!\noct=%ld\n", oct );
printf( "i=%ld, x=%ld, y=%ld\n", i, pt[i].x, pt[i].y );
j = nn[i][oct];
printf( "nn=%ld, x=%ld, y=%ld, dist = %ld\n", j, pt[j].x, pt[j].y,
dist(pt[i], pt[j ]) );
}
// assert( octant(pt[i], pt[ nn[i][oct] ]) == oct );
assert( octant(pt[i], pt[ nn1[i][oct] ]) == oct );
if( dist(pt[i], pt[ nn[i][oct] ]) !=
dist(pt[i], pt[ nn1[i][oct] ]) )
{
printf( "NNs DON'T MATCH!\noct=%ld\n", oct );
printf( "i=%ld, x=%ld, y=%ld\n", i, pt[i].x, pt[i].y );
j = nn[i][oct];
printf( "nn=%ld, x=%ld, y=%ld, dist = %ld\n", j, pt[j].x, pt[j].y,
dist(pt[i], pt[j ]) );
j = nn1[i][oct];
printf( "nn1=%ld, x=%ld, y=%ld, dist = %ld\n", j, pt[j].x, pt[j].y,
dist(pt[i], pt[ j ]) );
}
// assert( dist(pt[i], pt[ nn[i][oct] ]) ==
// dist(pt[i], pt[ nn1[i][oct] ]) );
}
}
}
free( nn1 );
}
/***************************************************************************/
/***************************************************************************/
} // Flute namespace.