//-------------------------------------------------------------------------- // // File: nncommon.c // // Created: 04/08/2000 // // Author: Pavel Sakov // CSIRO Marine Research // // Purpose: Common stuff for NN interpolation library // // Description: None // // Revisions: 15/11/2002 PS: Changed name from "utils.c" // 28/02/2003 PS: Modified points_read() to do the job without // rewinding the file. This allows to read from stdin when // necessary. // 09/04/2003 PS: Modified points_read() to read from a // file specified by name, not by handle. // Modified: Andrew Ross 20/10/2008 // Change <= comparison in circle_contains() to use EPSILON // to catch case where the point lies on the circle and there // is floating point rounding error in the radii. // //-------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include "nan.h" #include "delaunay.h" #define BUFSIZE 1024 #define EPSILON 1.0e-8 int nn_verbose = 0; int nn_test_vertice = -1; NN_RULE nn_rule = SIBSON; #include "version.h" void nn_quit( const char* format, ... ); int circle_build( circle* c, point* p1, point* p2, point* p3 ); int circle_contains( circle* c, point* p ); void nn_quit( const char* format, ... ) { va_list args; fflush( stdout ); // just in case, to have the exit message // last fprintf( stderr, "error: nn: " ); va_start( args, format ); vfprintf( stderr, format, args ); va_end( args ); exit( 1 ); } int circle_build( circle* c, point* p1, point* p2, point* p3 ) { double x1sq = p1->x * p1->x; double x2sq = p2->x * p2->x; double x3sq = p3->x * p3->x; double y1sq = p1->y * p1->y; double y2sq = p2->y * p2->y; double y3sq = p3->y * p3->y; double t1 = x3sq - x2sq + y3sq - y2sq; double t2 = x1sq - x3sq + y1sq - y3sq; double t3 = x2sq - x1sq + y2sq - y1sq; double D = ( p1->x * ( p2->y - p3->y ) + p2->x * ( p3->y - p1->y ) + p3->x * ( p1->y - p2->y ) ) * 2.0; if ( D == 0.0 ) return 0; c->x = ( p1->y * t1 + p2->y * t2 + p3->y * t3 ) / D; c->y = -( p1->x * t1 + p2->x * t2 + p3->x * t3 ) / D; c->r = hypot( c->x - p1->x, c->y - p1->y ); return 1; } // This procedure has taken it final shape after a number of tries. The problem // was to have the calculated and stored radii being the same if (x,y) is // exactly on the circle border (i.e. not to use FCPU extended precision in // the radius calculation). This may have little effect in practice but was // important in some tests when both input and output data were placed // in rectangular grid nodes. // int circle_contains( circle* c, point* p ) { return hypot( c->x - p->x, c->y - p->y ) <= c->r * ( 1.0 + EPSILON ); } // Smoothes the input point array by averaging the input x,y and z values // for each cell within virtual rectangular nx by ny grid. The corners of the // grid are created from min and max values of the input array. It also frees // the original array and returns results and new dimension via original // data and size pointers. // // @param pn Pointer to number of points (input/output) // @param ppoints Pointer to array of points (input/output) [*pn] // @param nx Number of x nodes in decimation // @param ny Number of y nodes in decimation // void points_thin( int* pn, point** ppoints, int nx, int ny ) { int n = *pn; point * points = *ppoints; double xmin = DBL_MAX; double xmax = -DBL_MAX; double ymin = DBL_MAX; double ymax = -DBL_MAX; int nxy = nx * ny; double * sumx = calloc( (size_t) nxy, sizeof ( double ) ); double * sumy = calloc( (size_t) nxy, sizeof ( double ) ); double * sumz = calloc( (size_t) nxy, sizeof ( double ) ); int * count = calloc( (size_t) nxy, sizeof ( int ) ); double stepx = 0.0; double stepy = 0.0; int nnew = 0; point * pointsnew = NULL; int i, j, ii; if ( nn_verbose ) fprintf( stderr, "thinned: %d points -> ", *pn ); if ( nx < 1 || ny < 1 ) { free( points ); *ppoints = NULL; *pn = 0; if ( nn_verbose ) fprintf( stderr, "0 points" ); free( sumx ); free( sumy ); free( sumz ); free( count ); return; } for ( ii = 0; ii < n; ++ii ) { point* p = &points[ii]; if ( p->x < xmin ) xmin = p->x; if ( p->x > xmax ) xmax = p->x; if ( p->y < ymin ) ymin = p->y; if ( p->y > ymax ) ymax = p->y; } stepx = ( nx > 1 ) ? ( xmax - xmin ) / nx : 0.0; stepy = ( ny > 1 ) ? ( ymax - ymin ) / ny : 0.0; for ( ii = 0; ii < n; ++ii ) { point* p = &points[ii]; int index; // // Following is the portion of the code which really depends on the // floating point particulars. Do not be surprised if different // compilers/options give different results here. // i = ( nx == 1 ) ? 0 : (int) ( ( p->x - xmin ) / stepx ); j = ( ny == 1 ) ? 0 : (int) ( ( p->y - ymin ) / stepy ); if ( i == nx ) i--; if ( j == ny ) j--; index = i + j * nx; sumx[index] += p->x; sumy[index] += p->y; sumz[index] += p->z; count[index]++; } for ( j = 0; j < ny; ++j ) { for ( i = 0; i < nx; ++i ) { int index = i + j * nx; if ( count[index] > 0 ) nnew++; } } pointsnew = malloc( (size_t) nnew * sizeof ( point ) ); ii = 0; for ( j = 0; j < ny; ++j ) { for ( i = 0; i < nx; ++i ) { int index = i + j * nx; int nn = count[index]; if ( nn > 0 ) { point* p = &pointsnew[ii]; p->x = sumx[index] / nn; p->y = sumy[index] / nn; p->z = sumz[index] / nn; ii++; } } } if ( nn_verbose ) fprintf( stderr, "%d points\n", nnew ); free( sumx ); free( sumy ); free( sumz ); free( count ); free( points ); *ppoints = pointsnew; *pn = nnew; } // Generates rectangular grid nx by ny using min and max x and y values from // the input point array. Allocates space for the output point array, be sure // to free it when necessary! // // @param n Number of points // @param points Array of points [n] // @param nx Number of x nodes // @param ny Number of y nodes // @param zoom Zoom coefficient // @param nout Pointer to number of output points // @param pout Pointer to array of output points [*nout] // void points_generate1( int nin, point pin[], int nx, int ny, double zoom, int* nout, point** pout ) { double xmin = DBL_MAX; double xmax = -DBL_MAX; double ymin = DBL_MAX; double ymax = -DBL_MAX; double stepx, stepy; double x0, xx, yy; int i, j, ii; if ( nx < 1 || ny < 1 ) { *pout = NULL; *nout = 0; return; } for ( ii = 0; ii < nin; ++ii ) { point* p = &pin[ii]; if ( p->x < xmin ) xmin = p->x; if ( p->x > xmax ) xmax = p->x; if ( p->y < ymin ) ymin = p->y; if ( p->y > ymax ) ymax = p->y; } if ( isnan( zoom ) || zoom <= 0.0 ) zoom = 1.0; if ( zoom != 1.0 ) { double xdiff2 = ( xmax - xmin ) / 2.0; double ydiff2 = ( ymax - ymin ) / 2.0; double xav = ( xmax + xmin ) / 2.0; double yav = ( ymax + ymin ) / 2.0; xmin = xav - xdiff2 * zoom; xmax = xav + xdiff2 * zoom; ymin = yav - ydiff2 * zoom; ymax = yav + ydiff2 * zoom; } *nout = nx * ny; *pout = malloc( (size_t) ( *nout ) * sizeof ( point ) ); stepx = ( nx > 1 ) ? ( xmax - xmin ) / ( nx - 1 ) : 0.0; stepy = ( ny > 1 ) ? ( ymax - ymin ) / ( ny - 1 ) : 0.0; x0 = ( nx > 1 ) ? xmin : ( xmin + xmax ) / 2.0; yy = ( ny > 1 ) ? ymin : ( ymin + ymax ) / 2.0; ii = 0; for ( j = 0; j < ny; ++j ) { xx = x0; for ( i = 0; i < nx; ++i ) { point* p = &( *pout )[ii]; p->x = xx; p->y = yy; xx += stepx; ii++; } yy += stepy; } } // Generates rectangular grid nx by ny using specified min and max x and y // values. Allocates space for the output point array, be sure to free it // when necessary! // // @param xmin Min x value // @param xmax Max x value // @param ymin Min y value // @param ymax Max y value // @param nx Number of x nodes // @param ny Number of y nodes // @param nout Pointer to number of output points // @param pout Pointer to array of output points [*nout] // void points_generate2( double xmin, double xmax, double ymin, double ymax, int nx, int ny, int* nout, point** pout ) { double stepx, stepy; double x0, xx, yy; int i, j, ii; if ( nx < 1 || ny < 1 ) { *pout = NULL; *nout = 0; return; } *nout = nx * ny; *pout = malloc( (size_t) ( *nout ) * sizeof ( point ) ); stepx = ( nx > 1 ) ? ( xmax - xmin ) / ( nx - 1 ) : 0.0; stepy = ( ny > 1 ) ? ( ymax - ymin ) / ( ny - 1 ) : 0.0; x0 = ( nx > 1 ) ? xmin : ( xmin + xmax ) / 2.0; yy = ( ny > 1 ) ? ymin : ( ymin + ymax ) / 2.0; ii = 0; for ( j = 0; j < ny; ++j ) { xx = x0; for ( i = 0; i < nx; ++i ) { point* p = &( *pout )[ii]; p->x = xx; p->y = yy; xx += stepx; ii++; } yy += stepy; } } static int str2double( char* token, double* value ) { char* end = NULL; if ( token == NULL ) { *value = NaN; return 0; } *value = strtod( token, &end ); if ( end == token ) { *value = NaN; return 0; } return 1; } #define NALLOCATED_START 1024 // Reads array of points from a columnar file. // // @param fname File name (can be "stdin" for standard input) // @param dim Number of dimensions (must be 2 or 3) // @param n Pointer to number of points (output) // @param points Pointer to array of points [*n] (output) (to be freed) // void points_read( char* fname, int dim, int* n, point** points ) { FILE * f = NULL; int nallocated = NALLOCATED_START; char buf[BUFSIZE]; char seps[] = " ,;\t"; char * token; if ( dim < 2 || dim > 3 ) { *n = 0; *points = NULL; return; } if ( fname == NULL ) f = stdin; else { if ( strcmp( fname, "stdin" ) == 0 || strcmp( fname, "-" ) == 0 ) f = stdin; else { f = fopen( fname, "r" ); if ( f == NULL ) nn_quit( "%s: %s\n", fname, strerror( errno ) ); } } *points = malloc( (size_t) nallocated * sizeof ( point ) ); *n = 0; while ( fgets( buf, BUFSIZE, f ) != NULL ) { point* p; if ( *n == nallocated ) { nallocated *= 2; *points = realloc( *points, (size_t) nallocated * sizeof ( point ) ); } p = &( *points )[*n]; if ( buf[0] == '#' ) continue; if ( ( token = strtok( buf, seps ) ) == NULL ) continue; if ( !str2double( token, &p->x ) ) continue; if ( ( token = strtok( NULL, seps ) ) == NULL ) continue; if ( !str2double( token, &p->y ) ) continue; if ( dim == 2 ) p->z = NaN; else { if ( ( token = strtok( NULL, seps ) ) == NULL ) continue; if ( !str2double( token, &p->z ) ) continue; } ( *n )++; } if ( *n == 0 ) { free( *points ); *points = NULL; } else *points = realloc( *points, (size_t) ( *n ) * sizeof ( point ) ); if ( f != stdin ) if ( fclose( f ) != 0 ) nn_quit( "%s: %s\n", fname, strerror( errno ) ); } //* Scales Y coordinate so that the resulting set fits into square: //** xmax - xmin = ymax - ymin //* //* @param n Number of points //* @param points The points to scale //* @return Y axis compression coefficient // double points_scaletosquare( int n, point* points ) { double xmin, ymin, xmax, ymax; double k; int i; if ( n <= 0 ) return NaN; xmin = xmax = points[0].x; ymin = ymax = points[0].y; for ( i = 1; i < n; ++i ) { point* p = &points[i]; if ( p->x < xmin ) xmin = p->x; else if ( p->x > xmax ) xmax = p->x; if ( p->y < ymin ) ymin = p->y; else if ( p->y > ymax ) ymax = p->y; } if ( xmin == xmax || ymin == ymax ) return NaN; else k = ( ymax - ymin ) / ( xmax - xmin ); for ( i = 0; i < n; ++i ) points[i].y /= k; return k; } //* Compresses Y domain by a given multiple. // // @param n Number of points // @param points The points to scale // @param Y axis compression coefficient as returned by points_scaletosquare() // void points_scale( int n, point* points, double k ) { int i; for ( i = 0; i < n; ++i ) points[i].y /= k; }